Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lab: SSRF to Lambda Runtime

Objective

Use SSRF to reach internal services inside the Lambda execution environment and leak sensitive data.

Why This Matters

Cloud services run internal APIs that are never exposed to the internet. AWS Lambda has a Runtime API on 169.254.100.1:9001 that manages invocations. If an attacker can make the Lambda fetch an arbitrary URL, they can reach these internal services and extract data that should never leave the function.

Real-world parallel: SSRF to the EC2 metadata service at 169.254.169.254 has been exploited in major breaches including Capital One (2019), where attackers used SSRF to steal IAM credentials and access S3 buckets containing 100+ million customer records.

The Vulnerable Code

The /redirect endpoint fetches whatever URL the caller passes:

@app.route('/redirect')
def web():
    site = request.args.get('url')
    response = urllib.request.urlopen(site)    # no allowlist
    output = json.dumps(response.read().decode('utf-8'))
    return jsonify({"output": output}), 200

There is no validation on the url parameter. The server will fetch any address — external or internal.

Steps

Before you start: make sure you are in the project directory.

cd /workspaces/very-vulnerable-serverless
source ~/.lab-env

Step 1 — Confirm SSRF with an external URL

Fetch a public service to prove the Lambda is making requests on your behalf:

curl "$ENDPOINT/redirect?url=https://httpbin.org/ip"

You will see the Lambda function’s public IP address in the response — not yours. The server made the request, not your browser.

Step 2 — Reach the Lambda Runtime API

The Lambda Runtime API runs on 169.254.100.1:9001 inside every Lambda function. It is meant for the Lambda runtime only. Hit it:

curl "$ENDPOINT/redirect?url=http://169.254.100.1:9001/2018-06-01/runtime/invocation/next"

This returns invocation context that should never leave the function — request IDs, deadlines, and internal routing information.

Step 3 — Understand the impact

From here, an attacker could:

  • Read internal configuration and environment variables
  • Chain with command injection to steal AWS credentials (we did this in the previous lab)
  • Reach other internal services running on localhost or within the VPC

What Happened

The application trusts user input as a URL and fetches it without any validation. There is no allowlist of permitted domains or protocols. The server becomes a proxy that reaches internal services the attacker cannot access directly.

Fix: Validate URLs against an allowlist of permitted domains. Block private IP ranges (127.0.0.0/8, 10.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16). Use a URL parser to check the resolved IP, not just the hostname (to prevent DNS rebinding).