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:

How the Vulnerabilities Were Patched

The maintainers addressed path resolution flaws through several layered defenses in versions leading up to 4.17.21:

  1. Denylisting Dangerous Path Segments: Lodash introduced checks during path compilation and traversal to identify reserved keywords. Segments containing __proto__, constructor, or prototype are ignored or explicitly blocked during deep assignment operations.
  2. 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.
  3. Safe Path Tokenization: Path parsing logic was hardened to prevent obfuscated or malicious array indices and nested keys from bypassing standard keyword filters.