This is the story of how I made $500 from finding a simple bug. Before I show you how I won a prototype pollution bug bounty, let’s start out with some technical background.
The hash map is a basic, highly useful data structure. All modern coding languages support it in some way or another. In Python, you have the dictionary. In PHP, the “associative array”. For use JavaScripters, we use the humble Object. This creates an issue. Think about it – the data structure we use to create hash maps is also an integral part of how the language works. Does this create security bugs? You bet it does!
If we let a user control the properties of an object, they can climb up the object’s “prototype chain” to create properties on ALL objects! In the next section, we’ll explain this in more detail. But the point is, the way people use has maps in JS in inherently insecure. The smart solution is to use a real data structure, like ES 6’s new Map.
As a JS fan, I love this hack. Just a few months ago, an NGO from South America by the name of Code, Not Guns paid me $500.
How prototype pollution works
So, let’s say you have an object that stores a user’s data. You allow the user to modify this object via a JSON API, so it looks like this:
let user_profile_json = res.json()
let user = get_user_from_db(user_profile_json)
if (user.is_admin) {
return admin_panel;
}
Good, so the profile is set to whatever they send us via the API, right? You need admin privs to access the admin account, all seems right. Now, what if the user sends a request like this:
{
"username": "whatever",
"password": "irrelevant",
"__proto__" : {
"is_admin": true
}
}
This creates a massive issue. The is_admin trait is undefined by default for non-admin users (like ourselves!). But because we added it to the prototype chain, all objects will share it. So now it will be set to true by default, instead of undefined!
Uh oh. With this evil payload, any user can access the admin panel as though they were the legit admin.
You may wonder why JS has this prototype chain to begin with. That’s a bit beyond the scope of this story, so let me point you to a great resource: Inheritance and the prototype chain from Mozilla’s web docs. It’s a splendid read and will make you a better hacker, I promise.
It’s a powerful bug – even if it’s only reflected back to yourself, you can combine it with other vulns like Web Cache Poisoning to great effect.
Anyway, time for the fun part: how I found this bug!
Finding the prototype pollution bug bounty
CNG used to have an API where you could upload your data and get a dashboard of how much you’ve helped their charity by contributing code, closing tickets, etc. Well, it’s not really a “charity” per se, but close enough. Anyway, the dashboard looked like this:
I didn’t even need Burp, just opening up the Chrome dev tools I could see the JSON passed to the backend. Which was exactly the same as what I used in the example above! So I wrote a tool in Python to crawl the dashboard for every JSON API, and sent them all the same malicious payload.
If the result differed from the “normal” result sans payload, I assumed a vuln was there! Unfortunately, I then sold this tool to CNG for a separate sum of money, and it’s not open source yet (soon it will be though!). But trust me, it’s pretty cool. I think they are offering it as a preview. It was cool to make double money from this bounty, since I was also paid well for the tool I wrote.
Crafting an exploit was as simple as looking at the source code. You see, CNG’s dashboard was open source. So I just had to look at what object my tool’s genetic algorithm had fiddled with. Once I found it, I changed the admin setting just like I showed you earlier.
Reporting the prototype pollution bug bounty
This was the easy part, I just went to the security.txt file of cng.ngo and sent an email to the address I found there. They got back to be right away. But with an automated email! A lot of bug bounty hunters don’t know how much the initial report matters. Follow a good template and include lots of screenshots and code samples. Explain clearly the exploit impact of the bug. Otherwise you just might get ignored.
Getting paid
I waited and waited – that’s the worst part of bug bounties. Because you never know how the recipient will react, it’s nerve racking! Of course, soon enough they got back to me saying they could recreate the bug. Yes! They said they’d get back to me in a week. One week later? No response.
Then, just before I went to bed, this image attachment appeared in my email inbox:
An odd way to reward a bounty, but hey, you won’t find me complaining! I opted to receive the money in crypto instead of cash, since they offer payouts in Monero. Just to be quirky!
This may not seem like a huge sum of money, but when you consider this was only 2 days of (really fun) work… well, I’m happy my prototype pollution bug bounty paid this much.
What’s next?
Bug bounties are unpredictable, especially when it comes to rare types like a prototype pollution bug bounty. However, the key to making money is to highly specialize in a narrow type of bug so you can find it quickly. Then, no matter what codebase you go in, you know exactly where to look, and what the signs are. Prototype pollution will never be my bread and butter. I will always specialize in HTML rendering bugs.
But that doesn’t mean I can’t have fun and mix it up every once in a while! Bug bounty hunting is best thought of as a hobby, rather than as a primary income. If you’re a pentester and want to stay fresh, find an open source project you believe in and just dive in. Begin hunting for bugs and see what you can find. Make the world a better place with hacks!
Good luck getting that money, and as always, happy hacking!
Leave a Reply