June 30, 2023 2:15 PM (GMT+2) - BitK & SAKIIR

Prototype pollution is a vulnerability in JavaScript applications that can have varying impacts depending on the complexity and nature of the affected app. It exploits the prototype inheritance feature of JavaScript, which allows objects to inherit properties and methods. By manipulating the prototype chain of an object, an attacker can introduce malicious properties, leading to unexpected behavior and potentially allowing the attacker to execute arbitrary code.

In this talk, we will first give an overview of JavaScript prototypes and prototype pollution attacks. We will then introduce a new tool we have developed to assist ourselves in identifying gadgets by instrumenting the source code. This allows it to aid in whitebox audits, enabling researchers to easily identify vulnerabilities in large codebase. Finally, we will demonstrate how the tool is used during a live demo targeting popular JavaScript libraries. Our goal is to help researchers and developers understand the potential impacts of prototype pollution and learn how to identify and exploit these vulnerabilities in JavaScript applications.

https://github.com/yeswehack/pp-finder

https://blog.yeswehack.com/talent-development/server-side-prototype-pollution-how-to-detect-and-exploit/

IMG20230630140849.jpg

IMG20230630142135.jpg

Today, we will dive into the world of prototype pollution, focusing on server-side exploitation. Although this attack has become more prevalent for client-side or open-source applications, it can still be challenging to test and exploit in a black box scenario. In this blog post, we’ll first explore how prototypes work in JavaScript, then examine methods for detecting vulnerable websites and finally finding gadgets in popular libraries to develop a working exploit.

Prototype pollution?

To have a comprehensive understanding of prototype pollution in JavaScript, it’s important to first grasp the concept of prototypes in the language. JavaScript is an object-oriented language that heavily relies on inheritance. For example, where does the method hasOwnProperty come from in the following example?

const x = {a: 42}

x.hasOwnProperty("a") // true
x.hasOwnProperty("hasOwnProperty") // false

When you try to access a property, JavaScript first looks at the object itself and checks if the property exists. If it exists, the value of this property is returned. If not, JavaScript looks if the same property exists in it’s prototype. This process is then repeated until the property is found, or when an object doesn’t have a prototype anymore.

This method actually comes from the prototype of x, which is Object.prototype.

You can access the prototype of any object via the magic property __proto__.

const x = {a: 42}

typeof x.hasOwnProperty // "function"
x.__proto__ === Object.prototype // true
x.__proto__.hasOwnProperty("hasOwnProperty") // true

const s = "test"
typeof s.hasOwnProperty // "function"
s.__proto__ === String.prototype // true
s.__proto__.hasOwnProperty("hasOwnProperty") // false
s.__proto__.__proto__ === Object.prototype // true
s.__proto__.__proto__.hasOwnProperty("hasOwnProperty") // true

The Object prototype is shared accross all objects, that mean if we modify it, it will affect all the object that use it.

const x = {y:42}

x.y // 42
x.z // undefined

Object.prototype.y = 'hello y'
Object.prototype.z = 'hello z'

x.y // 42
x.z // 'hello z'

In this example, the value of x.z is ‘hello z’ because the property z doesn’t exist in x but exists in the prototype. This is an important aspect to understand when it comes to prototype pollution. If an attacker can modify the prototype, they can affect all objects that inherit from it, which can lead to dangerous and unexpected consequences

Let’s look at a vulnerable code example:

const config = {
    //allowCMD: true
}
const users = {
    "guest": {name: "guest"},
}

function updateUser(username, prop, value){
    users[username][prop] = value
}

app.route("/update", (req) => {
    const {name, prop, value} = req.query
    updateUser(name, prop, value)
})

app.route("/eval", (req) => {
    const {code} = req.query
    if (!config.allowEval){
        return req.status(403)
    }
    eval(code)
})

In this example, submitting a request with name=__proto__&prop=allowEval&value=true will update the prototype of the users object with allowEval=true. Since the config and users objects share the same prototype, the next time we try to access the /eval route, we will be allowed to run arbitrary code.