Apr 2, 2026·6 min read·3 visits
A prototype pollution bypass in Lodash <= 4.17.23 allows attackers to delete arbitrary properties from global prototypes using array-wrapped path segments in `_.unset` and `_.omit`. The vulnerability is fixed in version 4.18.0.
Lodash versions 4.17.23 and earlier are vulnerable to prototype pollution via a bypass of the previous fix for CVE-2025-13465. By supplying array-wrapped path segments to functions like `_.unset` and `_.omit`, attackers can evade type-checking logic and delete properties from built-in prototypes.
Lodash provides utility functions that manipulate objects and arrays using string-based or array-based object paths. Functions like _.unset and _.omit parse these paths to locate and delete specific object properties recursively. This path evaluation logic historically suffered from prototype pollution vulnerabilities when handling __proto__ or constructor.prototype payloads.
CVE-2026-2950 represents a direct bypass of the mitigation implemented for CVE-2025-13465 within the baseUnset function. The previous patch attempted to block malicious prototype traversal by inspecting the sequence of path segments during property deletion. This remediation relied on the assumption that path traversal keys would always evaluate as string primitives during the security iteration.
The vulnerability exists in Lodash versions up to and including 4.17.23. By leveraging JavaScript type coercion rules against the mitigation logic, an attacker can supply array-wrapped elements to bypass the traversal blocklist. Exploitation leads to prototype pollution (CWE-1321), specifically enabling the deletion of built-in properties from global objects like Object.prototype or String.prototype, which subsequently causes application-wide denial of service or logic failures.
The root cause lies in the type-checking phase of the path traversal loop inside baseUnset.js. In Lodash 4.17.23, the iteration over path segments implemented a safety guard utilizing strict equality against the string type. This check was designed to ignore non-string keys such as Symbols or numbers, inadvertently assuming that structural traversal components would exclusively present as strings.
Lodash path parameters accept mixed arrays, meaning a path array can contain strings, numbers, or nested arrays. When an attacker provides a nested array like ['constructor'] as a path segment, the JavaScript typeof operator evaluates typeof ['constructor'] as 'object'. The mitigation loop encounters this object type and executes the continue statement, immediately skipping the downstream security logic that explicitly checks for the constructor string.
Because the security check is skipped, the underlying Lodash object resolution logic still processes the nested array. Lodash's core property resolution implicitly coerces the array element back into a string during the actual property access phase. This discrepancy between the strict type checking in the security loop and the loose type coercion in the property access routine enables the traversal to reach Object.prototype unimpeded.
The vulnerable implementation in Lodash 4.17.23 isolated its security checks strictly to string types and required an exact constructor.prototype sequence block. The code implemented a typeof check that bypassed validation entirely when the key evaluated to an object type. This rigid sequence detection failed entirely when key was a nested array.
// Vulnerable logic in 4.17.23
if (typeof key !== 'string') {
continue; // Bypasses the checks below if key is an array
}
if (key === 'constructor' && (index + 1) < length && typeof path[index + 1] === 'string' && path[index + 1] === 'prototype') {
return false;
}Commit fe8d32eda854377349a4f922ab7655c8e5df9a0b resolves this by removing the typeof escape hatch and normalizing all keys before evaluation. The fix introduces var key = toKey(path[index]);, which forces type coercion upfront. Consequently, an array like ['constructor'] becomes the string 'constructor' before any security checks occur.
// Patched logic in 4.18.0
var key = toKey(path[index]);
if (key === '__proto__' && !hasOwnProperty.call(object, '__proto__')) {
return false;
}
if ((key === 'constructor' || key === 'prototype') && index < length - 1) {
return false;
}The patch also structurally alters the blocklist logic. Instead of requiring constructor and prototype to appear sequentially, the updated code unconditionally blocks either term if it appears as a non-terminal segment. This prevents any deep object traversal that attempts to pivot through a constructor or prototype reference, closing the vulnerability class effectively.
Exploitation requires an application to pass untrusted user input into the path argument of _.unset or _.omit. The attacker crafts a payload utilizing the nested array structure to evade the 4.17.23 filter. A functional payload takes the form of [['constructor'], 'prototype', 'toString'], where the nested array acts as the bypass mechanism for the first segment.
When the vulnerable _.unset function executes, it iterates over the three segments. The first segment bypasses the security loop due to its object type. The underlying engine resolves the path against the empty object, traversing from {} to Object.prototype. The function then deletes the terminal property, toString, from the global prototype object.
const _ = require('lodash');
const maliciousPath = [['constructor'], 'prototype', 'toString'];
console.log("Before: ", typeof Object.prototype.toString); // "function"
_.unset({}, maliciousPath);
console.log("After: ", typeof Object.prototype.toString); // "undefined"The immediate consequence is the deletion of core JavaScript object properties. Any subsequent application code attempting to call .toString() on standard objects will throw a runtime error. This dynamic guarantees reliable, application-wide denial of service with a single crafted request.
The primary impact of CVE-2026-2950 is a persistent Denial of Service (DoS) state. By deleting fundamental prototype methods like toString, valueOf, or hasOwnProperty, the attacker corrupts the standard JavaScript runtime environment. The application process will crash the moment it encounters standard object manipulation code, requiring a full process restart to restore functionality.
Beyond simple service availability, the deletion of properties introduces subtle application logic failures. If an application relies on specific prototype properties for validation, routing, or access control, the absence of these properties might bypass expected execution branches. While traditional prototype pollution often involves adding properties to escalate privileges or achieve remote code execution, property deletion typically manifests as severe availability and integrity degradation.
The CVSS v3.1 base score of 6.5 reflects the network attack vector, low attack complexity, and the lack of required privileges. The confidentiality impact remains None, but the integrity and availability metrics are Low, representing the localized corruption of the process memory space.
The definitive remediation for CVE-2026-2950 is updating Lodash to version 4.18.0. Software engineers must audit their dependency trees using tools like npm ls lodash or yarn why lodash to identify transitive dependencies relying on vulnerable versions. Updates should be applied via package manager resolutions or direct dependency updates.
In environments where immediate upgrading is technically unfeasible, strict input validation serves as a viable temporary mitigation. Development teams must implement rigid schemas that restrict the path argument of _.unset and _.omit strictly to flat strings. Any path argument containing nested arrays, or strings matching __proto__, constructor, or prototype, must be rejected prior to processing by Lodash.
Security teams can deploy Static Application Security Testing (SAST) rules to identify instances where _.unset and _.omit receive dynamic user input. Tracking dynamic path allocation ensures that untrusted data correctly flows through validation wrappers before interacting with vulnerable utility functions.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
lodash lodash | <= 4.17.23 | 4.18.0 |
lodash-es lodash | <= 4.17.23 | 4.18.0 |
lodash-amd lodash | <= 4.17.23 | 4.18.0 |
lodash.unset lodash | <= 4.17.23 | 4.18.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1321 |
| Attack Vector | Network |
| CVSS Score | 6.5 |
| EPSS Score | 0.00042 |
| Exploit Status | poc |
| KEV Status | Not Listed |
Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')