
Aviso de seguridad: una fuga de memoria sin autenticación conduce al agotamiento de la memoria (TinyWeb)
Identificador CVE asignado: CVE-2026-67183
TinyWeb asigna varios objetos al analizar cada solicitud y nunca los libera. Las estructuras de solicitud y de cabeceras no tienen destructores, y nada las elimina después de enviarse la respuesta. La memoria del worker crece con cada solicitud y nunca disminuye, por lo que un flujo constante de solicitudes ordinarias hace que el worker se quede sin memoria.
TnyWeb/0.0.8e48f15d (2018-11-20), donde se introdujeron estas estructuras y el analizador, hasta a381da2 (2023-11-22, el más reciente en 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 atacante remoto no autenticado que pueda alcanzar el puerto TCP de escucha del servidor (9090 en la configuración distribuida) y enviar solicitudes a una tasa sostenida moderada. No se requieren credenciales ni interacción del usuario. Las solicitudes pueden estar bien formadas; no se necesita ninguna carga útil especial.
Para cada solicitud, HttpParser::execute() asigna un Url, un HttpHeaders y un HttpHeader por cada línea de cabecera:
// 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 y HttpHeaders son estructuras simples sin destructor, por lo que destruir un HttpRequest no libera ninguna de estas:
// 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 almacena la solicitud en un std::shared_ptr<HttpRequest> (src/tiny_http/http_protocol.h:49). Cuando la siguiente solicitud la reemplaza, se ejecuta el ~HttpRequest por defecto y se filtran url, headers, body y cada HttpHeader de generals. El único delete request del código está en getDataFromProxy() (src/tiny_http/http_protocol.cc:194), una función que la ruta de solicitud nunca invoca. Los búferes de respuesta por solicitud del memory pool contribuyen al crecimiento.
Inicie el servidor con la configuración distribuida (escuchando en el puerto 9090). Anote el ID de proceso de un worker.
Registre 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 nuevamente. Ha aumentado y no disminuye después. Una ejecución representativa:VmRSS before: 3704 kB
after 500 requests: 4788 kB
after 1000 requests: 22068 kB
after 1500 requests: 41652 kB
after 2000 requests: 59956 kB
El crecimiento es monótono, de aproximadamente 20 a 28 kB por solicitud, sin meseta. Repetir el lote continúa la subida hasta que el worker es eliminado por quedarse sin memoria.
Un atacante no autenticado puede agotar la memoria de un worker con un flujo sostenido de solicitudes y forzar una condición de agotamiento de memoria, denegando el servicio. El tráfico ordinario también provoca fugas con el tiempo, por lo que la condición puede ocurrir sin un atacante.
Proporcione a HttpRequest y HttpHeaders destructores que liberen url, headers, body y cada HttpHeader de generals, o manténgalos en punteros inteligentes para que se liberen automáticamente. Libere también los búferes del memory pool por solicitud una vez que se haya enviado la respuesta.