
Avviso di sicurezza: perdita di memoria non autenticata che porta all'esaurimento della memoria (TinyWeb)
ID CVE assegnato: CVE-2026-67183
TinyWeb alloca diversi oggetti durante l'analisi di ogni richiesta e non li libera mai. Le strutture delle richieste e degli header non hanno distruttori e nulla le elimina dopo l'invio della risposta. La memoria dei worker cresce a ogni richiesta e non scende mai, quindi un flusso costante di richieste ordinarie porta il worker all'esaurimento della memoria.
TnyWeb/0.0.8e48f15d (2018-11-20), in cui sono state introdotte queste strutture e il parser, fino a a381da2 (2023-11-22, ultimo su master).CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NUn attaccante remoto non autenticato che può raggiungere la porta TCP in ascolto del server (9090 nella configurazione fornita) e inviare richieste a un tasso sostenuto moderato. Non sono richieste credenziali né interazione con l'utente. Le richieste possono essere ben formate; non è necessario alcun payload speciale.
Per ogni richiesta, HttpParser::execute() alloca un Url, un HttpHeaders e un HttpHeader per ogni riga di header:
// src/tiny_http/http_parser.cc:1692
request->url = new Url;
// src/tiny_http/http_parser.cc:1709
request->headers = new HttpHeaders;
// src/tiny_http/http_parser.cc:1033-1040
HttpHeader* header = new HttpHeader;
return_val = parseHeader(stream, offset, len, header);
...
result->generals.push_back(header); // stored in a raw-pointer list
HttpRequest e HttpHeaders sono semplici struct senza distruttore, quindi distruggere un HttpRequest non libera nessuno di questi:
// src/tiny_http/http_parser.h:442
typedef struct HttpRequest {
...
Url* url;
HttpHeaders* headers;
HttpBody* body;
} HttpRequest;
// src/tiny_http/http_parser.h:279-304
typedef struct HttpHeaders {
...
std::list<HttpHeader*> generals; // raw pointers, never deleted
...
} HttpHeaders;
WebProtocol conserva la richiesta in uno std::shared_ptr<HttpRequest> (src/tiny_http/http_protocol.h:49). Quando la richiesta successiva la sostituisce, viene eseguito il ~HttpRequest predefinito che perde url, headers, body e ogni HttpHeader in generals. L'unico delete request nel codice si trova in getDataFromProxy() (src/tiny_http/http_protocol.cc:194), una funzione che il percorso della richiesta non chiama mai. I buffer di risposta per richiesta del memory pool contribuiscono alla crescita.
Avviare il server con la configurazione fornita (in ascolto sulla porta 9090). Annotare l'ID di processo (PID) di un worker.
Registrare la memoria residente del worker:
grep VmRSS /proc/<worker_pid>/status
for i in $(seq 1 2000); do
curl -s -o /dev/null http://TARGET:9090/
done
VmRSS. È cresciuta e non scende in seguito. Un'esecuzione rappresentativa:VmRSS before: 3704 kB
after 500 requests: 4788 kB
after 1000 requests: 22068 kB
after 1500 requests: 41652 kB
after 2000 requests: 59956 kB
La crescita è monotona, circa 20-28 kB per richiesta, senza plateau. Ripetere il lotto fa continuare la salita finché il worker non viene terminato per esaurimento della memoria.
Un attaccante non autenticato può esaurire la memoria di un worker con un flusso costante di richieste e causare una condizione di esaurimento della memoria, negando il servizio. Anche il traffico ordinario causa perdite di memoria nel tempo, quindi la condizione può verificarsi anche senza un attaccante.
Fornire a HttpRequest e HttpHeaders dei distruttori che liberino url, headers, body e ogni HttpHeader in generals, oppure conservarli in puntatori intelligenti così che vengano rilasciati automaticamente. Rilasciare anche i buffer del memory pool per richiesta una volta che la risposta è stata inviata.