From ca08f801cf71a258a204c40dcb71db17465293b7 Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Wed, 8 Apr 2026 19:30:35 +0000 Subject: [PATCH] Add content from: Node.js Trust Falls: Dangerous Module Resolution on Windows --- .../README.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/windows-hardening/windows-local-privilege-escalation/README.md b/src/windows-hardening/windows-local-privilege-escalation/README.md index 878b781024a..84ab0f9ead1 100644 --- a/src/windows-hardening/windows-local-privilege-escalation/README.md +++ b/src/windows-hardening/windows-local-privilege-escalation/README.md @@ -887,6 +887,72 @@ For more information about how to abuse this check: dll-hijacking/writable-sys-path-dll-hijacking-privesc.md {{#endref}} +## Node.js / Electron module resolution hijacking via `C:\node_modules` + +This is a **Windows uncontrolled search path** variant that affects **Node.js** and **Electron** applications when they perform a bare import such as `require("foo")` and the expected module is **missing**. + +Node resolves packages by walking up the directory tree and checking `node_modules` folders on each parent. On Windows, that walk can reach the drive root, so an application launched from `C:\Users\Administrator\project\app.js` may end up probing: + +1. `C:\Users\Administrator\project\node_modules\foo` +2. `C:\Users\Administrator\node_modules\foo` +3. `C:\Users\node_modules\foo` +4. `C:\node_modules\foo` + +If a **low-privileged user** can create `C:\node_modules`, they can plant a malicious `foo.js` (or package folder) and wait for a **higher-privileged Node/Electron process** to resolve the missing dependency. The payload executes in the security context of the victim process, so this becomes **LPE** whenever the target runs as an administrator, from an elevated scheduled task/service wrapper, or from an auto-started privileged desktop app. + +This is especially common when: + +- a dependency is declared in `optionalDependencies` +- a third-party library wraps `require("foo")` in `try/catch` and continues on failure +- a package was removed from production builds, omitted during packaging, or failed to install +- the vulnerable `require()` lives deep inside the dependency tree instead of in the main application code + +### Hunting vulnerable targets + +Use **Procmon** to prove the resolution path: + +- Filter by `Process Name` = target executable (`node.exe`, the Electron app EXE, or the wrapper process) +- Filter by `Path` `contains` `node_modules` +- Focus on `NAME NOT FOUND` and the final successful open under `C:\node_modules` + +Useful code-review patterns in unpacked `.asar` files or application sources: + +```bash +rg -n 'require\\("[^./]' . +rg -n "require\\('[^./]" . +rg -n 'optionalDependencies' . +rg -n 'try[[:space:]]*\\{[[:space:][:print:]]*require\\(' . +``` + +### Exploitation + +1. Identify the **missing package name** from Procmon or source review. +2. Create the root lookup directory if it does not already exist: + +```powershell +mkdir C:\node_modules +``` + +3. Drop a module with the exact expected name: + +```javascript +// C:\node_modules\foo.js +require("child_process").exec("calc.exe") +module.exports = {} +``` + +4. Trigger the victim application. If the application attempts `require("foo")` and the legitimate module is absent, Node may load `C:\node_modules\foo.js`. + +Real-world examples of missing optional modules that fit this pattern include `bluebird` and `utf-8-validate`, but the **technique** is the reusable part: find any **missing bare import** that a privileged Windows Node/Electron process will resolve. + +### Detection and hardening ideas + +- Alert when a user creates `C:\node_modules` or writes new `.js` files/packages there. +- Hunt for high-integrity processes reading from `C:\node_modules\*`. +- Package all runtime dependencies in production and audit `optionalDependencies` usage. +- Review third-party code for silent `try { require("...") } catch {}` patterns. +- Disable optional probes when the library supports it (for example, some `ws` deployments can avoid the legacy `utf-8-validate` probe with `WS_NO_UTF_8_VALIDATE=1`). + ## Network ### Shares @@ -2022,5 +2088,9 @@ C:\Windows\microsoft.net\framework\v4.0.30319\MSBuild.exe -version #Compile the - [A Link to the Past. Abusing Symbolic Links on Windows](https://infocon.org/cons/SyScan/SyScan%202015%20Singapore/SyScan%202015%20Singapore%20presentations/SyScan15%20James%20Forshaw%20-%20A%20Link%20to%20the%20Past.pdf) - [RIP RegPwn – MDSec](https://www.mdsec.co.uk/2026/03/rip-regpwn/) - [RegPwn BOF (Cobalt Strike BOF port)](https://github.com/Flangvik/RegPwnBOF) +- [ZDI - Node.js Trust Falls: Dangerous Module Resolution on Windows](https://www.thezdi.com/blog/2026/4/8/nodejs-trust-falls-dangerous-module-resolution-on-windows) +- [Node.js modules: loading from `node_modules` folders](https://nodejs.org/api/modules.html#loading-from-node_modules-folders) +- [npm package.json: `optionalDependencies`](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#optionaldependencies) +- [Process Monitor (Procmon)](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon) {{#include ../../banners/hacktricks-training.md}}