__Proto__ Pollution
DaNeil C
Posted on September 22, 2020
Recently, I was doing a HackTheBox that was using Prototype Pollution as the exploit and thought I would share what I learned.
Objects
First thing to understand is Objects in JavaScript so let's create one. They can be created through two major ways: by creating a variable collection of values or though a class constructor.
To create an object collection of values you:
let myPersonObj = {
"name": "Jason",
"age": 31,
"home": "Here",
"job": True,
"pets": ["Willow", "Cooper"]
}
When you create a Object in JavaScript you use a specific notation (JSON) that includes key/value pairs that are surrounded by curly braces {}, separated by comma, each key and value are separated by a colon, and each key must be a string.
You can also create an object though constructor. Using a constructor still needs the same JavaScript Object Notation as the previous example but is made a bit easier with a function.
function newPersonObj(name, age, home, job, pets) {
this.name = name;
this.age = age;
this.home= home;
this.job= job;
this.pets= pets;
}
Using a constructor allows for more flexibility when needing to create a large number of objects by calling the function and passing it values.
let myFriend1 = new newPersonObj("Jason", 30, "Seattle", true, ["Willow", "Cooper"])
Inheritance
We know that JavaScript is an Object Oriented Programming language, but it is also known as a "prototype-based programming" language.
In a prototype-based program, objects inherit properties/methods from classes. The classes are derived by adding properties/methods to an instance of another class or by adding them to an empty object. (1) (Basically, everything is an object and it inherits its properties/methods from other object's properties/methods or is created.)
"In addition, any object can be associated as the prototype for another object, allowing the second object to share the first object’s properties." (4) This is possible because in JavaScript every object has a prototype as a way for it to be linked to another object and allows you to add new properties to object constructors.
In the example above, myFriend1 is derived from the myPersonObj which is itself an Objects.Prototype of null.
Also notice that, if you add a property to an object that is used as the prototype for a set of objects (like the myPersonObj), the objects for which it is the prototype also get the new property, but that property is not printed unless specifically called on.
Now, if we build the same object in a browser we can see the break down of the object (myFriend1), its prototype (that's not seen but is: Object {gender: "male", ...}), and its prototype constructor (newPersonObj).
You'll also notice that each level of prototype has a constructor and prototype. It seems endless and repetitive if you open them up actually.
Prototype Pollution
Now that we understand prototypes and inheritance a bit it's time to pollute it.
Think about this...
"When new objects are created, they carry over the properties and methods of the prototype “object”, which contains basic functionalities such as toString, constructor, and hasOwnProperty." (5)
Because of this chaining we can make application-wide changes to all objects by accessing it through the "__proto__
" property of any JavaScript object.
Looking at the code below you can see a general flow of information in this HTB.
-The Index calls Clone() in the ObjectHelper.
-The Clone() checks if the key is not "__proto__
" with isValidKey() and then passes it to isObject() to check if it is an object or a function.
-Once this is done Index calls on isDumb() in the StudentHelper file to check if the name that is being posted is one of the two that are restricted. If found then "no00o0o00oope" is passed back.
Index also can call on the Debug file and pass an action to it. In the DebugHelper file there is the RAM action that returns an error and the Version action that retunes the version on the screen.
If we look at the error from the RAM action we can see the mention of the execSync Child Process and the Object.execute helpers... interesting.
The ChildProcess is an EventEmitter that uses the execSync() method to create a shell execute command that will not return until the child process has fully closed.
The Attack
A common prototype pollution is done with the payload containing the "__proto__
" property, but as shown above, this isn't an option as it is checked for. So something like { “name” : “Cat”, "__proto__" : { "pass" : true } }
will not work.
After a lot of research I found the article "Exploiting prototype pollution – RCE in Kibana (CVE-2019-7609)." (8) This article talks about how "in Linux there’s a file called /proc/self/environ which lists all environmental variables of the current process."
From here we can construct the following payload to find the flag.
{
"name":"Cat",
"constructor":{
"prototype":{
"env":{
"EVIL":"console.log(
require('child_process').execSync('cat flag_*').toString())//"
},
"NODE_OPTIONS":"--require /proc/self/environ"
}
}
}
This payload pollutes the Object prototype by traveling up the prototype chain to set two environmental variables (EVIL and NODE_OPTIONS). NODE_OPTIONS gains access to the environmental variables and EVIL uses the execSync() to make use of bash shell to gain access to the flag file in the website and display it on the page with the console log command.
To get the payload to work it needs to be uploaded in one line into the website. This is done through the POST request that is made for the student's name.
Although it appears that it didn't work if we navigate back to the debug page and use the Version action we can read the flag file.
Afterthoughts
Below you can see what is shown in "ls" is in the console.log instead of the "cat" command.
It's very interesting seeing that with this payload that it appears that any bash commands can be sent in... think about that for a minute.
Mitigation
- Always validate user input on server side and client side before processing the data.
- Be careful when using the clone operation because it is basically a merge operation on an empty object.
- Be sure to conduct regular code reviews and penetration testing.
Resources:
- https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance
- https://www.w3schools.com/js/js_object_prototypes.asp
- https://www.w3schools.com/js/js_object_prototypes.asp
- https://medium.com/@theflyingmantis/javascript-a-prototype-based-language-7e814cc7ae0b
- https://portswigger.net/daily-swig/prototype-pollution-the-dangerous-and-underrated-vulnerability-impacting-javascript-applications
- https://node.readthedocs.io/en/stable/api/child_process/
- https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options
- https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
Posted on September 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.