Prototype Pollution in JavaScript: Source, Sink, Gadgets, and RCE Chains
Cybersecurity
From client-side query string pollution to server-side RCE via lodash, ejs, and Node child_process gadgets. The 2024 prototype pollution playbook.
By Arjun Raghavan, Security & Systems Lead, BIPI · November 25, 2024 · 12 min read
Prototype pollution feels like a curiosity until you find the right gadget. Then a single polluted property turns into XSS on every page, an authorization bypass, or full RCE on a Node server. The class has been studied since 2018 and still ships in fresh libraries each year.
How Pollution Happens
- Recursive merge functions that copy proto keys
- Object.assign with attacker-controlled property names
- Query string parsers that build nested objects from bracketed syntax
- JSON.parse output passed directly to a merge without sanitization
- Setting object key to value where the key is untrusted
Client-Side Pollution Sources
URL query strings are the canonical client-side source. A library that parses proto bracket isAdmin equals true and merges it into config pollutes Object.prototype for every subsequent object. PortSwigger DOM Invader has a dedicated prototype pollution mode that finds these in minutes.
Client-Side Gadgets
- jQuery extend variants that pull options from Object.prototype
- Analytics scripts that build script src from polluted defaults
- AngularJS expression sandboxing escapes via polluted constructor
- Ad scripts and tag managers reading config from prototype
- innerHTML sinks fed by templates with polluted helpers
Server-Side Pollution to RCE
On Node, child_process spawn and exec accept an options object. If you can pollute Object.prototype.shell with a shell path and Object.prototype.env with a payload, a downstream spawn call inherits your values. The 2022 lodash CVEs and many Express middleware bugs followed this exact path.
Template Engine Gadgets
- EJS: pollute outputFunctionName to inject arbitrary code into compiled templates
- Handlebars: pollute helpers map to override safe helpers with attacker functions
- Pug: pollute compileDebug and self to inject expression context
- Lodash template: similar outputFunctionName gadget
Detection Methodology
Send a JSON body with a proto key that sets a marker property, then probe an endpoint that echoes object keys. Burp prototype pollution scanner and the server-side variant from PortSwigger Research cover most cases. Manual testing wins on custom merges.
Prototype pollution is rarely the bug. It is the primitive. The bounty is in the gadget you chain to.
Real CVEs Worth Studying
- CVE-2019-10744 lodash defaultsDeep pollution
- CVE-2020-7598 minimist pollution via argv parsing
- CVE-2021-44906 minimist follow-up
- CVE-2022-25883 semver pollution-adjacent regex
- CVE-2023-26136 tough-cookie prototype pollution
Remediation
- Use Object.create with null prototype for any map that holds untrusted keys
- Freeze Object.prototype at app boot
- Reject proto, constructor, and prototype keys at the parser layer
- Use Map instead of plain objects for dynamic key value stores
- Pin and audit merge libraries; many have shipped multiple bypasses
Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.