# REPRO-2026-00070: wlc: Path traversal via unsanitized API slugs in download command ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00070 GHSA: GHSA-mmwx-79f6-67jg CVE: CVE-2026-23535 ## Package Name: wlc Ecosystem: pip Affected: wlc < 1.17.2 Fixed: wlc 1.17.2 ## Root Cause ## Summary A malicious Weblate server can abuse the `wlc download` command’s multi-component workflow to write files outside the caller’s chosen output directory. The client trusts component metadata returned by the server and uses the embedded project slug verbatim when constructing the zip filename, allowing absolute paths to be honored and causing arbitrary file writes on the filesystem running wlc < 1.17.2. ## Impact - **Package:** Weblate `wlc` CLI - **Affected versions:** All releases prior to 1.17.2 - **Risk:** High – a crafted server (or MITM) can force the client to overwrite arbitrary files (e.g., `/etc/cron.d/...`) during `wlc download --multi`, leading to privilege escalation or system compromise on any machine that runs the command. ## Root Cause `wlc.main.Download.download_component()` builds the destination path as `Path(output_dir) / f"{component.project.slug}-{component.slug}.zip"`. The values `component.project.slug` and `component.slug` come directly from API responses without sanitization. When a malicious server returns an absolute path such as `/tmp/weblate-owned` for the project slug, `pathlib.Path` treats it as an absolute path and drops the intended base directory, writing the downloaded archive to that attacker-chosen location. The issue is fixed upstream in https://github.com/WeblateOrg/wlc/pull/1128 (released in 1.17.2) which normalizes and validates slugs before using them in filesystem paths. ## Reproduction Steps 1. Run `repro/reproduction_steps.sh`. 2. The script provisions a Python venv, installs `wlc==1.17.1`, starts a mock Weblate HTTP server that serves a component listing whose project slug is `/tmp/weblate-owned`, and then executes `wlc download --output repro-output` against it. 3. Successful reproduction is indicated by creation of `/tmp/weblate-owned-absolute-component.zip` (outside the requested output directory) and logs stored in `logs/`. ## Evidence - Log artifacts: `logs/mock_server.log`, `logs/wlc_download.log`, `logs/evidence.txt`. - Key excerpt (`logs/evidence.txt`): - `-rw-r--r-- 1 root root 58 ... /tmp/weblate-owned-absolute-component.zip` - Intended output directory `repro-output/` remains empty, proving the write escaped the sandbox. - Environment: Python 3.11 virtualenv with `wlc==1.17.1` on Ubuntu (container) as provisioned by the script. ## Recommendations / Next Steps - Upgrade `wlc` clients to 1.17.2 or later, which sanitizes slugs before constructing output paths. - For additional defense-in-depth, consider enforcing a jail by resolving all download outputs relative to the configured directory and rejecting absolute inputs from the server. - Add regression tests that simulate malicious slugs to ensure future changes preserve the validation. ## Additional Notes - `repro/reproduction_steps.sh` is idempotent: running it twice in succession succeeds, recreating the evidence and overwriting the malicious file each time. - The mock server only implements the minimal endpoints needed for this PoC; real-world instances may require authentication, but the vulnerability manifests regardless once the client processes attacker-controlled component metadata. ## Reproduction Details Reproduced: 2026-01-17T22:23:35.864Z Duration: 1259 seconds Tool calls: 216 Turns: 2 Handoffs: 1 ## Timeline (Key Moments) 1. [env_setup] Virtual Environment Created (repro) A Python virtual environment was created to isolate and prepare the vulnerable version for testing. 2. [poc_created] Mock Server Script Written (repro) The mock_weblate_server.py exploit or helper script was written, indicating development of the proof-of-concept. 3. [poc_created] Reproduction Steps Script Written (repro) The repro/reproduction_steps.sh script was created to automate the exploit execution. 4. [vuln_triggered] Exploit Script Executed (repro) The reproduction_steps.sh script was run, representing the first attempt to trigger the vulnerability. 5. [confirmation] Exploit Log Verified (repro) The logs/wlc_download.log was read after exploit execution to confirm successful vulnerability triggering. 6. [confirmation] Reproduction Completed (repro) A message confirmed that the reproduction was complete, indicating successful verification of the vulnerability. ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00070 pruva-verify GHSA-mmwx-79f6-67jg pruva-verify CVE-2026-23535 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00070&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00070/artifacts/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - GitHub Advisory: https://github.com/advisories/GHSA-mmwx-79f6-67jg - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-23535 - Source: https://nvd.nist.gov/vuln/detail/CVE-2026-23535 ## Artifacts - repro/reproduction_steps.sh (reproduction_script, 2017 bytes) - repro/rca_report.md (analysis, 3177 bytes) - bundle/ticket.md (ticket, 1116 bytes) - repro/mock_weblate_server.py (script, 4636 bytes) - logs/wlc_download.log (log, 77 bytes) - logs/mock_server.log (log, 427 bytes) - logs/evidence.txt (other, 477 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00070 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00070/artifacts/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00070 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev