_Node.js version 20 introduced an experimental permission model to help restrict what code can do—such as reading files—using flags like --allow-fs-read. However, a newly reported vulnerability, CVE-2023-30582, shows that these controls aren't as tight as they should be. Specifically, when the --allow-fs-read flag is set with specific (non-wildcard) paths, attackers may still use the fs.watchFile API to monitor changes to *any* file, even ones for which they don’t have explicit read permissions._
This post gives an exclusive deep dive into how this happens, with code samples and steps that show just how attackers might abuse this loophole.
What Is the Permission Model?
Node.js, by default, gives your code broad access to the file system, environment variables, and more. To improve security, Node.js 20 introduced an experimental permission model: a way to limit what code can access. For instance, you can say: “Only allow this script to read from /tmp and nowhere else,” like this:
node --experimental-permission --allow-fs-read=/tmp my-script.js
The intention? Stop untrusted code from poking around where it shouldn’t. But, as with all new features, bugs can creep in…
What goes wrong?
If you use --allow-fs-read to only allow some files (but not all, i.e., not --allow-fs-read=*), Node.js should prevent your script from interacting with files outside this list.
But—due to CVE-2023-30582—fs.watchFile wasn’t properly checked. That means even if you don’t have read permission for /etc/passwd, you can still monitor it for changes!
Suppose you launch Node.js like so
node --experimental-permission --allow-fs-read=allowed.txt watcher.js
You expect watcher.js to only read allowed.txt. But using fs.watchFile, it can still watch changes in files like secret.txt or even system files.
Exploitation: How Attackers Can Monitor Off-limits Files
An attacker who wants to see if a sensitive file (like /etc/passwd or some config) changes, could use this trick—even if they don’t have permission to *read* the content.
Here’s a Code Snippet Demonstrating the Flaw
// watcher.js
const fs = require('fs');
const target = '/etc/passwd'; // Replace with any file not in --allow-fs-read
fs.watchFile(target, (curr, prev) => {
console.log(${target} changed!);
console.log('Previous mtime:', prev.mtime);
console.log('Current mtime:', curr.mtime);
});
Launched as
node --experimental-permission --allow-fs-read=./allowed.txt watcher.js
Expected: Script should error out, since /etc/passwd isn’t allowed.
Actual: Script _succeeds_—you can track when /etc/passwd changes, without reading the content.
You get notified _whenever_ a file changes.
- You see the old and new timestamps—potentially useful for attackers tracking admin activity or system state.
Why is that a problem?
Even if you can’t see what's inside, monitoring sensitive files could aid targeted attacks, timing side-channels, or simply snoop on operational patterns.
Why Did It Happen?
The Node.js permission model checks common file APIs (like fs.readFile) but did not properly check permission on the lower-level file watching functions (fs.watchFile). So, even with restricted flags, the permissions were effectively bypassed by this API.
Has It Been Fixed?
Yes – patches were published and the issue has been tracked upstream. All users of the experimental permission model are advised to upgrade.
- Node.js security release blog post
- CVE Record at NVD
- Node.js GitHub Advisory
Note: If you are not using the --experimental-permission model, this does *not* affect you.
Upgrade to the latest Node.js 20+ release. The bug was fixed soon after it was reported.
- Understand the permission model is still experimental. Don’t rely solely on this model if you need strict security boundaries!
Avoid running untrusted code, even with permission flags.
- Follow Node.js security releases: Official Security Reports.
Conclusion: Permission Models Are Hard
CVE-2023-30582 is a useful reminder that security is complex, and “experimental” features should always be used with that in mind. The Node.js team patched this bug quickly, but if you ever use --experimental-permission, make sure you upgrade.
Until this feature matures, keep experimental features away from production and remain cautious running untrusted code.
More References
- Node.js 20.2. Release Notes
- Bug report and patch discussion
Timeline
Published on: 09/07/2024 16:15:02 UTC
Last modified on: 09/09/2024 19:35:01 UTC