割り当て済み 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 バージョンがあるとバージョンチェックに失敗し、error ラベルにジャンプします。そのため、割り当てが実行される前に -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
認証されていない攻撃者は、いくつかの短いリクエストでサーバのワーカーをクラッシュさせ、再送信によってサービスをオフラインにし続けることができます。これはサービス拒否 (DoS) です。
dataReceived() で、valid_requ が false の場合、buildResponse() を呼び出さずにエラーレスポンスを構築して return します。buildResponse() では、req->url を使用する前に NULL かどうかをチェックします。パースが失敗し得る前に URL オブジェクトを割り当てることでも、この問題を解消できます。