flawopen.com/Teardowns/cve-2023-32558-nodejs-permission-model-fs-escape

● CVE-2023-32558 · CVSS 7.5 · Tinggi
Riset Keamanan · FlawOpen

CVE-2023-32558: Node.js Permission Model Filesystem Escape Teardown

Analisis teknis mendalam dan mitigasi rekayasa sistem untuk vulnerabilidade: How accessing low-level C++ internal bindings via process.binding('fs') bypassed the experimental --permission --allow-fs-read flags.

💡 Penjelasan Sederhana (ELI5)

Analogi dunia nyata: 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.

Konsep Kunci & Istilah

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.

Analisis Akar Masalah (Root Cause)

Akar masalah bermula dari parameter batas yang tidak divalidasi pada sistem open source, yang memungkinkan desinkronisasi status dan bypass kontrol keamanan.

Alur Serangan Langkah demi Langkah

Step 1

Tahap serangan: Start Sandboxed Node.js Process

Mekanisme eksploitasi teknis dan detail eksekusi: A developer runs untrusted code with node --permission --allow-fs-read=/tmp app.js.

Step 2

Tahap serangan: Access Internal Binding

Mekanisme eksploitasi teknis dan detail eksekusi: The script invokes const binding = process.binding('fs');.

Step 3

Tahap serangan: Bypass High-Level JS Checks

Mekanisme eksploitasi teknis dan detail eksekusi: The script calls binding.open('/etc/passwd') directly, bypassing fs.readFile() wrappers.

Step 4

Tahap serangan: Arbitrary System File Read

Mekanisme eksploitasi teknis dan detail eksekusi: The native C++ binding executes without permission checks, leaking confidential server files.

Kode Sumber: Rentan vs Aman

IMPLEMENTASI RENTAN
// 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);
}
PERBAIKAN AMAN & KUAT
// 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);
}

Daftar Periksa Penguatan Sistem Rekayasa

Sumber