Zum Inhalt springen
Auto-CTI
Zurück zu allen Deep Dives
ZERO DAY INITIATIVE - BLOG

Node.js Trust Falls: Dangerous Module Resolution on Windows

LOW CVE-2026-0776 CVE-2026-0775 LPE Windows

Strategische Zusammenfassung

Node.js auf Windows durchsucht bei der Modulauflösung standardmäßig C:\node_modules, ein Verzeichnis, das von Benutzern mit geringen Rechten erstellt werden kann. Angreifer können dort bösartige Module platzieren und eine lokale Rechteausweitung erreichen, wenn Anwendungen optionale oder fehlende Abhängigkeiten aufweisen. Obwohl es sich um eine bekannte Schwachstelle handelt, betrachtet das Node.js-Team dies nicht als Sicherheitslücke und verweist auf die Verantwortung der Entwickler. Fallstudien wie npm CLI und Discord belegen die konkrete Ausnutzbarkeit in der Praxis.

Key Findings

  • Die standardmäßige Modulauflösung von Node.js unter Windows durchläuft C:\node_modules, das für jeden Benutzer beschreibbar ist und das Einschleusen von Schadcode ermöglicht.
  • Dies führt zu lokaler Rechteausweitung, wenn Anwendungen optionale oder fehlende Abhängigkeiten haben und die Auflösung bis zum Wurzelverzeichnis fortschreitet.
  • Das empfohlene Muster für optionale Abhängigkeiten (try, require, catch) verschleiert das Problem: Fehlende Module werden stillschweigend ignoriert, aber die Suche erreicht dennoch C:\node_modules.
  • Drittanbieter-Bibliotheken tief im Abhängigkeitsbaum können dieses Verhalten unbeabsichtigt auslösen, ohne dass Entwickler es bemerken.
  • Das Node.js-Sicherheitsteam akzeptiert CWE-427 nicht als Schwachstelle und wälzt die Verantwortung auf Anwendungsentwickler ab, was zu einer Vielzahl angreifbarer Anwendungen führt.

Relevanz für dich

Ein systemisches Design-Problem in Node.js' Modulauflösung auf Windows ermöglicht Privilege Escalation durch lokale Dateisystem-Manipulation, bleibt aber von Discord und Node.js nicht als kritisch eingestuft.

Volltext

[Node.js Trust Falls: Dangerous Module Resolution on Windows]

Zero Day Initiative — Node.js Trust Falls: Dangerous Module Resolution on Windows

April 08, 2026 | Bobby Gould and Michael DePlante

In September of 2024, ZDI received a vulnerability submission from an anonymous researcher affecting npm CLI that revealed a fundamental design issue in Node.js. This blog details how it continues to expose applications to local privilege escalation (LPE) attacks on Windows systems, including the Discord desktop app (CVE-2026-0776 0-Day), which remains unpatched and vulnerable.

The issue is straightforward: when Node.js resolves modules, the runtime searches for packages in `C:\node_modules` as part of its default behavior. Since low-privileged Windows users can create this directory and plant malicious modules there, any Node.js application with missing or optional dependencies becomes vulnerable to privilege escalation.

"Node.js trusts the file system."

They do not treat CWE-427 (Uncontrolled Search Path Element) as a vulnerability, pushing responsibility onto application developers.

_Figure 1: The vendor’s security policy stance on CWE-427 as a non-issue_

As the case studies below demonstrate, this stance has dangerous consequences. Developers are largely unaware of this attack surface, and the result is a proliferation of exploitable applications. We will show examples in npm CLI and Discord, but there are likely many more applications that are impacted by this.

The root cause lies in the way Node.js performs module resolution. This is documented here. Although UNIX paths are used in the documentation provided by Node.js, the same logic is applied on Windows.

When a Node.js application calls require(‘bar’), the runtime searches for the module in the following order:

1. C:\Users\Administrator\projects\node_modules\bar.js 2. C:\Users\Administrator\node_modules\bar.js 3. C:\Users\node_modules\bar.js 4. C:\node_modules\bar.js <-- The problem

