## https://sploitus.com/exploit?id=521582AC-2452-55F6-83CC-FA8F15B5F4FD
# CVE-2026-12243 β NLTK Path Traversal Lab
[](https://nvd.nist.gov/vuln/detail/CVE-2026-12243)
[](https://github.com/advisories/GHSA-m42h-3232-vpv3)
[](#safety-model)
[](LICENSE)
A small, reproducible Docker lab that contrasts the percent-encoded path
traversal behavior in NLTK 3.9.4 with the corrected behavior in NLTK 3.10.0.
The vulnerable version validates the resource name before URL decoding. The
fixed payload `%2e%2e/%2e%2e/outside/lab-secret.txt` passes the check, is later
decoded to `../../outside/lab-secret.txt`, and escapes both the configured
NLTK data directory and the process working directory. The patched version
rejects the same input.
> [!CAUTION]
> This repository is for defensive education and authorized testing only. The
> lab uses a fixed synthetic secret inside an isolated container. Do not adapt
> it to access systems or data you do not own or have explicit permission to
> test.
## At a glance
| Container | NLTK version | Expected result |
| --- | ---: | --- |
| `vulnerable` | `3.9.4` | Reads the synthetic marker outside `/lab/nltk_data` |
| `patched` | `3.10.0` | Blocks the encoded traversal |
No corpus download, server, port, bind mount, or runtime internet connection
is used.
## Prerequisites
- Docker Engine or Docker Desktop
- Docker Compose v2 (`docker compose`)
- A POSIX shell for `run.sh`
## Usage
```bash
git clone https://github.com/morzelowski/CVE-2026-12243-NLTK-PoC.git
cd CVE-2026-12243-NLTK-PoC
docker compose build
./run.sh
```
Expected result:
```text
=== Vulnerable image: NLTK 3.9.4 ===
NLTK version : 3.9.4
Working directory : /app
Configured data dir: /lab/nltk_data
Encoded resource : %2e%2e/%2e%2e/outside/lab-secret.txt
Decoded resource : ../../outside/lab-secret.txt
Resolved candidate : /outside/lab-secret.txt
NLTK warning : Security Violation [pathsec.open]: Unauthorized path /outside/lab-secret.txt
NLTK result : read 36 bytes
Synthetic marker : CVE-2026-12243-SYNTHETIC-LAB-MARKER
[PASS] Traversal escaped the configured NLTK data directory.
=== Patched image: NLTK 3.10.0 ===
NLTK version : 3.10.0
NLTK result : blocked (ValueError: Unsafe resource path: '...')
[PASS] Patched version rejected the same encoded traversal.
Lab completed: traversal reproduced and patched rejection verified.
```
The affected image reports that `/outside/lab-secret.txt` is unauthorized but
still returns its contents because NLTK 3.9.4 uses warning-only path security
by default. Exception and warning text can vary slightly; `run.sh` uses the
process exit status, not a fragile text match, to decide whether the
demonstration passed.
## How the demonstration works
Each image contains this deliberately separated layout:
```text
/
βββ app/
β βββ poc.py # process CWD is /app
βββ lab/
β βββ nltk_data/ # configured NLTK search root
βββ outside/
βββ lab-secret.txt # public synthetic marker
```
`poc.py` replaces `nltk.data.path` with `/lab/nltk_data` and passes the fixed
resource name `%2e%2e/%2e%2e/outside/lab-secret.txt` to
`nltk.data.load(..., format="raw")`. In the affected release, decoding occurs
too late in the validation flow:
```text
/lab/nltk_data
β ../../outside/lab-secret.txt
/outside/lab-secret.txt
```
Because the working directory is `/app`, the canary is outside both locations
that matter to the demonstration: `/app` and `/lab/nltk_data`.
The script has two explicit assertions:
1. NLTK 3.9.4 must return the known synthetic marker.
2. NLTK 3.10.0 must reject the exact same resource name.
Any other result exits non-zero, so automated checks cannot report a false
success.
## Safety model
The Compose configuration intentionally limits the lab:
- `network_mode: none` disables networking while either demonstration runs.
- No host ports are published.
- No host directories or Docker socket are mounted.
- The containers run as an unprivileged user with all Linux capabilities
dropped and `no-new-privileges` enabled.
- The root filesystem is read-only; only a small, `noexec` temporary filesystem
is writable.
- The only disclosed value is `fixtures/lab-secret.txt`, a public test marker
copied into the image during build.
The image build needs package-index access to install the two pinned NLTK
versions. Runtime execution is offline.
## Technical details
- **CVE:** CVE-2026-12243
- **GHSA:** GHSA-m42h-3232-vpv3
- **Weakness:** CWE-22 β path traversal
- **Affected:** NLTK versions before 3.10.0
- **Fixed:** NLTK 3.10.0
- **Impact:** disclosure of files readable by the application process when an
attacker controls a resource name supplied to `nltk.data.load()` or
`nltk.data.find()`
The lab pins 3.9.4 because it is the concrete release named in the original
report and 3.10.0 because it is the first patched release recorded by the
maintainer advisory.
## Mitigation
Upgrade NLTK and keep it current:
```bash
python -m pip install "nltk>=3.10.0"
```
Also avoid passing untrusted resource names into filesystem-loading APIs.
Where user selection is required, map user-facing identifiers to an allowlist
of application-owned resources instead of accepting paths or URLs directly.
## Manual commands
Run either side independently:
```bash
docker compose run --rm vulnerable
docker compose run --rm patched
```
Inspect the fully resolved Compose configuration:
```bash
docker compose config
```
Remove the locally built lab images:
```bash
docker compose down --rmi local
```
## Troubleshooting
- **`permission denied: ./run.sh`** β run `chmod +x run.sh` once.
- **`docker: command not found`** β install Docker Desktop or Docker Engine.
- **Package download errors during build** β confirm Docker has temporary
outbound access for the build, then retry `docker compose build --no-cache`.
## References
- [GitHub maintainer advisory GHSA-m42h-3232-vpv3](https://github.com/advisories/GHSA-m42h-3232-vpv3)
- [NVD entry for CVE-2026-12243](https://nvd.nist.gov/vuln/detail/CVE-2026-12243)
- [Original NLTK issue #3504](https://github.com/nltk/nltk/issues/3504)
- [NLTK fix pull request #3522](https://github.com/nltk/nltk/pull/3522)
- [Fix commit `aec4fce`](https://github.com/nltk/nltk/commit/aec4fce1b84ad725b8975f7365b23a4f626572a9)
## License
Released under the [MIT License](LICENSE).