SSRF labs

ssrf.vulnlab.dev

A dozen labs covering the SSRF detection surface most tools care about: unfiltered fetch, broken validators, parser-disagreement bypasses, redirect chases, scheme abuse, gopher-to-TCP pivots, fully and semi-blind variants, and cloud metadata in three flavors (AWS, GCP, Azure).

Internal targets that exist on this host (and would normally be unreachable from the public internet):

A self-hosted open redirector lives at /r/?to=<url> on this domain — convenient for chaining through host-allowlist labs.

Labs

Basic SSRF requests.get
Unfiltered server-side URL fetch.
SSRF behind a substring blocklist urllib.request.urlopen
Rejects URLs containing 'localhost' or '127.0.0.1', then fetches.
SSRF behind a substring allowlist requests.get
Only fetches if the URL 'contains' vulnlab.dev — which the validator checks the wrong way.
SSRF via URL-parser disagreement (userinfo) requests.get
Validator and HTTP client disagree on which part of the URL is the host.
SSRF via follow-redirect on a trusted host requests.get
Validator only checks the submitted URL's hostname. The fetcher chases 302s.
SSRF with unrestricted URL scheme urllib.request.urlopen
Any scheme urllib supports works — including file:// for local file read.
Blind SSRF httpx.get
Server fetches the URL but tells you nothing about the response.
Semi-blind SSRF via a webhook poster requests.post
POSTs your URL with a JSON body and echoes status, length, content-type, X-* headers.
SSRF in cloud-hosted (mock AWS) app requests.get
A profile-image fetcher running on a fake EC2. Reach the metadata service.
SSRF in cloud-hosted (mock GCP) app requests.get
GCP-style metadata service. The app injects Metadata-Flavor: Google.
SSRF in cloud-hosted (mock Azure) app requests.get
Azure-style metadata + managed-identity. The app injects Metadata: true.
SSRF via gopher:// to a non-HTTP service pycurl
Fetcher uses libcurl, which speaks gopher://. A TCP service is listening on 127.0.0.1:6479.

For tool builders

Source for every lab is published. Each lab page links to its own source via /source/<slug>. Run your SAST/DAST/LLM-based reviewer against the deployed app or the GitHub repo and see what each catches.