Lodash Path Resolution Security Vulnerabilities
Older versions of the Lodash JavaScript library contained critical
security vulnerabilities tied directly to how the library resolved
object paths, allowing attackers to execute Prototype Pollution attacks.
Functions designed to traverse and manipulate nested properties using
string or array paths failed to properly sanitize special object
properties such as __proto__ and
constructor.prototype. This article examines these path
resolution flaws, the affected methods, their potential impact, and how
the Lodash maintainers resolved them across various patch releases.
Understanding Path Resolution in Lodash
Lodash provides helper methods to access and modify deeply nested
object properties without manually verifying the existence of each
intermediate key. Functions such as _.set,
_.setWith, _.defaultsDeep,
_.merge, and _.zipObjectDeep allow developers
to define targets using string paths (such as
"user.profile.name") or array segments (such as
['user', 'profile', 'name']).
When resolving these paths, the utility iterates through each segment, creating intermediate objects if they do not exist, until it reaches and assigns the final value.
Key Path Resolution Vulnerabilities
The primary issue in older versions of Lodash was the absence of strict path key validation during recursive or iterative property assignment.
1. Prototype Pollution via Deep Path Assignment (CVE-2020-8203)
Prior to Lodash version 4.17.19, methods utilizing path
resolution—most notably _.set, _.setWith, and
_.zipObjectDeep—could be tricked into modifying the root
prototype. If user-supplied input contained keys like
__proto__ or constructor.prototype, the path
resolution logic traversed directly into JavaScript's global
Object.prototype.
For example, passing a path like:
_.set({}, '__proto__.polluted', true);or
_.zipObjectDeep(['__proto__.polluted'], [true]);resulted in the property polluted being added to every
plain JavaScript object across the application runtime.
2. Recursive Deep Merging Exploits (CVE-2018-3721 & CVE-2019-10744)
In versions prior to 4.17.11 and 4.17.12, utilities like
_.defaultsDeep and _.merge followed paths
dynamically when combining objects. If an attacker supplied a JSON
payload with a constructor key containing a
prototype object, the recursive path traversal mechanism
failed to treat it as a protected boundary. Instead, it recursively
traversed into the prototype and merged the attacker's properties into
the base JavaScript object.
Impact of the Vulnerabilities
Prototype pollution caused by insecure path resolution can have severe consequences:
- Denial of Service (DoS): Overwriting standard
built-in properties or methods (such as
toStringorvalueOf) causes application exceptions, crashing the Node.js runtime. - Property Injection: Attackers can inject bypass
flags (such as
isAdmin: true) into plain objects evaluated by authorization checks. - Remote Code Execution (RCE): In Node.js environments, manipulating internal properties that are later used in system executions (such as child processes or template compilers) can allow attackers to execute arbitrary shell commands.
How the Vulnerabilities Were Patched
The maintainers addressed path resolution flaws through several layered defenses in versions leading up to 4.17.21:
- Denylisting Dangerous Path Segments: Lodash
introduced checks during path compilation and traversal to identify
reserved keywords. Segments containing
__proto__,constructor, orprototypeare ignored or explicitly blocked during deep assignment operations. - Safe Property Assignment: Methods that create or assign intermediate objects now avoid assigning values if the current target is an object prototype, preventing traversal from escaping the boundary of the local target object.
- Safe Path Tokenization: Path parsing logic was hardened to prevent obfuscated or malicious array indices and nested keys from bypassing standard keyword filters.