If the legitimate package is missing, whether due to optional dependencies, development packages removed in production, or installation failures, the resolution search will eventually reach the root of the drive. Any user can create `C:\node_modules` and place a malicious package there. Once the low-privileged user has populated `C:\node_modules\bar.js`, Node.js will load and execute it in the context of the current user. In the following case studies, we will provide evidence of how, despite properly following NPM’s guidelines, third-party dependencies end up triggering this vulnerability anytime you launch the application.

**Case Studies: Real-World Manifestations**

The Optional Dependency Pattern: npm supports optional dependencies to be specified in the project’s package.json file. The recommended pattern for checking for these dependencies is as follows:

_Figure 2: npm Docs showing optionalDependencies example code_

This pattern silently catches errors when optional packages are missing, allowing execution to continue. So what’s the problem? On Windows, Node.js will search all the way up to `C:\node_modules` where an attacker may have planted a malicious replacement. This search behavior mirrors UNIX conventions where `/node_modules` at the filesystem root is typically only writable by root. Windows systems by default allow any user to create `C:\node_modules`. Once `require` is called, Node.js will traverse the search path and execute any matching module it finds.

Important things to note:

1. This pattern can be found in third party libraries deep in a dependency tree, as we will see in the following examples. 2. There is no runtime indication to either the developers or the end users that such a vulnerability exists without looking at the filesystem logs with Procmon. 3. The optional dependency pattern itself would not be dangerous if Node.js did not search for packages in `C:\node_modules`.

Let’s take a deeper look at both cases and see why this is so dangerous.

**Case 1: npm CLI (ZDI-26-043 / ZDI-CAN-25430 / CVE-2026-0775)**.

Prior to version 11.2.0, npm CLI used a library called “promise-inflight”, which contained an optional dependency on a package called “bluebird”.

