flawopen.com/Teardowns/cve-2021-22960-nodejs-llhttp-request-smuggling

● CVE-2021-22960 · CVSS 6.5 · 中
セキュリティ研究 · FlawOpen

技術解説とコード分析:CVE-2021-22960: Node.js llhttp HTTP Request Smuggling Teardown

vulnerabilidade に関する技術的なソースコード解析と堅牢化対策:脆弱性の根本原因と安全な実装パッチの詳細。

💡 わかりやすい解説 (ELI5)

直感的な物理的アナロジー解説:Imagine a two-window ticket booth. Window 1 reads: 'One ticket for Alice'. Window 2 reads: 'Wait, there's another ticket for Bob glued to the back'. Because Window 1 ignored the extra ticket, Bob's ticket sits in the window until Charlie walks up. Charlie gets handed Bob's ticket and private receipt, allowing an attacker to steal user sessions.

主要な概念と専門用語

llhttp
The high-performance C HTTP parser library embedded inside Node.js to parse incoming HTTP/1.1 requests.
HTTP Request Smuggling
Desynchronization between frontend reverse proxies (e.g. NGINX, Cloudflare) and backend Node.js servers regarding request boundaries.
Chunked Transfer-Encoding
An HTTP streaming mechanism where payloads are sent in numbered chunks, terminated by a 0\r\n\r\n chunk.
Chunk Extensions
Optional semicolon-delimited parameters appended to the chunk length (0;extension=value\r\n).

根本原因の分析 (Root Cause)

根本原因は、オープンソースシステムにおける未検証の境界パラメータに起因し、状態の非同期化とセキュリティ制御の迂回を可能にします。

ステップ・バイ・ステップの攻撃フロー

Step 1

攻撃フェーズ:Send Ambiguous Request

技術的な脆弱性悪用メカニズムと実行フローの詳細:The attacker sends an HTTP request with chunk extensions containing unescaped control characters.

Step 2

攻撃フェーズ:Frontend Interpretation

技術的な脆弱性悪用メカニズムと実行フローの詳細:The frontend proxy treats the entire payload as a single continuous request body.

Step 3

攻撃フェーズ:Node.js Parser Desync

技術的な脆弱性悪用メカニズムと実行フローの詳細:Node.js's llhttp stops parsing prematurely, leaving the smuggled second request in the TCP buffer.

Step 4

制御フローのハイジャック(Victim Session Hijacking)

技術的な脆弱性悪用メカニズムと実行フローの詳細:The next innocent user's request is prepended to the smuggled request, exfiltrating their session tokens.

ソースコード比較:脆弱 vs 堅牢化

脆弱な実装
// VULNERABLE: deps/llhttp/src/llhttp.c before patch
int llhttp__on_chunk_extension(llhttp_t *parser, const char *p, const char *end) {
    // ROOT CAUSE:
    // Does not enforce strict ASCII validation on chunk extension tokens!
    // Tolerates invalid whitespace and carriage return sequences, desyncing from proxies!
    while (p < end && *p != '\r' && *p != '\n') {
        p++; // Skips unvetted extension bytes
    }
    return 0;
}
堅牢化されたセキュアパッチ
// SECURE: deps/llhttp/src/llhttp.c patch
int llhttp__on_chunk_extension(llhttp_t *parser, const char *p, const char *end) {
    // 1. Strictly validate characters inside chunk extensions according to RFC 9112
    while (p < end && *p != '\r' && *p != '\n') {
        uint8_t ch = (uint8_t)*p;
        // Disallow spaces, control codes, and invalid token bytes
        if (ch <= 0x20 || ch >= 0x7F || ch == ';') {
            return HPE_INVALID_CHUNK_SIZE; // Reject immediately!
        }
        p++;
    }
    return 0;
}

エンジニアリング&システム堅牢化チェックリスト

参考資料