What is Prototype Pollution?
Prototype pollution is a critical vulnerability that affects JavaScript applications by allowing an attacker to manipulate the prototype of JavaScript objects, often leading to unexpected and dangerous behaviors throughout the application. In this article, we will outline the elements of this JavaScript vulnerability as well as how it functions, how to test for it, and how to exploit it in a pentesting situation (without causing a denial of service).
In JavaScript, every object inherits properties and methods from the Object prototype, making it a central part of how objects work. If an attacker can modify this prototype—sometimes through user input that’s incorporated into an object—they can “pollute” the entire prototype chain. This pollution can result in:
- Unexpected Behavior: Default methods or properties can be overridden, which may break essential functionality.
- Denial of Service (DoS): In server-side environments, prototype pollution often results in DoS, even when that’s not the attacker’s goal.
- Data Leakage: With certain payloads, attackers might gain unauthorized access to sensitive data or elevate their privileges.
- Remote Code Execution (RCE): In some advanced cases, if code execution relies on polluted object properties, an attacker may achieve RCE.
Before diving deeper, it’s essential to understand how JavaScript’s object prototypes work. Without a firm grasp of these concepts, finding and fixing prototype pollution issues is challenging, and you may inadvertently cause a DoS if you’re testing on a live system or during a bug bounty. So, let’s start by covering the fundamentals of JavaScript’s object model, including Objects, Prototypes, the Prototype Chain, and Inheritance.
Objects, Prototypes, Prototype Chain, and Inheritance
Basic Objects in JavaScript
// Creating a simple object
const car = {
brand: "Mini",
model: "Countryman"
};
// Viewing properties of the car object
car.brand;
car.model;
// Adding a new property
car.year = 2019;
car.year;
// Converting the year to a string
car.year.toString();
Now that we’ve created an object and can assign and access properties, notice that we didn’t add toString()—where did it come from? By inspecting the car object, we can see it has a prototype, and within that prototype, the toString() method is available. Let’s explore how prototypes work to understand this further.
Understanding Prototypes
In JavaScript, objects have a hidden property called __proto__, which points to the prototype of the object:
car.__proto__;
This will display the Object prototype, which every object inherits from and which contains default methods like toString and hasOwnProperty. This is how our car object can access the toString() method, even though it isn’t explicitly defined within car. This mechanism is known as the prototype chain.
Prototype Chain and Inheritance
The prototype chain enables JavaScript’s inheritance model. When a property or method isn’t found on an object, JavaScript traverses up the chain to check for it on the object’s prototype. Let’s examine this concept with our example.
car.hasOwnProperty("brand"); // true, exists on car
car.hasOwnProperty("idontexist"); // false, doesn’t exist on car
// Adding a new property to the base Object prototype
Object.prototype.gearBox = "manual";
Object.hasOwnProperty("gearBox"); // false
Object.gearBox; // undefined
car.gearBox; // "manual"
car.hasOwnProperty("gearBox"); // false
As shown, JavaScript uses the prototype chain to look up properties when they don’t exist on an object itself, returning values from the prototype if found. If we create another object, say mini, using car as its prototype, JavaScript creates a prototype chain. Accessing a property on mini first checks mini, then car, and finally the base Object. This forms a “chain” of prototypes.
Adding Methods to the Prototype
In JavaScript, you can also add custom methods to the base prototype:
// Adding a custom method to the Object prototype
Object.prototype.greet = function() {
return "Hello there";
};
console.log(car.greet()); // Outputs: "Hello there" from the prototype
Now, every object has access to greet() through its prototype, demonstrating why modifying Object.prototype can have widespread effects. This ability is what makes prototype pollution so dangerous.
Testing for Server-Side Prototype Pollution
When testing for server-side prototype pollution vulnerabilities, it’s important to approach with caution. Payloads can easily crash an application or change its behavior to cause denial of service, as changes to the prototype will impact the entire application. First, we’ll look at “safe” techniques for testing and detecting prototype pollution vulnerabilities without causing disruptions. Note that the application can still be impacted in a negative way even when these techniques are used, so ensure you fully understand them and the risks before proceeding with your target. To prevent accidentally breaking functionality or triggering a DoS, we can use benign payloads that don’t interfere with critical application logic. For example:
{
"__proto__": {
"status":499
}
}
To check for Prototype Pollution using the “status” in NodeJS applications:
- We can send a request with a broken JSON payload, observe the response code (this is likely to be 400)
- Send the status payload to attempt to change the standard status code
- Resend the broken JSON and once again observe the response code
If the response code is 499, then we have achieved Prototype Pollution, and we can revert this change by sending the status once again with the value of 0.
Crafting an Impactful Payload
Crafting an impactful payload for prototype pollution is highly context-dependent. The unique structure and behavior of each application determine the possible effects of a polluted prototype. Different applications rely on specific objects and properties, so the properties you target will vary significantly depending on how the application uses its data. To maximize the impact, understanding how the application structures its objects and where you might influence properties is essential.
Why Impact is Unique to Each Application
Every application interacts with object properties in its own way. Because of this, what constitutes a “high-impact” payload will differ from one app to another. For example:
- User Session Management: One application might rely on certain properties in a session object to determine user roles. By injecting custom properties here, you could manipulate user permissions.
- Configuration Settings: Another application might look for configuration flags in an object’s prototype, making it vulnerable to pollution that enables or disables features.
- Error Handling and Logging: Some apps log object properties to track errors or activity. Modifying prototype properties could result in false logging data or expose sensitive information.
An impactful payload, then, is one that targets the objects or properties an application relies on for core functionality. The most valuable targets are often found by observing how the application accesses and manipulates data, especially in sensitive areas like authorization, configuration, and error handling.
Finding Object Properties to Test
Finding the right properties to target often requires techniques such as API response analysis, fuzzing, and source code review. Here are some methods to identify potentially impactful properties:
- Inspect API Responses: APIs often expose property names that the application uses internally. Look for objects in JSON responses with predictable structures or sensitive properties. For instance, properties like isAdmin, config, or userRole could be useful targets for pollution if they affect access or configuration.
- JWT Claims: If the application uses JWTs (JSON Web Tokens), it may look for certain properties in these tokens to determine user roles or permissions. Polluting properties that align with JWT claims (like role or permissions) can be impactful if the application naively reads these values from polluted objects.
- Fuzzing for Object Properties: Fuzzing can help reveal object properties that trigger certain behaviors in the application. Tools like burp suite or custom scripts can send various payloads to test for unexpected behaviors. For instance, by fuzzing with properties like “admin”: true or “authenticated”: true, you may reveal roles or features that can be influenced through prototype pollution.
- Source Code Review: When possible, reviewing the application’s source code or JavaScript bundles can give insight into how it handles objects and properties. Look for places where user input is merged into objects or configuration data, especially if Object.assign, _.merge, or other object manipulation functions are used.
- Analyze Error Messages: Error messages can sometimes expose property names used within the application. If an error message mentions a missing or undefined property, it can clue you in on names that might be used within critical objects. Injecting values for these properties can sometimes alter the app’s error handling, potentially leading to information leakage or other unexpected effects.
- Configuration and Feature Flags: Many applications rely on configuration flags or feature toggles. By identifying property names associated with configuration (e.g., debug, enabled, loggingLevel), you may be able to influence settings in a way that exposes additional information or functionality.
Example: Crafting a Targeted Payload
Once you have a sense of which properties may be impactful, construct a payload that targets these areas. For example:
{
"__proto__": {
"isAdmin": true,
"loggingLevel": "debug",
"userRole": "superuser"
}
}
Here we’re targeting properties (isAdmin, loggingLevel, userRole) that might influence user permissions and the app’s logging behavior. If the application relies on these properties for access control or logging, this payload could lead to elevated privileges or increased visibility into the application’s inner workings.
By carefully identifying and targeting specific properties, you can maximize the impact of your prototype pollution payload without inadvertently causing a denial of service. Each application may require a different approach, so the more you understand its data flow and object structure, the more effective your payloads will be.
For More Advanced Web Hacking…
We hope this overview was helpful, and if you would like to learn more about this JavaScript vulnerability, the first module of our Advanced Web Hacking course covers prototype pollution in greater detail with hands-on labs. For more information and training about this and other advanced web hacking techniques, consider joining the TCM Academy.
About the Author: Alex Olsen
Alex is a Web Application Security specialist with experience working across multiple sectors, from single-developer applications all the way up to enterprise web apps with tens of millions of users. He enjoys building applications almost as much as breaking them and has spent many years supporting the shift-left movement by teaching developers, infrastructure engineers, architects, and anyone who would listen about cybersecurity. He created many of the web hacking courses in TCM Security Academy, as well as the PWPA and PWPP certifications.
Alex holds a Master’s Degree in Computing, as well as the PNPT, CEH, and OSCP certifications.
About TCM Security
TCM Security is a veteran-owned, cybersecurity services and education company founded in Charlotte, NC. Our services division has the mission of protecting people, sensitive data, and systems. With decades of combined experience, thousands of hours of practice, and core values from our time in service, we use our skill set to secure your environment. The TCM Security Academy is an educational platform dedicated to providing affordable, top-notch cybersecurity training to our individual students and corporate clients including both self-paced and instructor-led online courses as well as custom training solutions. We also provide several vendor-agnostic, practical hands-on certification exams to ensure proven job-ready skills to prospective employers.
Pentest Services: https://tcm-sec.com/our-services/
Follow Us: Email List | LinkedIn | YouTube | Twitter | Facebook | Instagram | TikTok
Contact Us: sales@tcm-sec.com
See How We Can Secure Your Assets
Let’s talk about how TCM Security can solve your cybersecurity needs. Give us a call, send us an e-mail, or fill out the contact form below to get started.