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:

  1. Use the public victim server as a proxy to reach a private internal admin API that is completely inaccessible from the network

  2. Exfiltrate AWS EC2 instance metadata (IAM credentials, access keys) via the simulated 169.254.169.254 endpoint

  3. Map hidden internal services with a port scanner built entirely on SSRF responses

  4. Read arbitrary local files using the file:// scheme — including /etc/passwd and the app's own source code

  5. Dump internal secrets (DB passwords, JWT keys, AWS keys) from a /secrets endpoint 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 network available

  • Basic familiarity with curl and HTTP

Tools

Tool
Container
Purpose

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 network

  • Internal admin API on 127.0.0.1:8080 — only reachable from within the container

📸 Verified Output:

💡 The victim has two services running simultaneously. Port 5000 is exposed to the Docker network (Kali can reach it). Port 8080 is bound to 127.0.0.1 inside the container — only the victim's own process can reach it. Our SSRF attack will use the public :5000 API to reach the private :8080 service.


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/ping are 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.1 from its own perspective. From inside the container, 127.0.0.1:8080 is 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 called aws s3 sync and 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

Many 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/*.py or /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.


📸 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

Phase
Endpoint Used
Target
Impact

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

Vulnerability
Root Cause
Fix

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