Sep 4, 2026·6 min read·50 visits
Improper key verification and unrestricted recursive object traversal in toml-node's compiler logic allow remote attackers to poison Object.prototype via crafted TOML input, enabling prototype pollution.
A prototype pollution vulnerability exists in the toml-node library (by BinaryMuse) in versions prior to 4.1.2. The flaw arises from inconsistent internal tracking of parsed paths (comma-joined vs. dot-joined serialization) combined with lack of object ownership validation during recursive dictionary descent (scalar descent). This allows unauthenticated remote attackers to modify base object structures by crafting malicious TOML documents containing conflicting duplicate table paths or nested references.
The toml-node library is a standard package used to parse TOML configuration data in Node.js environments. The core vulnerability is categorized under CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes, or 'Prototype Pollution'). By feeding a specifically structured input to toml.parse(), an attacker can bypass duplicate-key detection mechanisms and force the engine to write properties onto the global Object.prototype dictionary.
In standard applications, configuration files are often treated as trusted inputs. However, in scenarios where users can supply configurations dynamically, such as API configurations, dashboard layouts, or serverless configuration payloads, this parser vulnerability opens up a significant unauthenticated remote execution attack vector.
Because most JavaScript objects inherit directly from Object.prototype, any successful injection of key-value pairs becomes immediately visible to all objects across the active Node.js application process. This global state mutation can lead to downstream property injection attacks, process crashes, or arbitrary command execution depending on the existence of vulnerable gadget paths within other loaded dependencies.
The compiler within lib/compiler.js tracks traversed table pathways to ensure they are not declared more than once, as dictated by the TOML specification. Two main data structures are tasked with this state tracking: assignedPaths and valueAssignments. When evaluating and enforcing duplicate boundaries, the library relies on exact string matching of serialized path segments.
However, a path serialization desynchronization occurs because the engine records values inside the track sets using inconsistent formats. Value assignments generate implicit or comma-separated representations such as a,b.y, while the internal lookup function deepRef() formats keys using a standard dot-separated representation like a.b.y. Because of this structural mismatch, validation queries against the tracking sets fail to find existing records, silently bypassing duplicate-key detection checks.
Furthermore, when a table definition specifies a nested path (for instance, [a.b.y.__proto__.__proto__]), the loop resolves the prefix a.b.y. If a.b.y was previously defined as a scalar value (like a Number 1), resolving the field __proto__ returns the native prototype of that scalar (e.g., Number.prototype). The subsequent resolution of __proto__ accesses Object.prototype, where the compiler then writes arbitrary properties without verifying ownership of the destination dictionary. This behavior is termed Scalar Descent.
The patch implemented in version 4.1.2 mitigates this flaw by introducing a strict ownership mechanism and unifying path serialization helpers. Below is a structured analysis of the code-level modifications in lib/compiler.js.
// BEFORE THE PATCH:
var currentPath = "";
// AFTER THE PATCH:
var currentPath = [];
var ownedContainers = new WeakSet();
var data = createTable();By tracking the current path as an array rather than an inline flat string, the compiler avoids the serialization desynchronization. The addition of the ownedContainers WeakSet provides a robust mechanism to differentiate between compiled TOML dictionaries and native JavaScript prototypes.
During traversal inside deepRef(), the patched code enforces strict object-ownership checks:
// Post-patch recursive lookup safety check:
if (i < keys.length - 1) {
if (!isOwnedContainer(ctx)) {
genError("Cannot redefine existing key '" + traversedPath + "'.", off);
}
if (ctx instanceof Array) {
if (!ctx.length) {
genError("Cannot redefine existing key '" + traversedPath + "'.", off);
}
ctx = ctx[ctx.length - 1];
if (!isOwnedContainer(ctx)) {
genError("Cannot redefine existing key '" + traversedPath + "'.", off);
}
}
}These modifications successfully halt the traversal process if the parser descends into any object reference that is not explicitly registered within the ownedContainers set. Any attempt to access native prototypes will trigger a validation error, preventing modifications to parent object structures.
An attacker can exploit this flaw through two primary vectors. The first vector is through direct scalar descent, where a scalar value is declared and subsequently leveraged to reference ancestral prototypes. The second vector utilizes table arrays to bypass the parser's nesting constraints.
Consider the scalar descent exploit vector below. The payload first instantiates a scalar key y containing an integer, then immediately redefines a table using that key to walk back through the prototype chains:
[a.b]
y = 1
[a.b.y.__proto__.__proto__]
polluted = "yes"When toml.parse() processes this payload, it creates the scalar y. Upon processing the subsequent table definition, deepRef() crawls into a.b.y, accesses (1).__proto__ to reach Number.prototype, and crawls again to reach Object.prototype. Finally, it sets Object.prototype.polluted = "yes", executing a successful system-wide modification.
The impact of prototype pollution in Node.js depends heavily on other active libraries and application logic. If downstream code uses uninitialized objects or conducts unsafe merges of configuration hashes, an attacker can manipulate program flow control.
For example, if the application invokes subprocesses via child_process.spawn or child_process.fork, the runtime looks up properties on the options parameter. An attacker who has successfully polluted properties such as shell or env can force the application to execute arbitrary binary payloads. This transitions prototype pollution from a passive property injection directly into an unauthenticated remote execution scenario.
Additionally, polluting properties like toString, valueOf, or basic loop attributes can disrupt standard system functionality, leading to persistent denial of service (DoS) conditions where the entire Node.js server crashes upon loading.
The recommended approach to address this vulnerability is upgrading toml-node to version 4.1.2 or later. This version contains the complete rewrite of the path serialization engine and introduces runtime WeakSet ownership validation.
In scenarios where dependency upgrades are blocked, application operators can use CLI flags to secure the runtime environment. Running Node.js with the --disable-proto flag disables the __proto__ property completely across all native contexts:
node --disable-proto=delete app.jsAlternatively, developers can execute Object.freeze(Object.prototype) inside the main application entry point. This locks down the base dictionary against runtime modification, though it may trigger errors in legacy libraries that dynamically extend global prototypes. A mitigation function can also be used to filter incoming configuration streams for dangerous object keywords like __proto__ and constructor before forwarding the payload to the parser.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
toml-node BinaryMuse | < 4.1.2 | 4.1.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1321 |
| Attack Vector | Network (Unauthenticated) |
| CVSS v3.1 Score | 8.2 (High) |
| Exploit Status | Proof of Concept (PoC) available |
| KEV Status | Not Listed |
| EPSS Score | 0.00383 |
| Impact Category | Integrity (High), Availability (Low) |
The software receives input from an upstream component, but does not neutralize or incorrectly neutralizes special elements that could modify the system-wide prototype structure, enabling attackers to inject properties that alter base object structures.
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.