Command Palette

Search for a command to run...

Hectal
Lab 3.3·Pipeline & RuntimeCRITICAL

A URL field that can reach cloud credentials

A new 'import product image from URL' feature fetches whatever URL it's given — from inside the VPC, next to the instance metadata service.

Weakness
CWE-918 · Server-side request forgery · OWASP A10
Target
POST /products/:id/image-from-url and the EC2/ECS metadata endpoints
what SSRF isIMDSv2 and hop limitsURL allowlisting done properlyegress controlGuardDuty credential-exfiltration findings

Run these techniques only against the lab you own. Using them on systems you don't have permission to test is illegal.

The threat

  1. 01What SSRF is and why cloud makes it worse

    Server-side request forgery happens when an application fetches a URL supplied by a user. The request comes FROM the server, so it can reach things the user can't: internal services, admin endpoints, and in the cloud, the instance METADATA SERVICE at a link-local address, which hands out the machine role's temporary credentials.

    This isn't theoretical: an SSRF reaching the metadata service was central to the 2019 Capital One breach, which exposed data on roughly 100 million people. It's why AWS introduced IMDSv2.

What's at risk

  • Whatever the instance or task role can do in AWS, available to anyone who can call the feature.

Detect

  1. 01Find user-controlled URLs reaching network calls

    Semgrep's SSRF rules follow request data into fetch, axios, and http.request. Every finding is a feature that needs an allowlist.

    terminal
    $ docker run --rm -v "$PWD:/src" semgrep/semgrep:1.128.0 semgrep scan --config p/ssrf -q /src/app
    ── output ──
    app/server.js
    ❯❱ javascript.express.security.audit.express-ssrf.express-ssrf
    The following request uses user input to build its URL. This could lead to
    server-side request forgery ...
    41┆ const img = await fetch(req.body.url);
  2. 02Check whether instances still allow IMDSv1

    IMDSv1 answers any plain GET, which is exactly what a naive SSRF can send. IMDSv2 requires a session token obtained with a PUT that includes a special header, which typical SSRF primitives can't produce. List instances that still accept v1.

    terminal
    $ aws ec2 describe-instances --query "Reservations[].Instances[?MetadataOptions.HttpTokens=='optional'].[InstanceId,Tags[?Key=='Name']|[0].Value]" --output text
    ── output ──
    i-0a1b2c3d4e5f60718 legacy-image-worker
  3. 03Runtime: GuardDuty notices stolen instance credentials

    If instance credentials are used from an IP outside AWS, GuardDuty raises UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS (Terraform course, Mission 7.3). Route it to the pager.

Defend

  1. 011. Require IMDSv2 with a hop limit of 1

    http_tokens = required disables v1 entirely. A hop limit of 1 means the token response can't travel beyond the instance itself, so containers on bridge networks can't use it either unless intended. ECS tasks use their own credentials endpoint, but the same principle applies: scope what the app's role can do.

    Vulnerable

    infra/probe.tfadd to filehcl
    resource "aws_instance" "worker" {
      # metadata_options not set → IMDSv1 allowed on older AMIs/defaults
    }

    Hardened

    infra/probe.tfadd to filehcl
    resource "aws_instance" "worker" {
      metadata_options {
        http_tokens                 = "required"   # IMDSv2 only
        http_put_response_hop_limit = 1
        http_endpoint               = "enabled"
      }
    }
  2. 022. Allowlist destinations — and check the resolved IP

    Accept only https, only hosts on an allowlist (your CDN, known partners), and reject any hostname that RESOLVES to a private, loopback, or link-local address, because an attacker-controlled domain can point anywhere. Disable redirects (or re-validate each hop) and cap size and time. Libraries like ssrf-req-filter implement the IP checks at the connection level, which also defeats DNS rebinding.

    Vulnerable

    server.jsadd to filejs
    const img = await fetch(req.body.url);

    Hardened

    server.jsadd to filejs
    const ALLOWED_HOSTS = new Set(["images.acme-cdn.com", "partner-media.example.com"]);
    
    function assertAllowed(raw) {
      const url = new URL(raw);
      if (url.protocol !== "https:") throw new Error("https only");
      if (!ALLOWED_HOSTS.has(url.hostname)) throw new Error("host not allowed");
      return url;
    }
    
    const url = assertAllowed(req.body.url);
    const img = await fetch(url, {
      redirect: "error",
      signal: AbortSignal.timeout(3000),
      // plus an agent that refuses private/link-local IPs at connect time
    });
  3. 033. Restrict egress at the network layer

    Services that fetch external content should have egress limited to what they need (an egress proxy with an allowlist, or security group and NACL rules). Even a missed code path then can't reach arbitrary internal addresses.

Verify

  1. 01Every instance requires IMDSv2; the allowlist rejects everything else

    The instance query returns nothing. Unit tests for assertAllowed cover: http scheme rejected, unknown host rejected, allowed host accepted, and an allowed-looking hostname resolving to a private IP rejected by the connection filter.

    terminal
    $ aws ec2 describe-instances --query "Reservations[].Instances[?MetadataOptions.HttpTokens=='optional'].InstanceId" --output text | wc -w
    npm test -- ssrf
    ── output ──
    0
    ✔ rejects non-https URLs
    ✔ rejects hosts outside the allowlist
    ✔ accepts allowlisted CDN URLs
    ✔ rejects allowlisted names resolving to private addresses
    4 passing

The concepts

Why blocklists fail for SSRF

Blocking '169.254.169.254' or 'localhost' as strings misses alternative encodings of the same address, IPv6 forms, DNS names that resolve to internal IPs, and redirects from an allowed host to an internal one. Robust defences are allowlists of destinations plus checking the IP actually connected to, combined with making the internal targets themselves safe (IMDSv2, authenticated internal services).

Your turn

01

How do you enforce IMDSv2 across a whole AWS account, including instances created by hand?

Interview questions

01

What is SSRF and how do you defend against it in the cloud?

0/4 · 0%