CVE-2024-30260 / SSRF & Request Splitting

CVE-2024-30260: Node.js Undici CRLF Injection & SSRF Teardown

How unvetted carriage return characters in HTTP header values allowed attackers to split outgoing HTTP requests in Node.js globalThis.fetch().

💡 Plain English Explainer (ELI5)

Imagine you send a telegraph that says: 'Ship 10 boxes of apples'. But you sneak in special hidden control instructions: 'STOP. IGNORE PREVIOUS. TRANSFER ALL FUNDS TO EVIL CORP'. The telegraph operator reads the message line by line and treats the second line as a brand-new official instruction, sending your money straight to the attacker.

Core Concepts & Subsystem Terms

Undici
The official next-generation HTTP/1.1 client for Node.js, powering the global `fetch()` implementation.
CRLF Injection
Injecting Carriage Return (`\r`) and Line Feed (`\n`) characters into HTTP headers to create a new header or a whole new request.
HTTP Request Splitting
A vulnerability where an attacker splits a single outgoing HTTP connection into two separate requests.
SSRF (Server-Side Request Forgery)
Coercing a backend server to issue unauthorized requests to internal cloud metadata APIs (`169.254.169.254`).

Step-by-Step Exploit Mechanics

Step 1

1. Input Submission

An attacker supplies a crafted header value: Admin\r\nHost: 169.254.169.254.

Step 2

2. Application Issues Fetch

The Node.js backend calls fetch(url, { headers: { 'X-User': input } }).

Step 3

3. Header Splitting in Undici

Undici serializes the headers without sanitizing \r\n, injecting the forged Host header.

Step 4

4. Cloud Metadata Exfiltration

The internal proxy directs the request to the cloud metadata service, exposing AWS/GCP credentials.

Source Code: Fatal Flaw vs. High-Level Fix

Delivered in clean, readable high-level source code (no raw assembly or binary diffs).

UNPATCHED FLAW
// VULNERABLE: lib/core/request.js before patch
function addHeader(headers, key, value) {
    // ROOT CAUSE:
    // Does not sanitize or reject carriage return (\r) and line feed (\n) in values!
    // Allows attackers to split headers and inject arbitrary HTTP directives!
    headers[key] = value;
}
HARDENED PATCH
// SECURE: lib/core/request.js patch
function addHeader(headers, key, value) {
    // 1. Strict regex checking for dangerous control characters
    const INVALID_HEADER_CHAR_REGEX = /[\r\n]/;
    
    if (INVALID_HEADER_CHAR_REGEX.test(key) || INVALID_HEADER_CHAR_REGEX.test(value)) {
        throw new TypeError(`Invalid character in header content: ["${key}": "${value}"]`);
    }
    
    headers[key] = value;
}

Engineering & System Hardening Checklist

← Browse Full Security Directory Explore All Source Teardowns →