CVE-2024-22019 / Sandbox Escape

CVE-2024-22019: Node.js Permission Model Filesystem Escape Teardown

How accessing low-level C++ internal bindings via process.binding('fs') bypassed the experimental --permission --allow-fs-read flags.

💡 通俗通俗化解析 (ELI5)

Imagine a high-security office building with security guards at the front doors checking badges. A worker who doesn't have a badge walks around back, opens the maintenance door marked 'Internal Staff Only', and walks straight into the vault. In Node.js, the developer guarded the public JavaScript functions, but forgot to lock the underlying C++ internal door.

核心概念与底层架构术语

Node.js Permission Model
The experimental `--permission` CLI flag restricting process capabilities like filesystem, child process, and worker access.
process.binding()
The legacy internal Node.js API that directly exposes native C++ bindings to JavaScript code.
Path Containment
Ensuring file operations remain strictly inside specified directory boundaries.
Defense-in-Depth
Enforcing security checks at the lowest possible layer (native C++ layer) rather than superficial JS wrappers.

攻击利用全流程逐步拆解

Step 1

1. Start Sandboxed Node.js Process

A developer runs untrusted code with node --permission --allow-fs-read=/tmp app.js.

Step 2

2. Access Internal Binding

The script invokes const binding = process.binding('fs');.

Step 3

3. Bypass High-Level JS Checks

The script calls binding.open('/etc/passwd') directly, bypassing fs.readFile() wrappers.

Step 4

4. Arbitrary System File Read

The native C++ binding executes without permission checks, leaking confidential server files.

源码对比:致命缺陷 vs. 加固补丁

采用清晰易读的高级编程语言展示(不含晦涩汇编或二进制机器码)。

未修补缺陷代码
// VULNERABLE: src/node_file.cc before patch
static void Open(const FunctionCallbackInfo<Value>& args) {
    Environment* env = Environment::GetCurrent(args);
    
    // ROOT CAUSE:
    // Only verified permissions if called through JS 'fs' module wrappers!
    // Direct callers of process.binding('fs').open() evaded the check!
    const char* path = *Utf8Value(env->isolate(), args[0]);
    int fd = uv_fs_open(..., path, ...);
    args.GetReturnValue().Set(fd);
}
安全加固补丁
// SECURE: src/node_file.cc patch
static void Open(const FunctionCallbackInfo<Value>& args) {
    Environment* env = Environment::GetCurrent(args);
    
    const char* path = *Utf8Value(env->isolate(), args[0]);
    
    // 1. Enforce permission verification directly inside native C++ binding layer
    if (env->permission()->is_enabled()) {
        if (!env->permission()->is_granted(PermissionScope::kFileSystemRead, path)) {
            THROW_ERR_ACCESS_DENIED(env, "Access to path %s denied by Permission Model", path);
            return;
        }
    }
    
    int fd = uv_fs_open(..., path, ...);
    args.GetReturnValue().Set(fd);
}

工程落地与系统加固清单

← 浏览完整安全目录 所有平台安全更新 →