CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-23644
7.70.06%

TarSlip on the CDN: Breaking esm.sh with Path Traversal

Alon Barad
Alon Barad
Software Engineer

Feb 16, 2026·6 min read·4 visits

PoC AvailableCISA KEV Listed

Executive Summary (TL;DR)

esm.sh failed to properly sanitize file paths inside NPM tarballs. Attackers can upload a package containing filenames like `../../evil.js`, causing the server to write files outside the intended directory. This can poison the CDN cache or overwrite server configs.

A critical Path Traversal vulnerability (TarSlip) in the esm.sh CDN allows attackers to escape the extraction directory when processing NPM packages. By crafting a malicious tarball with relative path sequences, an attacker can overwrite arbitrary files on the server, leading to potential Remote Code Execution (RCE) via cache poisoning or configuration tampering.

The Hook: When CDNs Go Rogue

We all love esm.sh. It’s the magic wand of the modern web—no build steps, just import and go. It acts as a bridge between the chaotic world of NPM and the browser, fetching packages, transpiling them, and serving them up on a silver platter. But here's the thing about magic: it usually relies on a lot of complex plumbing behind the scenes. And in this case, the plumbing involved untrusted tarballs.

At its core, esm.sh is a glorified unzipper. It grabs a compressed file from a registry (like NPM), extracts it to a temporary directory, and processes the contents. This is a classic setup for one of the oldest tricks in the book: the TarSlip attack. If you blindly trust the filenames inside a tar archive, you're asking for trouble. It's the digital equivalent of letting a delivery driver walk into your house and place a package directly into your safe, simply because the label said "To: Safe".

CVE-2026-23644 isn't just a file write bug; it's a potential supply chain nuke. Since esm.sh caches these files to serve to millions of users, poisoning the cache means poisoning every developer and application downstream that relies on that specific package version.

The Flaw: Trusting the Path

The vulnerability lies in the extractPackageTarball function. The developers made a classic mistake: confusing lexical cleaning with security boundaries. In Go, path.Clean (note the missing file prefix) is designed for URL-like paths. It simplifies strings like a/b/../c into a/c. It does not, however, check if the resulting path actually stays inside a specific directory on the host's filesystem.

Here is the logic flaw in a nutshell: The code attempted to split the filename by the first slash to remove the root folder (usually package/), and then joined it with the destination directory. But what happens if the tar entry is named package/../../../../etc/passwd? The split removes package/, leaving ../../../../etc/passwd. path.Join then happily concatenates this to your pkgDir.

Because the code wasn't verifying the final resolved path against the intended root directory, the operating system (or the filepath package if it were used correctly) would interpret those .. sequences effectively, walking right out of the sandbox. The developer locked the front door but left the walls completely missing.

The Code: Autopsy of a TarSlip

Let’s look at the vulnerable code pattern found in commit 9d77b88. It uses path (generic) instead of filepath (OS-aware) and relies on weak string manipulation.

The Vulnerable Code:

// Danger: blindly joining paths
_, name := utils.SplitByFirstByte(h.Name, '/')
// path.Clean purely parses the string, it doesn't resolve OS paths
filename := path.Join(pkgDir, path.Clean(name))
 
file, err := os.Create(filename) // OOPS. We just wrote outside pkgDir.

The Fix (Commit c62ab83c589e7b421a0e1376d2a00a4e48161093):

The patch introduces a robust defense-in-depth strategy. They switched to filepath, implemented a strict normalizer, and crucially, added an allowlist for extensions.

// 1. Normalize and strip leading slashes/dots
name := utils.NormalizePathname(h.Name)[1:]
 
// 2. Strict Extension Allowlist (Whitelisting > Blacklisting)
// Only allow web-safe assets (.js, .css, .json, etc.)
if !assetExts[ext] && !moduleExts[ext] {
    continue
}
 
// 3. Prevent symbolic link attacks
if h.Typeflag != tar.TypeReg {
    continue
}
 
// 4. Secure Join
targetPath := filepath.Join(pkgDir, name)
// (Implicit check: NormalizePathname prevents '..' traversal)

This fix is aggressive. By rejecting anything that isn't a regular file (no symlinks!) and enforcing strict extensions, they killed an entire class of attacks, not just the path traversal.

