Understanding Prototype Pollution

SangharshaSangharsha
6 min read

Intro Hook:
Imagine a village with family traditions, town archives, and public notice boards. Now, imagine a stranger sneaks in and subtly changes these traditions. Suddenly, rules that everyone depends on are corrupted. This is exactly what prototype pollution does in JavaScript: a hidden attacker changes the default “rules” of every object in the system, creating security risks. Let’s break it down in plain analogies.

In short :

- Prototype = Family tradition
- Pollution = Malicious new tradition
- Source = Door stranger used
- Sink = Where tradition influences decisions
- Gadget = Specific practice that turns tradition into real power
- Client-side = Public notice board corrupted
- Server-side = Town archive corrupted

1. JavaScript Prototypes & Inheritance → “Family Traditions”

Imagine a family.

  • The grandparent teaches “every child in this family can sing.”

  • Every child automatically inherits that ability, even if they didn’t learn it directly.
    That’s what Object.prototype is. Every object inherits from it.

2. Prototype Pollution → “Bad Tradition Injection”

Now imagine a malicious stranger sneaks in and says:

  • “From now on, every child in this family lies by default.”
    Suddenly, every child inherits that new “tradition.”
    That’s pollution: attacker modifies the shared ancestor, and everyone down the chain is corrupted.

3. Sources → “How the Stranger Enters”

The intruder needs a door:

  • Front door = query params (?__proto__=bad).

  • Back door = JSON body ({"__proto__":{}}).

  • Window = URL fragment (#attack).
    Each is just an entry point for injecting the bad tradition.


4. Sinks → “Where the Damage Shows”

Not every new tradition matters.
But if a polluted tradition affects something important (like honesty during voting), damage happens.

  • Sink = point in the system where the poisoned value gets used.

5. Gadgets → “How to Weaponize the Tradition”

A gadget is a cultural practice that can be hijacked.
Example:

  • Tradition: “Before signing a contract, everyone must tell their family motto.”

  • Attacker polluted the motto → now every contract gets signed with the wrong words.
    In code: if toString() is poisoned, anything that calls .toString() will run attacker code.


6. Client-Side PP → “Messing With the Village Notice Board”

On the browser, pollution = attacker changes defaults used in rendering the UI.
Impact: fake content, redirecting links, or injecting script → like rewriting public notices.


7. Server-Side PP → “Corrupting the Town’s Archive”

On the backend/server, pollution = attacker alters global settings of the archive.
Impact: every new law, every decision is poisoned → bypass rules, maybe take full control.


8. Browser APIs → “Town Services That Trust Outsiders”

Think of postman delivering letters (browser APIs).
If they don’t check envelopes properly, strangers can slip in poisoned “family rules” and the town accepts them.


9. Full Exploitation Chain Example (Story + Payload)

Story Analogy:

  • Stranger sneaks into a town meeting (source).

  • He convinces the mayor’s secretary that the family motto is now “Give me the treasury key” (pollution).

  • Later, when the mayor signs documents (sink), he must use the motto.

  • Because it’s polluted, the treasury key gets handed over (gadget/exploit).

Technical Reality:

  1. Source – Attacker injects:

     https://target.com/?__proto__[isAdmin]=true
    
  2. PollutionObject.prototype.isAdmin = true

  3. Sink – Some code checks:

     if (user.isAdmin) { giveAccess(); }
    
  4. Gadget – Because every user now “inherits” isAdmin=true, attacker bypasses auth.

Result: attacker becomes admin without valid credentials.


TL;DR Mapping

  • Prototype = Family tradition

  • Pollution = Malicious new tradition

  • Source = Door stranger used

  • Sink = Where tradition influences decisions

  • Gadget = Specific practice that turns tradition into real power

  • Client-side = Public notice board corrupted

  • Server-side = Town archive corrupted


A cheat sheet is below for prototype pollution concepts to get quick overview and to revise quickly

JS Term / ConceptAnalogy (Non-Tech World)What Happens / Why It MattersQuick Example
Object.prototypeFamily traditions / rules passed down from grandparentsEvery object (“child”) automatically inherits these traitstoString() exists on all objects
Prototype PollutionA malicious new tradition injected into the familyEvery “child” now follows the attacker’s ruleAll objects now have isAdmin = true
SourceDoor/window where the stranger entersWhere the attacker injects their malicious rule?__proto__[isAdmin]=true in URL
SinkWhere the new tradition actually affects decisionsPollution only matters if it reaches a critical pointif(user.isAdmin) { giveAccess() }
GadgetA specific cultural practice that can be hijacked for powerTurns the polluted rule into a real exploit.toString() called on an object runs attacker code
Client-side PPPublic notice board / village bulletinAffects what everyone in the village sees / interacts withUI renders polluted values → fake content
Server-side PPTown archive / government recordsCorrupts rules globally → bypasses permissions / controlsNode.js merges user input into global settings
Browser APITown services (postmen, messengers) that blindly trust outsidersAttacker can slip in pollution via legit channelsURLSearchParams, postMessage handling unvalidated input
Query ParamsFront doorUser-controlled input in URLs?theme=dark
Hash FragmentWindow / secret note passed to town notice boardAnother way to feed user input into app state#/profile?id=123
Merge Function (Object.assign, _.merge)Town clerk combining family rules with user instructionsWhere pollution actually changes the default behaviorObject.assign(defaults, userInput)
PayloadThe specific “bad tradition” / instruction attacker injectsThe concrete data that corrupts defaults{"__proto__":{"polluted":"yes"}}

Common built-in prototypes and custom objects

1. Object.prototype → “Grandparent’s Rules”

  • Root for most objects. Pollution here affects almost everything.

  • Analogy: The grandparent sets family rules everyone inherits.

  • Example: If the grandparent suddenly declares “all kids must wear red hats,” every child and even distant relatives follow it.


2. Array.prototype → “School Club Rules”

  • Pollution affects arrays ([].push, [].map) and objects inheriting from arrays.

  • Analogy: A school club has its own rules in addition to family rules.

  • Example: “All club members must raise their hand before speaking.” If this rule is changed by a mischief-maker, every member of the club follows it, but non-members aren’t affected.


3. Function.prototype → “Coach’s Guidelines”

  • Pollution affects functions (.call, .apply) and objects inheriting from functions.

  • Analogy: Every coach has a set of instructions for exercises.

  • Example: If a bad coach says “run clockwise instead of counterclockwise,” all athletes following that coach change their routine.


4. Custom Prototypes → “Mentors for Special Teams”

  • Any object can have its own prototype chain. Polluting a parent prototype here affects only children inheriting from it.

  • Analogy: Special mentorship groups have their own rules on top of family and school rules.

  • Example: “Mentorship rule: always submit homework on Monday.” Changing this affects only students in this group, not the whole school.


5. Pollution Flow → “How the Bad Rule Spreads”

  • Adding a property to any prototype → children inherit it → sinks/gadgets trigger impact.

  • Analogy: A single “bad rule” injected at the grandparent, coach, or mentor level spreads down the chain: children, students, or athletes follow it, and when someone acts on that rule (sink/gadget), it causes real-world effects.

  • Example: The red-hat rule gets enforced at a parade (sink), causing chaos because everyone follows it blindly.

0
Subscribe to my newsletter

Read articles from Sangharsha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sangharsha
Sangharsha

I write to revisit topics I’m interested in or when I’m bored and curious.