In modern web development, JavaScript plays a significant role in both frontend and backend development. However, the flexibility of JavaScript can also expose applications to serious vulnerabilities, one of which is Prototype Pollution. In this blog post, we will dive deep into what prototype pollution is, how attackers exploit it, how you can detect and prevent it, and the tools that can assist in this process.
What is Prototype Pollution?
Prototype pollution is a vulnerability in JavaScript where an attacker can inject properties into the global Object.prototype. Since almost every object in JavaScript inherits from Object.prototype, manipulating this prototype can have widespread consequences. By altering core functionality through this attack, an attacker can affect the behavior of various parts of the code, potentially causing severe security issues such as Denial of Service (DoS), arbitrary code execution, or bypassing security controls.
Key Concepts:
- Prototype: A prototype in JavaScript is an object from which other objects inherit properties and methods.
- Pollution: This refers to the unwanted or unauthorized modification of the
Object.prototype, causing unwanted side effects across the application.
Example of Prototype Pollution
Let’s break it down with a simple example:
let obj = {};
console.log(obj.hasOwnProperty('polluted')); // false
// Malicious code altering the prototype
Object.prototype.polluted = "Yes, I am polluted!";
// Now, this property appears in every object
console.log(obj.hasOwnProperty('polluted')); // true
console.log(obj.polluted); // Yes, I am polluted!
Here, the attacker injected the property polluted into Object.prototype. Now, any object in the program has this new property, leading to potential misbehavior of the code.
Exploiting Prototype Pollution
The most common way prototype pollution happens is through improper handling of user input in JavaScript applications. When applications use certain libraries (e.g., lodash, jQuery) or native methods that merge objects without strict validation, an attacker can exploit these functions to modify the prototype.
Common Attack Vectors:
- Object Merge/Extend Functions:
Merging objects is a frequent practice in JavaScript development. If user inputs are merged directly without proper validation, an attacker can send a payload to pollute the global prototype. For instance, usinglodash’smergefunction:
const _ = require('lodash');
let obj = {};
let maliciousInput = JSON.parse('{"__proto__": {"polluted": "Injected!"}}');
_.merge(obj, maliciousInput);
console.log(obj.polluted); // "Injected!"
- Improper Parsing of User Input:
Attackers can inject malicious code when user inputs are parsed into objects. For instance, in an API accepting JSON input, the payload might include__proto__as part of the object:
{
"user": "attacker",
"__proto__": {
"polluted": "Injected!"
}
}
Example of Exploiting Prototype Pollution in a Real-World Scenario
Imagine a scenario where an e-commerce website allows users to submit orders. Each user’s order details are stored in an object, and the system merges them with default values. An attacker submits a JSON object like this:
{
"orderId": 123,
"__proto__": {
"isAdmin": true
}
}
If the system does not validate the user input and uses an unsafe merge function, the attacker can pollute the Object.prototype to gain unauthorized admin privileges.
Tools for Finding Prototype Pollution Vulnerabilities
- Burp Suite:
- Why Use It: Burp Suite is the go-to tool for web vulnerability scanning. By intercepting requests, modifying payloads, and injecting
__proto__parameters, you can test whether the application is vulnerable to prototype pollution. - How to Use: Use Burp Suite’s Repeater to modify request bodies and headers to test for prototype pollution. Inject payloads such as
{"__proto__": {"polluted": "yes"}}into requests and observe if the application is affected.
- Prototype Pollution Scanner:
- Why Use It: A Burp Suite extension that specifically identifies prototype pollution vulnerabilities by automatically adding
__proto__payloads to requests. - How to Use: Add this extension to Burp Suite and scan your target to find instances of prototype pollution vulnerabilities.
- Lodash Scanner:
- Why Use It:
lodashis a popular JavaScript library frequently targeted in prototype pollution attacks. Thelodash-scannertool can help you identify if your version oflodashis vulnerable to known prototype pollution exploits. - How to Use: Run the scanner on your application dependencies to check for vulnerable versions.
- Manual Code Review:
- Why Use It: Sometimes automated scanners might miss subtle vulnerabilities. Reviewing the code to look for unsafe merging of objects or improper input validation can help you find these issues early.
- How to Use: Look for object merge or extend functions and check if user-controlled input is being passed without validation.
How to Prevent Prototype Pollution
Preventing prototype pollution is largely about ensuring secure coding practices and understanding how objects are handled in JavaScript. Here’s how to mitigate the risks:
1. Avoid Using __proto__
One of the best practices is to avoid the use of __proto__ in your code. If your code or any third-party libraries are manipulating this property, it’s a red flag.
Solution: Use the Object.create(null) pattern to create an object without a prototype:
let safeObj = Object.create(null);
2. Validate User Inputs
Any input coming from the user must be strictly validated and sanitized. Ensure that user inputs cannot inject properties into the global prototype chain.
Solution: Explicitly check and block properties like __proto__ before processing the input.
function validateInput(input) {
if (input.hasOwnProperty('__proto__')) {
throw new Error('Prototype pollution attempt detected!');
}
}
3. Use Safe Object Merge/Extend Functions
Instead of using potentially unsafe functions like Object.assign() or certain lodash methods, opt for safer alternatives or make sure that the function ignores inherited properties like __proto__.
Solution: Use libraries that provide safe merging by default or use custom logic to perform deep merges securely.
function safeMerge(target, source) {
for (let key in source) {
if (key === '__proto__' || key === 'constructor') continue;
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
target[key] = safeMerge({}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
4. Patch and Update Vulnerable Libraries
Many prototype pollution vulnerabilities are introduced by third-party libraries like lodash or jQuery. Ensure that you are using the latest versions of these libraries, as developers often patch known vulnerabilities in updates.
Solution: Regularly audit your dependencies and use tools like npm audit or yarn audit to detect vulnerable packages.
Example of Preventing Prototype Pollution
Let’s revisit the example where an attacker submits the following JSON object to an e-commerce platform:
{
"orderId": 123,
"__proto__": {
"isAdmin": true
}
}
To prevent this attack, we can sanitize the input to exclude the __proto__ property:
let order = { orderId: 123 };
let userInput = {
"orderId": 123,
"__proto__": {
"isAdmin": true
}
};
// Validate the user input before merging
if (userInput.hasOwnProperty('__proto__')) {
throw new Error('Invalid input');
}
// Safely merge the input
order = safeMerge(order, userInput);
console.log(order.isAdmin); // undefined, input sanitized
By validating the user input and using a safe merge function, we prevent the attacker from polluting the prototype and injecting arbitrary properties.
Conclusion
Prototype pollution is a critical vulnerability in JavaScript applications that can lead to severe security risks. By understanding how this attack works, you can better protect your applications from potential exploitation. Always validate user input, use secure object manipulation functions, and keep your dependencies up to date. Tools like Burp Suite, manual code reviews, and specific prototype pollution scanners can help identify vulnerabilities before they can be exploited in the wild.
By following the best practices outlined in this guide, you can prevent prototype pollution attacks and keep your JavaScript applications secure.
