Lab 10: OWASP A10 — Server-Side Request Forgery (SSRF)
Objective
Exploit Server-Side Request Forgery (SSRF) vulnerabilities on a live vulnerable server using Kali Linux as the attacker. You will:
Use the public victim server as a proxy to reach a private internal admin API that is completely inaccessible from the network
Exfiltrate AWS EC2 instance metadata (IAM credentials, access keys) via the simulated
169.254.169.254endpointMap hidden internal services with a port scanner built entirely on SSRF responses
Read arbitrary local files using the
file://scheme — including/etc/passwdand the app's own source codeDump internal secrets (DB passwords, JWT keys, AWS keys) from a
/secretsendpoint only reachable from localhost
All attacks run from the Kali attacker container — no direct access to the victim's internal network ever needed.
Background
SSRF is OWASP #10 (2021 — new to the list, reflecting explosive growth in cloud environments). It occurs when a server fetches a remote resource based on a URL supplied by the client, without validating where that URL points.
Why it's critical in cloud: Every major cloud provider exposes an instance metadata service on a non-routable IP (169.254.169.254 on AWS/Azure/GCP). This service requires no authentication and returns temporary IAM credentials with full API access. Any EC2 instance can call it — and via SSRF, so can any attacker who can make the server issue an HTTP request.
Real-world examples:
2019 Capital One breach — 100 million records stolen. Attacker hit
http://169.254.169.254/latest/meta-data/iam/security-credentials/via a misconfigured WAF acting as a proxy, obtained temporary AWS credentials, listed and downloaded 700+ S3 buckets.2022 Confluence SSRF (CVE-2022-26134) — Used SSRF to reach internal Kubernetes API servers at Fortune 500 companies, extracting service account tokens for full cluster takeover.
GitHub Enterprise SSRF (2020) — Internal Redis and Memcached reachable via SSRF; led to RCE.
Architecture
Time
50 minutes
Prerequisites
Docker installed,
docker networkavailableBasic familiarity with
curland HTTP
Tools
curl
Kali
Send HTTP requests, exploit SSRF endpoints
python3
Kali
Automate port scanning, parse JSON responses
nmap
Kali
Fingerprint the victim service
gobuster
Kali
Enumerate public endpoints
Flask app
Victim
Vulnerable server with SSRF endpoints
Internal service
Victim (localhost only)
Simulated internal admin API + AWS metadata
Lab Instructions
Step 1: Environment Setup — Write the Victim Application
The victim runs two Flask apps inside one container:
Public app on
0.0.0.0:5000— accessible from the networkInternal admin API on
127.0.0.1:8080— only reachable from within the container
📸 Verified Output:
💡 The victim has two services running simultaneously. Port
5000is exposed to the Docker network (Kali can reach it). Port8080is bound to127.0.0.1inside the container — only the victim's own process can reach it. Our SSRF attack will use the public:5000API to reach the private:8080service.
Step 2: Launch the Kali Attacker Container
Inside the Kali shell — set your target and confirm connectivity:
📸 Verified Output:
Step 3: Reconnaissance — nmap + gobuster
📸 Verified Output:
💡
/api/fetch,/api/preview,/api/pingare all SSRF vectors — any endpoint that accepts a URL or hostname from the client and makes an outbound request is potentially vulnerable. The names sound innocent ("link preview", "connectivity check") but they are proxies the attacker can aim at internal services.
Step 4: SSRF Phase 1 — Reach the Internal Admin API
This is the core SSRF attack: we tell the victim server to fetch its own localhost:8080/admin, an endpoint completely unreachable from outside.
📸 Verified Output:
📸 Verified Output:
💡 The victim server fetches
127.0.0.1from its own perspective. From inside the container,127.0.0.1:8080is the internal Flask app. The victim makes the request and returns the response to us — we never needed direct network access. This is why SSRF is so devastating in microservice architectures: the "trusted internal network" is only one SSRF away from any attacker who can reach any public endpoint.
Step 5: SSRF Phase 2 — AWS EC2 Instance Metadata Exfiltration
In a real AWS deployment, every EC2 instance can reach http://169.254.169.254/latest/meta-data/ — the Instance Metadata Service (IMDS). It returns temporary IAM credentials with no authentication required. Via SSRF, the attacker reaches it through the victim.
📸 Verified Output:
💡 This exact attack stole 100 million Capital One records in 2019. The 4-step chain is: (1) find an SSRF endpoint, (2) hit
169.254.169.254, (3) get the IAM role name, (4) dump credentials. With those credentials, the attacker calledaws s3 syncand pulled 700+ S3 buckets. The defence: enable IMDSv2 (requires a session token header — SSRF requests can't get the token first), and follow least-privilege IAM (the role should only have the permissions the app actually needs).
Step 6: SSRF Phase 3 — Internal Port Scanning
The /api/check endpoint accepts host and port parameters, connecting via raw socket. By iterating over ports, an attacker can map all open services on the internal network without any direct access.
📸 Verified Output:
Step 7: SSRF Phase 4 — file:// Scheme: Read Local Files
file:// Scheme: Read Local FilesMany HTTP libraries support file:// URIs. If the server doesn't restrict URL schemes, an attacker can read any file accessible by the running process.
📸 Verified Output:
💡
file://SSRF gives the attacker a full local file read. Sensitive files to target:/etc/passwd(user enumeration),/proc/self/environ(environment variables including secrets),/app/*.pyor/app/config.py(source code with hardcoded credentials),/root/.ssh/id_rsa(SSH private keys). Fix: block all non-HTTPS schemes at the URL-parsing level before making any request.
Step 8: SSRF Phase 5 — Internal Secrets Exfiltration + Link Preview Bypass
📸 Verified Output:
💡 Every "outbound HTTP" feature is a potential SSRF vector — link preview generators, webhook senders, PDF generators (html2pdf fetches URLs), image import from URL, health check endpoints, OAuth redirect validators. Security review any feature that makes server-side HTTP requests.
Step 9: Cleanup
On your host machine:
Attack Summary
1
GET /api/fetch?url=
127.0.0.1:8080/admin
Internal admin panel exposed — DB connection string leaked
2
GET /api/fetch?url=
127.0.0.1:8080/latest/meta-data/iam/...
AWS IAM credentials extracted — full API access
3
GET /api/check?host=&port=
127.0.0.1:PORT
Internal port scan — full service topology mapped
4
GET /api/fetch?url=file:///
/etc/passwd, /app/victim.py
Local file read — source code, user list, credentials
5
GET /api/preview?url=
127.0.0.1:8080/secrets
All internal secrets dumped via different SSRF vector
6
GET /api/ping?host=
127.0.0.1:8080/admin
Blind SSRF — confirms internal service reachable
Remediation
1. URL Allowlisting (Most Important)
2. AWS IMDSv2 (Stops Metadata SSRF)
IMDSv2 requires a PUT request with a TTL header to get a session token first — a one-step SSRF cannot do this:
3. Network Segmentation
4. Remove Unnecessary Fetch Features
If the feature isn't required, remove it. Link previews, URL import, and webhook testing are high-risk features — evaluate whether they're worth the SSRF surface they create.
Remediation Summary
SSRF via /api/fetch
No URL validation
Allowlist: only https:// to pre-approved external domains
file:// scheme
No scheme restriction
Reject any URL whose scheme is not https
AWS metadata access
IMDSv1 requires no auth
Enable IMDSv2; apply least-privilege IAM role
Internal port scan
No host/port restriction on /api/check
Remove entirely; if needed, restrict to specific allowed targets
Multiple SSRF vectors
Several endpoints make outbound calls
Centralise all outbound requests through one validated safe_fetch() function
Further Reading
Last updated
