Client-Side Prototype Pollution in Bug Bounty Reports
JavaScript objects inherit from Object.prototype. If an app merges user-controlled JSON or query keys into an object without blocking __proto__ / constructor.prototype, you can inject properties every object suddenly “has.”
On the server that can mean RCE gadgets. In bug bounty, client-side prototype pollution (CSPP) shows up more often: polluted prototypes flip feature flags, break integrity checks, or feed DOM XSS sinks.
Finding the pollution source
Look for recursive merges: lodash.merge, custom deepExtend, jQuery extend(true, ...), config parsers that accept nested query syntax like ?__proto__[jquery]=....
Client-side URL parsers are fertile ground. Some apps parse the hash or query into a nested object and merge it into config.
Probe with a harmless property:
?__proto__[bugflarePp]=1
Then in the console (on a page you are allowed to debug): ({}).bugflarePp. If you see 1, you polluted the prototype.
Also try constructor[prototype][bugflarePp] when __proto__ is filtered.
From pollution to impact
Pollution alone may be “interesting but incomplete.” Programs want a gadget:
- Libraries that read
Object.prototypeproperties as defaults (XSS sinks,innerHTML, script URLs) - Feature toggles like
isAdmin,debug,bypass - Sanitizer bypasses where a polluted option disables escaping
PortSwigger’s research and gadget catalogs help, but always re-validate on the target’s library versions. A gadget for lodash X may not exist on lodash Y.
Hunting workflow
- Map JS bundles; note merge utilities and query-to-object helpers.
- Confirm pollution with a unique property.
- Search the bundle for property names you can influence (
src,url,html,callback). - Build a minimal PoC URL that triggers XSS or a security-sensitive state change in your browser.
- Record library versions and the exact merge call stack if DevTools allows.
Report without overclaiming
Say what you polluted, which gadget executed, and what a victim must do (usually open a link). If you only have pollution without a sink, label it clearly — some programs still want it for defense-in-depth; many will N/A until chained.
Client-side PP rewards code reading more than payload spraying. Find the merge, prove the prototype flip, then earn the bounty on a real gadget — not on ({}).x === 1 alone.