criticalCWE-918OWASP A10:2021

Cloud Metadata SSRF

Your app fetches user-supplied URLs without blocking cloud metadata endpoints like 169.254.169.254, letting attackers steal your cloud credentials via SSRF.

How It Works

Every cloud VM (AWS EC2, GCP, Azure) has an internal metadata service at a link-local address (169.254.169.254). This service returns the instance's IAM credentials, including temporary AWS access keys, with a simple HTTP GET — no authentication required from within the instance. If your app fetches any URL a user provides without validating it, an attacker supplies http://169.254.169.254/latest/meta-data/iam/security-credentials/ and steals your cloud credentials.

Vulnerable Code
// BAD: fetching user-supplied URL with no validation
export async function POST(req: Request) {
  const { url } = await req.json();
  // Attacker sends: http://169.254.169.254/latest/meta-data/iam/security-credentials/
  const response = await fetch(url);
  return Response.json({ content: await response.text() });
}
Secure Code
// GOOD: validate URL is external and not a private/metadata address
function isSafeUrl(url: string): boolean {
  const parsed = new URL(url);
  const blocklist = ['169.254.169.254', 'metadata.google.internal', '169.254.170.2'];
  return parsed.protocol === 'https:' && !blocklist.includes(parsed.hostname);
}
export async function POST(req: Request) {
  const { url } = await req.json();
  if (!isSafeUrl(url)) return Response.json({ error: 'Invalid URL' }, { status: 400 });
  const response = await fetch(url);
  return Response.json({ content: await response.text() });
}

Real-World Example

The 2019 Capital One breach was caused exactly by this: an SSRF vulnerability allowed the attacker to hit the EC2 metadata endpoint and retrieve IAM credentials, which were then used to download 100 million customer records from S3. It remains one of the most well-documented SSRF-to-cloud-takeover attacks.

How to Prevent It

  • Block requests to 169.254.169.254, metadata.google.internal, 169.254.170.2, and fd00:ec2::254 (IPv6 equivalent)
  • Also block private IP ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and localhost
  • Use IMDSv2 on AWS EC2 which requires a PUT request with a session token — simple GET requests to the metadata endpoint fail
  • Apply least-privilege IAM roles to your instances — even if credentials are stolen, their blast radius is limited
  • Validate that user-supplied URLs use HTTPS and resolve to public IP addresses before fetching

Affected Technologies

Node.jsPython

Data Hogo detects this vulnerability automatically.

Scan Your Repo Free

Related Vulnerabilities