When Node.js is installed on the system, npm is included by default without the `bluebird` package. This vulnerability was introduced when bluebird was removed through a well-intentioned pull request (https://github.com/npm/cli/pull/1438/changes), demonstrating how easy it is for developers to unknowingly create this attack surface.

We can see Node’s package resolution logic at work in the screenshot below:

_Figure 4: Procmon log showing the package resolution behavior of Node.js via CVE-2026-0775_

First, the application looks for the `bluebird.js` package in the Node.js installation directory. Node.js sequentially searches back to the system root until it finds the package. If an attacker has placed `C:\node_modules\bluebird.js`, the `require` call will find, read, and execute the malicious payload in the context of any user running npm on the system.

This vulnerability is especially dangerous because it is triggered when many `npm *` cli commands are used. Common development commands such as `npm install`, `npm –l`, and `npm prune` will all execute the malicious `bluebird.js`package.

**Case 2: Discord (ZDI-26-040/ ZDI-CAN-27057 / CVE-2026-0776/ UNPATCHED)**

On April 22, 2025, ZDI received a report for a similar vulnerability in Discord reported by T. Doğa Gelişli. Discord uses the ws WebSocket library, which contains an optional dependency on utf-8-validate for compatibility with older Node.js versions:

Figure 5: websockets library repo snippet showing require call for missing utf-8-validate package dependency

Discord does not ship with the utf-8-validate package. As a result, the following Procmon logs show the same behavior as Case 1. Anytime Discord is launched, the attacker controlled `C:\node_modules\utf-8-validate.js` is executed.

Figure 6: Procmon log showing the package resolution behavior of Node.js via CVE-2026-0776

The ws library does support disabling this check via the `WS_NO_UTF_8_VALIDATE` environment variable, but this requires the consuming application (Discord) to set it explicitly. Here’s a quick video demonstrating the bug by popping the calc app when opening Discord:

Speed Go back to previous menu

0.5x 0.75x Normal 1.25x 1.5x 1.75x 2x

Exit fullscreen Enter fullscreen

Discord automatically opens on login by default, so in practice code execution happens immediately without any user interaction. Strangely, the Discord Security team made it clear to us in their responses that they do not consider local attack vectors as valid security issues.

The cases above represent only a few of the applications affected by this pattern. During our investigation we found many other independent reports. These issues in Mongo DB Compass and Mongo DB Shell are just two other examples.

Every Windows application built on Node.js with missing or optional dependencies is potentially vulnerable. This includes desktop applications that utilize Electron as well as popular web frameworks such as Next.js and React.

Each vendor has clearly stated that they will not treat these issues as vulnerabilities:

NPM’s response to our report:

_“exploits that require local access to a machine are considered ineligible for npm CLI_

Discord’s response to our report:

_“We do not consider physical/local attacks as valid security issues”_

_“Node.js trusts the file system in the environment accessible to it. Therefore, it is not a vulnerability if it accesses/loads files from any path that is accessible to it.”_

The vulnerability pattern described in this blog stems from a deliberate design decision by Node.js maintainers. While Node.js's position that “applications should trust their filesystem” may hold true on properly administered UNIX systems, it creates a systemic vulnerability on Windows where low-privileged users can write to `C:\node_modules`. Without a fix from Node.js, the burden silently falls on application developers.

Making matters worse, the vulnerable code may not live in the application code itself. The optional dependencies that trigger this behavior could come from third-party libraries buried in the dependency tree as we saw with both Discord and npm CLI.

NPM CLI:

2024-11-13 – ZDI submitted the report to the vendor

2024-11-13 – The vendor acknowledged the receipt of the report

2024-11-13 – The vendor communicated that the reported behavior was by design and they do not consider local attacks as valid security issues

2025-08-05 – ZDI encouraged the vendor to re-assess the issue

2025-12-18 – ZDI notified the vendor of the intention to publish the case as a 0-day advisory

DISCORD:

2025-07-08 – ZDI notified vendor

2025-09-11 – ZDI followed up with vendor

2025-09-15 – Vendor stated they do not consider local attacks as valid security issues

2025-12-01 – ZDI explained why we believe the issue is still valid

2025-12-10 – Vendor replied that the vulnerability is still out of scope

2025-12-11 – ZDI informed vendor of intent to publish 0-day

[[email protected]](mailto:[email protected])

Find us on X

Find us on Mastodon

[[email protected]](mailto:[email protected])

Erwähnte CVEs

Risk Score

38
cvss base
73.00
kev bonus
0.00
epss bonus
0.00
poc bonus
15.00
raw before weight
88.00
industry weight
1.10
freshness factor
0.50
days old
53.00
vendor mismatch penalty
-10.00

Pfad: operational

MITRE ATT&CK Mapping

4 TTPs
Recon
Resource Dev
Initial Access
Execution
Persistence
Cred. Access
Discovery
Lateral Mov.
Collection
C2
Exfiltration
Impact
Conf.: high medium low

Procedure-Details

Technik Tactic Procedure Conf. Quelle
T1574.008
Path Interception by Search Order Hijacking
Privilege Escalation Low-privileged Windows users exploit Node.js module resolution order by creating C:\node_modules and planting malicious packages there, causing Node.js applications to load the attacker-controlled module instead of the legitimate one when the legitimate package is missing or optional high llm
T1574.001
DLL Search Order Hijacking
Defense Evasion Malicious Node.js modules are placed in C:\node_modules to intercept the runtime's module resolution search path, causing applications like Discord to execute attacker-controlled JavaScript code in the context of the application process high llm
T1068
Exploitation for Privilege Escalation
Privilege Escalation CVE-2026-0776 and CVE-2026-0775 are exploited by low-privileged users on Windows to achieve local privilege escalation by hijacking Node.js module resolution in applications such as Discord and npm CLI that run with higher privileges high llm
T1036.005
Match Legitimate Name or Location
Defense Evasion Attacker-controlled malicious packages are named to match legitimate optional or missing dependency package names and placed in C:\node_modules, causing them to be transparently loaded by the target Node.js application without detection medium llm
ESC