## https://sploitus.com/exploit?id=PACKETSTORM:227153
# Security Advisory: Unauthenticated Path Traversal Allows Arbitrary File Read (TinyWeb)
**Assigned CVE ID:** CVE-2026-67185
## Summary
TinyWeb builds the path of the file to serve by joining the web root with the request URL exactly as it arrives, with no normalization. A remote client that has never authenticated can insert `../` sequences into the request line and read any file the server process can open, including files outside the web root.
## Affected software
- Project: TinyWeb (https://github.com/GeneralSandman/TinyWeb)
- Version reported by the build: `TnyWeb/0.0.8`
- Affected commits: `0b3b5fd` (2019-01-09), where the path is first assembled this way, through `a381da2` (2023-11-22, latest on `master`).
- Fixed version: none. The project has no tagged releases; every published revision in that range is affected.
## Classification
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory ("Path Traversal")
- CVSS 4.0 base score: 8.7 (High)
- Vector: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/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). No credentials, no user interaction, and no prior access are required. A single request is enough.
## Technical details
`HttpBuilder::buildResponse()` starts with the configured web root and appends the URL path field straight from the parsed request:
```cpp
// src/tiny_http/http_responser.cc:80
sdsncat(&file_path, server.www.c_str(), server.www.size());
// src/tiny_http/http_responser.cc:83-87
if (url->field_set & (1 << HTTP_UF_PATH)) {
unsigned int off = url->fields[HTTP_UF_PATH].offset;
unsigned int len = url->fields[HTTP_UF_PATH].len;
sdsncat(&file_path, url->data + off, len);
}
// src/tiny_http/http_responser.cc:90-94
std::string f(file_path.data, file_path.len);
file_type = isRegularFile(f);
if (0 == file_type) {
return_val = file->setFile(f); // stat() + open() on the joined path
}
```
Between the join at line 86 and the filesystem call at line 94 there is no dot-segment removal (RFC 3986 section 5.2.4), no `realpath()` canonicalization, and no check that the result stays inside `server.www`.
The URL parser does not filter `..` either. In `HttpParser::parseUrlChar()` the path state accepts any character that passes `isUrlChar()`, and `.` is a valid URL character, so `../` reaches `buildResponse()` unchanged:
```cpp
// src/tiny_http/http_parser.cc:653-654
case s_requ_path: //finished
if (isUrlChar(ch))
return s_requ_path;
```
`HttpFile::setFile()` then opens and serves whatever path was produced, using the privileges of the server process:
```cpp
// src/tiny_http/http_model_file.cc:43
return_val = open(name.c_str(), O_RDONLY);
```
Note: TinyWeb never percent-decodes the URL path, so `%2e%2e%2f` does not work. The literal `../` form does.
## Proof of concept
1. Start the server with the shipped configuration (listening on port 9090, serving from the configured web root).
2. Send a request whose path climbs above the web root. The `--path-as-is` flag stops curl from collapsing the `../` before sending:
```
curl --path-as-is 'http://TARGET:9090/../../../../etc/passwd'
```
3. The server responds `200 OK` with the contents of `/etc/passwd`:
```
HTTP/1.1 200 OK
Server: TinyWeb/0.0.8
Transfer-Encoding: chunked
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
```
The number of `../` segments needed depends on how deep the web root sits. Any count at or above the web root depth works, because the kernel ignores `..` at the filesystem root.
## Impact
Any remote client can read any file the server process is allowed to open. In the default setup the workers run as root, so this includes `/etc/shadow`, TLS private keys, configuration files with credentials, and the application's own source under the web root.
## Remediation
Canonicalize the joined path with `realpath()` and reject the request unless the result is inside the configured web root. As defense in depth, remove dot-segments during URL parsing per RFC 3986 section 5.2.4, and percent-decode the path before that normalization runs.