分配的 CVE ID: CVE-2026-67184
当 TinyWeb 解析请求行失败时,它会返回错误,但请求的 URL 指针仍保持为 NULL。调用方忽略了该错误,并将请求传递给响应构建器,后者解引用了这个 NULL 指针并导致工作进程崩溃。少数格式错误的请求即可使整个服务器宕机,且无法恢复。
TnyWeb/0.0.8e48f15d(2018-11-20,此解析器与响应路径的引入点)至 a381da2(2023-11-22,master 上的最新提交)。CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N未认证的远程攻击者,能够访问服务器监听的 TCP 端口(随附配置中为 9090)。无需任何凭据或用户交互。
HttpParser::execute() 仅在请求行成功解析后才分配 URL 对象。格式错误的 HTTP 版本会使版本检查失败并跳转到错误标签,从而在分配执行之前返回 -1:
// src/tiny_http/http_parser.cc:1401 (the "H" of "HTTP" is expected here)
checkOrGoError((ch == 'H')); // false -> goto error
// src/tiny_http/http_parser.cc:1692 (skipped by the goto above)
request->url = new Url;
// src/tiny_http/http_parser.cc:1750-1753
error:
LOG(Debug) << "http request content is invalid\n";
return -1;
HttpRequest 通过 std::make_shared<HttpRequest>() 创建,这会对它进行值初始化,因此当分配被跳过时,url 保持为 NULL。
WebProtocol::dataReceived() 会记录返回值,但无论如何都会调用 buildResponse():
// src/tiny_http/http_protocol.cc:57-61
valid = m_nParser.execute(data.c_str(), begin, data.size(), m_pRequest.get());
valid_requ = (valid == -1) ? false : true;
// src/tiny_http/http_protocol.cc:75
m_nResponser.buildResponse(m_pRequest.get(), valid_requ, m_pResponse.get());
buildResponse() 接收到 valid_requ == false,但从未对其进行检查,并解引用了 NULL URL 指针:
// src/tiny_http/http_responser.cc:66
Url* url = req->url; // NULL
// src/tiny_http/http_responser.cc:83
if (url->field_set & (1 << HTTP_UF_PATH)) { // SIGSEGV
工作进程在此次访问时崩溃。它不会被重启到可服务的状态,因此一旦工作进程全部退出,服务器将停止响应。
使用随附配置启动服务器(监听 9090 端口)。
确认其能正常响应一个普通请求:
curl http://TARGET:9090/
XTTP/1.1 会在请求行已进入请求模式后导致版本检查失败:printf 'GET / XTTP/1.1\r\n\r\n' | nc TARGET 9090
附加到工作进程的调试器会显示崩溃发生在 NULL 解引用处:
#0 HttpBuilder::buildResponse (valid_requ=false) at src/tiny_http/http_responser.cc:83
#1 WebProtocol::dataReceived (data="GET / XTTP/1.1\r\n...") at src/tiny_http/http_protocol.cc:75
未认证的攻击者只需少量短请求即可使服务器工作进程崩溃,并通过不断重发这些请求使服务保持离线。这是一种拒绝服务攻击。
在 dataReceived() 中,当 valid_requ 为 false 时,构建错误响应并返回,而不是调用 buildResponse()。在 buildResponse() 中,在使用 req->url 之前检查其是否为 NULL。在解析可能失败之前就分配 URL 对象同样可以堵住这个漏洞。