The Exploit: Poisoning the Well

Exploiting this requires a bit of creativity because we want to achieve something meaningful, not just write a junk file. Since esm.sh is a CDN, our goal is Cache Poisoning.

Attack Scenario:

  1. Preparation: We create a malicious NPM package evil-lib. Inside, we craft a tarball manually. We don't use npm pack; we use a script to insert a file header with the name package/../../installed_pkg/index.js.
  2. Targeting: We target a popular library that is likely already cached on the server, or we aim to overwrite a configuration file if we know the server layout.
  3. The Trigger: We publish evil-lib to the NPM registry (or a proxy). Then, we curl https://esm.sh/evil-lib. The server pulls our tarball and triggers the extraction logic.
  4. The Payload: The server extracts our file. The ../ sequences traverse out of the temp directory for evil-lib and overwrite the index.js of react or lodash stored in the adjacent directory.

Now, anyone requesting react from esm.sh gets our backdoor. This is supply chain poisoning at the infrastructure level.

The Impact: Why You Should Care

The CVSS score of 7.7 is respectable, but the business impact is catastrophic. For a service like esm.sh, integrity is the product. If users cannot trust that the code served matches the code published on NPM, the service is dead.

Potential Consequences:

  • RCE via Configuration: If the attacker can overwrite a .env file or a server config file (e.g., config.json), they can point the database credentials to their own server or change execution flags.
  • Mass Distribution of Malware: As described in the exploit section, overwriting widely used JavaScript libraries allows the attacker to inject crypto-miners, keyloggers, or session stealers into thousands of websites instantly.
  • Denial of Service: Simply overwriting critical system binaries or filling the disk with garbage data outside the temp folder.

The Fix: Remediation & Lessons

If you are running a self-hosted instance of esm.sh, you need to update immediately. The vulnerability was patched in commit c62ab83c589e7b421a0e1376d2a00a4e48161093 (Jan 16, 2026).

For Developers (The Golden Rule of Archives):

Never, ever trust the file paths inside a zip, tar, or any archive format. They are user input. Treat them with the same hostility you treat SQL parameters.

  1. Resolve Absolute Paths: Calculate filepath.Abs(destination_dir).
  2. Join and Check: Join the user-provided path with the destination. Calculate the absolute path of the result.
  3. Verify Prefix: Ensure the result starts with the intended destination directory prefix.
// The Safe Way
dest := "/tmp/safe_zone"
pathInArchive := header.Name // e.g., "../../etc/passwd"
 
// Join them
fullPath := filepath.Join(dest, pathInArchive)
 
// Check if it starts with dest
if !strings.HasPrefix(fullPath, filepath.Clean(dest) + string(os.PathSeparator)) {
    panic("Hacker detected!")
}

Official Patches

esm-devOfficial patch commit on GitHub

Fix Analysis (1)

Technical Appendix

CVSS Score
7.7/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P
EPSS Probability
0.06%
Top 81% most exploited

Affected Systems

esm.sh (Self-hosted instances)Go applications using insecure tar extraction patterns

Affected Versions Detail

Product
Affected Versions
Fixed Version
esm.sh
esm-dev
< 0.0.0-20260116051925-c62ab83c589e0.0.0-20260116051925-c62ab83c589e
AttributeDetail
CWE IDCWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
Attack VectorNetwork
CVSS v4.07.7 (High)
EPSS Score0.00061 (Low Probability)
ImpactArbitrary File Write / RCE
Exploit StatusPoC Available (TarSlip Standard)

MITRE ATT&CK Mapping

T1083File and Directory Discovery
Discovery
T1190Exploit Public-Facing Application
Initial Access
CWE-22
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Known Exploits & Detection

GitHub Security AdvisoryAdvisory detailing the TarSlip vulnerability and path traversal vectors.
NucleiDetection Template Available

Vulnerability Timeline

Incomplete fix (9d77b88) committed
2025-11-17
Robust fix (c62ab83) committed
2026-01-16
CVE-2026-23644 assigned
2026-01-18
Added to CISA KEV
2026-01-20

References & Sources

  • [1]GHSA-2657-3c98-63jq Advisory
  • [2]Snyk: Zip Slip Vulnerability Research

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.