flawopen.com/Teardowns/cve-2022-31150-undici-crlf-injection-ssrf

● CVE-2022-31150 · CVSS 6.5 · 中危
安全研究 · FlawOpen

深度技术拆解:CVE-2022-31150: Node.js Undici CRLF Injection & SSRF Teardown

CVE-2022-31150 源代码级技术深度解析与系统加固工程指南:深入剖析漏洞触发条件、攻击利用链条与加固补丁的具体实现。

💡 通俗易懂的原理解析 (ELI5)

想象您通过邮局寄出一封信件,内容为:"寄出10箱苹果"。但您在信封中偷偷夹带了第二页伪造的官方文件:"停下。忽略上一指令。将所有款项转移给黑客组织"。邮政办事员按顺序阅读纸张时,将第二页误当作一封全新的正式信件,从而直接将资金转给了攻击者。

核心概念与专有名词

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).

根本原因剖析 (Root Cause)

根本原因在于开源系统中未经验证的边界参数,导致状态不同步并绕过安全控制。

攻击执行流程分解

Step 1

攻击阶段剖析:Input Submission

技术利用机制与执行路径分析:An attacker supplies a crafted header value: Admin\r\nHost: 169.254.169.254.

Step 2

攻击阶段剖析:Application Issues Fetch

技术利用机制与执行路径分析:The Node.js backend calls fetch(url, { headers: { 'X-User': input } }).

Step 3

攻击阶段剖析:Header Splitting in Undici

技术利用机制与执行路径分析:Undici serializes the headers without sanitizing \r\n, injecting the forged Host header.

Step 4

数据外发与窃取(Cloud Metadata Exfiltration)

技术利用机制与执行路径分析:The internal proxy directs the request to the cloud metadata service, exposing AWS/GCP credentials.

源代码对比:漏洞与安全实现

存在漏洞的实现
// 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;
}
加固后的安全修复
// 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;
}

工程与系统安全加固清单

参考来源