Aug 22, 2026·6 min read·24 visits
Unauthenticated remote code execution vulnerability in JSONata via prototype lookup chain traversal.
A critical prototype pollution and sandbox escape vulnerability was discovered in the JSONata query and transformation library before versions 1.8.8 and 2.2.0. By providing a malicious JSONata expression that bypasses ownership checks on object properties, remote attackers can execute arbitrary code in the context of the host Node.js application.
JSONata is a query and transformation language modeled on XPath that interprets queries and transformations against JSON input datasets. To evaluate expressions, JSONata compiles them into an Abstract Syntax Tree (AST) and steps through the nodes using its core evaluator engine. The engine acts as an interpreter, allowing users to define local variables, query specific fields, and invoke transformation operations. This flexibility requires strict sandbox boundaries when evaluating untrusted user queries.\n\nThe attack surface lies directly in the core evaluator's property resolution bottleneck. In standard Node.js environments, applications often evaluate query strings provided by external users or integrations. If these expressions are executed without safety boundaries, an attacker can access properties outside the scope of the local evaluation context. This behavior occurs because the evaluator performs raw property lookups on user-provided and internal JavaScript objects.\n\nThe vulnerability is classified under CWE-94 (Improper Control of Generation of Code or 'Code Injection'). Specifically, the engine's failure to validate object property ownership allows remote attackers to traverse the standard JavaScript prototype chain. By retrieving the global Function constructor, an adversary can compile and execute arbitrary JavaScript code inside the host process. This bypass leads to complete compromise of the hosting system's confidentiality, integrity, and availability.
The root cause of the vulnerability exists in JSONata's internal property resolution helper function, located in src/functions.js. During query evaluation, the engine resolves property keys using a central lookup routing function. This function takes an input object and a target key, and then returns the resolved value.\n\nIn JavaScript, all standard object instances inherit helper methods and meta-properties from Object.prototype. These properties include constructor, __proto__, __lookupSetter__, and __defineGetter__. When the lookup resolver evaluates an expression, it retrieves properties by directly indexing the object, e.g., using input[key]. Before the fix, the evaluator checked whether the input was an object and not a function, but it did not verify whether the key belonged to the object as an 'own' property.\n\nBecause of this omission, if a query references an inherited property such as constructor, the engine climbs up the prototype chain. The engine then returns the native prototype function or object to the evaluator scope. An attacker can construct a payload that chains these lookups. This chain moves from a standard object to the prototype accessor, and ultimately retrieves the global Function constructor. Once retrieved, the constructor serves as an execution vector to run arbitrary shell commands with the host process privileges.
The vulnerability was resolved by introducing explicit property ownership validation in the central resolver in src/functions.js. Below is the code diff of the patch implemented in both the v1 and v2 branches:\n\njavascript\n// Vulnerable Property Lookup\n} else if (input !== null && typeof input === 'object' && !isFunction(input)) {\n result = input[key];\n}\nreturn result;\n\n// Patched Property Lookup\n} else if (input !== null && typeof input === 'object' && Object.prototype.hasOwnProperty.call(input, key) && !isFunction(input)) {\n result = input[key];\n}\nreturn result;\n\n\nThe inclusion of Object.prototype.hasOwnProperty.call(input, key) ensures that the engine only returns values defined directly on the input instance. If the key exists on the prototype chain (such as constructor), the check fails. The resolver then returns undefined instead of returning the inherited function.\n\nInvoking hasOwnProperty indirectly via Object.prototype.hasOwnProperty.call is critical for application stability. Objects created without a prototype, such as Object.create(null), do not inherit methods from Object.prototype. Checking them using input.hasOwnProperty(key) would throw a TypeError, causing the application to crash. The indirect call avoids this issue, ensuring safety across all object types.\n\nAdditionally, supplementary hardening was introduced in PR #806. All internal maps, binding contexts, and regex lookup tables were refactored to use prototype-less objects created with Object.create(null). The maintainers also replaced for...in loops, which naturally traverse prototype properties, with direct Object.keys() iterations. Finally, they blacklisted internal variable prefixes like _jsonata_ to prevent injection vectors.
Exploitation is achieved by constructing a specific JSONata query that traverses the prototype chain to execute system commands. The sequence uses JavaScript's capability to override or access setters and getters on standard objects.\n\nFirst, the attacker executes __lookupSetter__('__proto__')(constructor) inside the query. This retrieves a reference to the global Function constructor from the prototype chain. Next, the attacker invokes this constructor to compile a dynamic shell payload. The payload retrieves the Node.js child_process module to run commands:\n\njavascript\nconstructor(\"return process.getBuiltinModule('child_process').execSync('id').toString()\")\n\n\nThe compiled payload is then bound as a getter named 'l' on the prototype of standard objects using __defineGetter__('l', ...). Finally, evaluating valueOf().l triggers the getter. This executes the system command within the context of the host process, returning the command output directly to the attacker.\n\nThis attack is highly operationalizable because it requires no specialized local environment variables, user authentication, or specific target configurations. The only prerequisite is that the host application must evaluate user-controlled JSONata expressions using standard runtime contexts. The execution flow can be visualized using the following flow chart:\n\nmermaid\ngraph LR\n A[\"Malicious JSONata Expression\"] --> B[\"Evaluate Expression\"]\n B --> C[\"Call __lookupSetter__('__proto__')(constructor)\"]\n C --> D[\"Access Global Function Constructor\"]\n D --> E[\"Bind Custom Getter 'l' with child_process Payload\"]\n E --> F[\"Trigger valueOf().l\"]\n F --> G[\"Execute Shell Command via host Node.js process\"]\n
The potential impact of CVE-2026-77413 is high. Because the JSONata evaluation runs directly within the Node.js runtime process, a successful escape gives the attacker full control of the hosting application. The attacker can execute arbitrary operating system commands, read sensitive system files, access environment variables, or establish persistent backdoors.\n\nThis vulnerability has been assigned a CVSS v4.0 Base Score of 9.3 (Critical). The CVSS vector is CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. This score reflects that no prior authentication, user interaction, or specialized network conditions are required to trigger execution.\n\nFurthermore, the blast radius is extended because JSONata is commonly deployed in low-code integration platforms, database transformation pipelines, and API gateways. In these environments, applications often evaluate expressions supplied by end-users or third-party webhooks. A compromise in these locations can lead to lateral movement, data exfiltration, or access to cloud metadata services.
The primary remediation strategy is upgrading the JSONata library to a secure version. For systems on the v1.x release branch, administrators must upgrade to version 1.8.8 or higher. For systems on the v2.x release branch, administrators must upgrade to version 2.2.0 or higher.\n\nIn addition to upgrading, developers should implement execution guardrails introduced in version 2.2.0. These guardrails restrict runtime resources, helping prevent denial-of-service vectors like exponential backtracking or infinite loops. Below is an example of configuring these safety options:\n\njavascript\nimport jsonata from 'jsonata';\n\nconst options = {\n stack: 500, // Recursion limit\n timeout: 1000, // 1-second timeout\n sequence: 10000 // Intermediate allocation limit\n};\n\nconst expression = jsonata(userInput, options);\nconst result = await expression.evaluate(data);\n\n\nIf immediate patching is not possible, applications should block any JSONata expressions containing keywords linked to prototype access. These keywords include __proto__, constructor, prototype, __lookupSetter__, and __defineGetter__. However, keyword blocking is prone to bypasses and should only be used as a temporary workaround until the package is updated.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 |
| Attack Vector | Network |
| CVSS Score | 9.3 (Critical) |
| EPSS Score | N/A |
| Impact | Unauthenticated Remote Code Execution |
| Exploit Status | Proof of Concept (PoC) documented |
| KEV Status | Not Listed |
The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes the input before it is executed.
Payload CMS, a popular open-source headless Content Management System, contains a critical Regular Expression Denial of Service (ReDoS) and uncontrolled resource consumption vulnerability in versions prior to 3.90.0 and canary versions prior to 4.0.0-canary.34. Due to nested quantifiers in the multipart boundary regex validation pattern, and the absence of streaming backpressure controls, remote attackers can trigger catastrophic backtracking and memory exhaustion. This blocks the single-threaded Node.js event loop, resulting in a persistent and complete Denial of Service (DoS).
An Improper Access Control vulnerability (CWE-284) in Payload CMS prior to version 3.90.0 and 4.0.0-canary.34 allows authenticated, low-privileged users to bypass field-level access control restrictions and overwrite the password of other accounts, leading to complete account takeover and privilege escalation.
Payload CMS was discovered to use an insecure default configuration for its password-hashing mechanism. The system requested a 512-byte key from PBKDF2-HMAC-SHA256 with 25,000 iterations, creating a severe cryptographic asymmetry. While the defending server sequentially computed 16 blocks of key material (equivalent to 400,000 internal iterations), an offline attacker only needed to compute the first 32-byte block to verify password guesses. This allowed offline attackers to crack stolen database hashes 16 times faster than intended by the security design.
VectorFreed identifies a critical Use-After-Free (UAF) memory corruption vulnerability in librsvg (CVE-2026-96889), which manifests when parsing structured SVG documents containing nested XML inclusions (XIncludes) and duplicate entity declarations. The flaw results from an entity ownership conflict where librsvg prematurely deallocates an xmlEntity structure still actively referenced by the underlying libxml2 parser context. When transitively compiled into downstream applications such as the high-performance sharp image processing library, this vulnerability facilitates denial of service and unauthenticated remote code execution on the host operating system.
CVE-2026-102275 (GHSA-x33g-cr3x-6449) is a public/private key identity confusion vulnerability in PyJWT versions 2.1.0 through 2.14.0. When importing Octet Key Pair (OKP) JSON Web Keys (JWKs) representing Ed25519 or Ed448 curves, PyJWT fails to verify that the public parameter 'x' matches the private parameter 'd'. An attacker can construct a hybrid JWK combining a victim's public key with the attacker's private key. In protocols like DPoP that bind sessions via public key thumbprints, this allows the attacker to authenticate as the victim while signing proofs with their own private key, fully bypassing sender-constrained security guarantees.
An authentication bypass and privilege escalation vulnerability exists in Filament (filamentphp/filament) due to missing password verification during multi-factor authentication (MFA) setup and management. An attacker with access to an active session can modify or disable MFA, leading to account hijacking.