Active Directory Domains
A comprehensive guide to Active Directory Domains, exploring their architecture, purpose, and common cybersecurity attack surfaces for both defenders and penetration testers.
Feb 17, 2026Windows
Object prototype pollution is a subtle yet powerful vulnerability in JavaScript that exploits the language's prototypal inheritance model. By manipulating the prototype chain of objects, attackers can inject malicious properties that propagate across an application's entire object graph. This can lead to severe consequences like remote code execution (RCE), privilege escalation, or data exposure. In this article, we'll explore the mechanics from a red team perspective, highlight the dangers it poses, and outline blue team strategies for prevention and detection.
In JavaScript, every object inherits properties from its prototype, ultimately tracing back to Object.prototype. When you access a property on an object, the engine checks the object itself first, then its prototype, and so on up the chain.
Prototype pollution occurs when untrusted input (e.g., from a JSON payload) is merged into an object without proper sanitization, allowing attackers to overwrite properties on Object.prototype. This pollution affects all objects in the application that inherit from the default prototype, turning a single tainted property into a global weakness.
Consider a basic user configuration merger:
// Vulnerable code: Merging user input without sanitization
function mergeUserConfig(userConfig) {
const defaultConfig = { theme: 'light', notifications: true };
for (let key in userConfig) {
defaultConfig[key] = userConfig[key];
}
return defaultConfig;
}
// Attacker's payload
const maliciousInput = {
"__proto__": {
isAdmin: true // This poisons Object.prototype!
}
};
const config = mergeUserConfig(maliciousInput);
console.log({}.isAdmin); // Now true for ALL objects!Here, the loop iterates over the malicious input, setting __proto__ on defaultConfig, which modifies Object.prototype.isAdmin. Suddenly, every object in the app has an isAdmin property set to true.
From an attacker's viewpoint, prototype pollution is a gateway to chaining exploits. Red teams love it because it's often low-hanging fruit in Node.js apps, especially those using libraries like Lodash's _.merge() or handling JSON inputs naively.
Reconnaissance: Identify endpoints that accept JSON payloads for merging configs, user prefs, or query params. Tools like Burp Suite or custom fuzzers can probe for __proto__ pollution.
Injection: Craft a payload targeting __proto__ or constructor.prototype to inject properties. Common targets:
isAdmin or role.child_process.exec() gated by a property, pollute that gate.Chaining Attacks: Once polluted, leverage it for deeper exploits. For instance, in a Express.js app:
// Vulnerable auth middleware
app.post('/api/action', (req, res) => {
if (req.user.isAdmin) {
// Sensitive operation
require('child_process').exec(req.body.cmd);
}
});
// Exploit payload
const exploit = {
username: 'attacker',
"__proto__": {
isAdmin: true
},
cmd: 'rm -rf /'
};By polluting isAdmin, the attacker bypasses auth and executes arbitrary commands.
Real-world tools like ppmap (a prototype pollution scanner) automate this, scanning for gadgets—code paths where polluted properties influence control flow.
Prototype pollution isn't just a "weird JS quirk"—it's a critical risk with CVSS scores often hitting 9.8 (Critical). Here's why:
process.mainModule.require.cache), SSRF, or DoS (e.g., infinite loops via getters).In production, this has led to breaches like the 2019 Capital One incident (though not purely prototype-based, similar prototype issues amplified damage). The danger amplifies in microservices or serverless environments where pollution can cascade across boundaries.
Blue teams must treat prototype pollution as a first-class citizen in their secure SDLC. Prevention is key, but layer on detection for defense-in-depth.
// Safe alternative: Use Object.assign with explicit keys
function safeMergeUserConfig(userConfig, allowedKeys) {
const config = { theme: 'light', notifications: true };
allowedKeys.forEach(key => {
if (key in userConfig) {
config[key] = userConfig[key];
}
});
return config;
}
// Or use libraries like 'deepmerge' with noProto option
const deepmerge = require('deepmerge');
const config = deepmerge(defaultConfig, userConfig, {
isMergeableObject: (val) => val && typeof val !== 'object'
});Object.create(null) for non-inheriting objects.// Create objects without prototype
const safeObj = Object.create(null);
safeObj.userInput = maliciousInput; // No pollution riskUse Object.freeze(Object.prototype) in sensitive environments to prevent modifications:
Object.freeze(Object.prototype);npm audit or Snyk. Prefer safe mergers like lodash.set() with path validation.
Modern versions of libraries like lodash (>=4.17.21) and jQuery have patched prototype pollution issues. Always update dependencies.prototypes-pollution-guard middleware in Express to log/block __proto__ accesses.retire.js or eslint-plugin-security to flag vulnerable patterns.Regular pentests and bug bounties uncover these; platforms like HackerOne have seen numerous prototype pollution bounties.
Object prototype poisoning exemplifies JavaScript's double-edged sword: flexible inheritance enables elegant code but invites insidious attacks. Red teams wield it for devastating impact, but armed with awareness, safe coding practices, and vigilant monitoring, blue teams can neutralize the threat. As JS ecosystems evolve, staying ahead means baking prototype safety into your defaults—don't let a single __proto__ undo your empire. For further reading, check OWASP's prototype pollution cheat sheet or experiment in a safe Node REPL.
Love it? Share this article: