
Sicherheitshinweis: Nicht authentifizierter Speicherleck führt zur Speichererschöpfung (TinyWeb)
Zugewiesene CVE-ID: CVE-2026-67183
TinyWeb weist beim Parsen jeder Anfrage mehrere Objekte zu und gibt sie nie wieder frei. Die Anfrage- und Header-Strukturen haben keine Destruktoren, und nichts löscht sie, nachdem die Antwort gesendet wurde. Der Arbeitsspeicher des Workers wächst mit jeder Anfrage und sinkt nie, sodass ein stetiger Strom gewöhnlicher Anfragen den Worker in die Speichererschöpfung treibt.
TnyWeb/0.0.8e48f15d (2018-11-20), in denen diese Strukturen und der Parser eingeführt wurden, bis a381da2 (2023-11-22, neuester Stand auf 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:NEin nicht authentifizierter entfernter Angreifer, der den lauschenden TCP-Port des Servers (9090 in der mitgelieferten Konfiguration) erreichen und Anfragen mit einer mäßigen, gleichmäßigen Rate senden kann. Es sind keine Anmeldeinformationen oder Benutzerinteraktion erforderlich. Die Anfragen können wohlgeformt sein; es ist keine spezielle Nutzlast erforderlich.
Für jede Anfrage weist HttpParser::execute() ein Url-Objekt, ein HttpHeaders-Objekt und pro Header-Zeile ein HttpHeader-Objekt zu:
// 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 und HttpHeaders sind einfache Strukturen ohne Destruktor, sodass die Zerstörung eines HttpRequest keines dieser Objekte freigibt:
// 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 hält die Anfrage in einem std::shared_ptr<HttpRequest> (src/tiny_http/http_protocol.h:49). Wenn die nächste Anfrage sie ersetzt, wird der Standard-~HttpRequest ausgeführt und url, headers, body sowie jedes HttpHeader in generals werden nicht freigegeben. Das einzige delete request im Code befindet sich in getDataFromProxy() (src/tiny_http/http_protocol.cc:194), einer Funktion, die der Anfragepfad nie aufruft. Die Antwortpuffer pro Anfrage aus dem Speicherpool tragen zusätzlich zum Anstieg bei.
Starten Sie den Server mit der mitgelieferten Konfiguration (er lauscht auf Port 9090). Notieren Sie die Prozess-ID eines Workers.
Erfassen Sie den residenten Speicher des Workers:
grep VmRSS /proc/<worker_pid>/status
for i in $(seq 1 2000); do
curl -s -o /dev/null http://TARGET:9090/
done
VmRSS erneut. Er ist gewachsen und fällt danach nicht wieder ab. Ein repräsentativer Lauf:VmRSS before: 3704 kB
after 500 requests: 4788 kB
after 1000 requests: 22068 kB
after 1500 requests: 41652 kB
after 2000 requests: 59956 kB
Das Wachstum ist monoton, etwa 20 bis 28 kB pro Anfrage, ohne dass ein Plateau erreicht wird. Die Wiederholung der Reihe setzt den Anstieg fort, bis der Worker wegen Speichermangels beendet wird.
Ein nicht authentifizierter Angreifer kann mit einem kontinuierlichen Anfragestrom den Speicher eines Workers erschöpfen und einen Out-of-Memory-Zustand erzwingen, was zur Verweigerung des Dienstes führt. Auch gewöhnlicher Datenverkehr führt mit der Zeit zu Speicherlecks, sodass der Zustand auch ohne Angreifer auftreten kann.
Versehen Sie HttpRequest und HttpHeaders mit Destruktoren, die url, headers, body und jedes HttpHeader in generals freigeben, oder halten Sie sie in Smart Pointern, sodass sie automatisch freigegeben werden. Geben Sie außerdem die Speicherpool-Puffer pro Anfrage frei, sobald die Antwort gesendet wurde.