Chaos — Hackthebox writeup

Jake Flint
12 min readMay 26, 2019

Chaos is a recently retired HTB machine that I completed a couple of weeks ago. It’s also the first 30 point machine that I’ve completed.

My usual “nmap -v -sV -A -p- 10.10.10.120 -oN nmap.txt”

The first thing I do is open Firefox and check out port 80. All I get is a big red “Direct IP not allowed”.

HTTP request to port 80

Having no idea what that means, and not seeing any scripts running, I hop over to port 10000.

Webmin running on port 10000

I look up some default credentials and try them out, but they don’t work. Because I promised myself that I wouldn’t jump straight in to gobuster each time, I start up hydra.

hydra -l admin -P /usr/share/wordlists/rockyou.txt chaos -s 10000 -S https-form-post “/session_login.cgi:^USER^:pass=^PASS^:Login=failed” -o hydraloginform.txt

My command seems to freeze, and slows my CPU down a lot. I thought the error message was correct, and there weren’t any redirects when I watched the login request/response… But because I’m eager to get a foothold and throwing rockyou at a login page is a terrible way to start working on a target, I kill it and move on.

I notice that there’s quite a new RCE metasploit module for Webmin < 1.900, but it’s for getting root access and we need to log in to a user that has “Java file manager” and “Upload and Download” fields. It seems like this will be useful in the future so I note it down.

I come crawling back to gobuster.

gobuster -u http://chaos/ -w /usr/share/dirb/wordlists/common.txt -x html,php,txt > gobuster.txt

It finds a Wordpress installation. As is tradition in CTF machines, it’s a very empty blog. There’s one post by ‘human’ that is password protected.

http://10.10.10.120/wp/wordpress/index.php/2018/10/28/chaos/

I try ‘human’. I’m a little annoyed about guessing the correct password on the first attempt, but wpscan or CeWL/Burp intruder would have easily done the job anyway.

We get some “Creds for webmail”. Time to look at those POP3/IMAP ports. I haven’t used either before, and don’t know anything about Dovecot either.
At first, I try to add the mailbox to thunderbird. But I can’t figure it out so I start reading about what else I can do.
I find out that you can use telnet, so I give it a go.

telnet to non-encrypted POP3 (110) and IMAP (143) ports

This shows that Dovecot is configured to disallow plaintext authentication. Telnet doesn’t like connecting to the encrypted ports 993 and 995, so I do some more reading. I find out that you can use openssl’s “s_client” as well as telnet.

openssl s_client -connect 10.10.10.120 995 (POP3)

I use the credentials, but there are 0 messages. I try triggering a password reset for the wordpress admin login page, but it says that ‘admin’, ‘administrator’, ‘ayush’, ‘chaos’ don’t exist. Regardless of what the error message says, no emails are received.
I keep in mind that another HTB user could have deleted an email containing a clue and remind myself to try resetting the box if I can’t find anything. Before I do that, I move on to IMAP (port 993).

I had a really hard time finding the commands to navigate through a mailbox with IMAP, but this page was very helpful.
https://busylog.net/telnet-imap-commands-note/

This is how I navigate through the mailbox:

Navigating through IMAP, after much trial and error

openssl s_client -connect 10.10.10.120:993
a login ayush jiujitsu //
logs me in with the creds from wordpress
a LIST “” “*” //
lists folders
a STATUS Drafts (MESSAGES) //
tells me there’s a message in the drafts folder
a SELECT Drafts //
navigates to the Drafts folder
a FETCH 1 (BODY.PEEK[TEXT]) //
I just guessed email 1, this gives me the email body text and attachments

The draft email, and its attachments ‘enim_msg.txt’ and ‘en.py’.

I base64 decode enim_msg.txt but it’s encrypted and I can’t read it. I base64 decode the python script and start trying to understand how it works.

Two functions, encrypt and getKey

The script defines two functions, encrypt and getKey. getKey is simple, you give it a password string and it SHA256 hashes it, outputting a 16-byte encryption key.

The encrypt function takes that key and a filename and performs these steps:

  1. Defines a chunksize, 65536 bytes (It uses CBC mode so it needs this, but the chunk size is much larger than the message so there’s only 1 chunk)
  2. Adds “en” to the front of the filename as the output filename
  3. Gets the filesize in bytes and stores that as a string, and zfills it out with 0s so the string is 16 bytes long (In this case, 0000000000000234)
  4. It uses the Crypto library to generate a random Initialisation Vector (IV), and stores the first 16 bytes in variable IV
  5. Defines the encryption method, AES with CBC mode, using the key and IV.
  6. First it writes the 16-byte filesize string to outfile
  7. Then it writes the 16-byte IV variable to outfile
  8. Finally, it takes the first (and in this case, the only) chunk of text in the file being encrypted, pads it out with null characters to the next 16 bytes, and encrypts it. It writes that to the outfile

To help me understand what’s going on, I add print functions after any variable is set, with a message about what happened to it. It makes it really easy to understand what’s going on.

encrypting the file ‘test.txt’ with some debugging print lines

Now that I understand how the encryption script works, I can see two flaws that will allow me to write a decryption script.

  1. Ayush tells me the password (or at least part of it), ‘sahay’
  2. The script includes the random IV in the output data.
    (Thank god for this, because at first I thought I had to brute force these 16 bytes, or learn how to write my own padding oracle attack script, which seems a bit beyond me right now.)

The first thing I do is use the getKey() function to do just that, get the key.

The encryption cipher used is AES, CBC mode. So as long as I use the same variables as Ayush did, all I need to use is the decrypt function and I’ll be able to decrypt the message.

Here’s the output of the file after running the decrypt script.

cat/hexdump to clearly see non-printable characters

I get the filesize variable from the first 16 bytes: ‘0000000000000234’
I know that IV makes up the next 16 bytes:

eeae 7aaa 8a19 b3d8 4b70 8538 435a cc83

hexdump displays Little Endian so I rearrange it to get:

IV = ‘\xAE\xEE\xAA\x7A\x19\x8A\xD8\xB3\x70\x4B\x38\x85\x5A\x43\x83\xCC’

I use printf to double check.

Next, I use head/tail to ignore the first 32 bytes (filesize+IV) of the encrypted file, and print the result in python:

data = ‘\xf5\xf0\xb9\x89⁹\xe4\xafkW\x87\x0f\xc0\x95\xd4&w\xf89\xdc\xbe\xa9\x82\xf6\xbdE\xd3\xe4\’q\x92[\x9e\xe8\x9e\xfb\x1e9\xeeZ\x8b\xde\x7f3\x80\x0f\xab\xed\xe6.\x9e\x06C\x96\x13\xb9\x14\xda\x1a\xc1\xed\xac\xcb;\xac\xd83\xc1\xf8\x95\xa2\xbe\xf36\xbc\x8f\x9fR`n\x7f\x0c\xed\x8d\xa63\xa2>\x94}3A\xbe\x97\xb7\xf2d\x84\xe6FY\x0c\xc7\xe2YDo!\xef\xbeR#~\xef\x8c[\x95\xeb8\x8e\x90\x01\xe5\xc6a4\xe2\x9d\x84\xae\xb6\xc3\xa1>)K\x9eM^\xe8z\xbb\x01I\xd3\xc5\xc3,\xaa\xa2\xdd\xa8B\xf9\xfb\xfaq\xdd\x95Yq\xcb\x8fR\xfb\x92\x99q\xc6\x13M\xb7\xdf\x9f.w\xd5\xca\xa2F\xcd@m\xcb9\x0b\x91\x16JD\xfc\xb4\xb5\xd3(\x1e\xfa^\x8e7\x885~\x96"\xcf,\x08\xdf\xe8}\x97\x920\xa6?\xd4U\xbd\x01qX(\x8e\xd9r\xcf\x98\x08\xbf]\xad\x00w\xfb\x92\x96zGO’

TRIVIA: Apparently, for CBC, using the wrong IV only corrupts the first block. In this case, the block size is so huge that the whole message is only one block, so it doesn’t help us. But that’s good to know for the future.

Here’s my decryption script:

the script
the results

This message indicates that “chaos.htb” should be my hostfile entry, not “chaos”, so I update my hosts file.
I could have just browsed to http://chaos:80 earlier but after updating my hosts file, I find the website that the “Direct IP not allowed” message was blocking me from accessing earlier.

The website we could have found at the start

I guess the clue was supposed to be the “Blog” section which is under construction, but thanks to gobuster I skipped that step.

A message that hints at the wp installation

But this taught me that you can serve a user different content, depending on whether they used IP address or hostname.

Anyway, the link in the encrypted message takes us to a page where we can create PDF files.

Unfinished services are always the next step

I run gobuster on this directory and find:

/index.php (Status: 200)
/templates (Status: 301)
/pdf (Status: 301)
/doc (Status: 301)
/assets (Status: 301)
/ajax.php (Status: 200)
/source (Status: 301)
/config.php (Status: 200)
/tex (Status: 301)
/compile (Status: 301)

Looking through the directories, we can see that this service uses LaTeX or pdftex specifically.
I enter some text in to the box and hit “Create PDF” but nothing happens. I turn BURP on again, and see that I was getting a response before.

HTTP response after submitting “test” with template2

It’s nice that I can navigate to the /pdf directory and view the PDF file, which says “test”, just like I entered. It looks like I need to some Remote Code Execution (RCE).

I do a lot of reading to understand how LaTeX works, and when I’m satisfied, I start trying out some commands. I also find the post below. I think this part of the machine is based it.
https://0day.work/hacking-with-latex/

I remember seeing “\write18 enabled” in the log output in the response I saw in Burp, so the service is probably set up in “ — shell-escape” mode.
“This construct is normally disallowed for security reasons.”

I try the first example \input that reads /etc/passwd, but…

After using \input or \include

Reading on, I can get around this by opening files and writing their contents line by line to other files (saving that output to the /pdf directory so I can easily browse to it seems like a good idea).

Working through to the “Executing commands” section, I see this:

\immediate\write18{COMMAND}

It runs the command (sending the input to file descriptor 18 or command line) immediately, rather than waiting for the PDF to be created. This gives me RCE where I can read the results in the response in BURP.
I can get the results of “env”:

\immediate\write18{env}

OR see what’s in /usr/bin:

\immediate\write18{ls /usr/bin}

OR what I really want, a reverse shell. After much messing around with nc, xterm, and trying to write a shell script that executes the reverse shell command, I remember that in the list of binaries above, ‘ncat’ is installed. Eventually I come up with this LaTeX:

Looking at it now, I’m not sure why it works, because the loop seems to be missing a \. But it works anyway!

I’m FINALLY in to the machine

I look around but the /var directory doesn’t seem to contain anything interesting, because I was able to look at most of the html directories in my browser earlier.
There’s a roundcube folder in there, which must be the ‘webmail’ system that ayush and sahay use to access their mailboxes. In the config file, ‘default_host’ is set to localhost, so I don’t think there’s anything more to see here.

I can’t access either of the home directories, so I copy LinEnum.sh over. Here are my notes from the results:

  • There’s a mysql user, irc user, sahay/ayush
  • cron.d contains a daily task called “popularity-contest”, sounds strange (this is just a package manager thing, not useful)
  • There’s a system timer called “phpsessionclean” (this does exactly what it says, and is not useful)
  • Nothing interesting in /etc/hosts
  • /etc/init/mysql.conf doesn’t contain any creds
  • SUID/SGID files look normal

I don’t notice anything particularly promising, so I just try su ayush and use his mail password… It works :(

I’ve got user privilege, but am in a restricted shell

I can log in as ayush now, but can’t use cd, can’t redirect output, and can’t specify ‘/’ in command names. I’m not too sure why, but rbash interprets almost any command as having the PATH “/home/ayush/.app” in front of it, and forbids it.

Double tab tells me what I can do, though.

A list of commands that I can use

I run through this list of restricted shell escaping techniques, but can’t use many of them.

That said, I’m not sure what I was expecting, I guess to noticeably stop being restricted. I keep searching and find this article:
https://pen-testing.sans.org/blog/2012/06/06/escaping-restricted-linux-shells

I learn from this that I need to set PATH and SHELL myself. At first, they’re:

$PATH: /home/ayush/.app
$SHELL: /opt/rbash

But they’re both read-only. After some reading online, there doesn’t seem to be a way to change read-only variables to writable.

BUT, because I’m able to use tar, I can spawn a child process where the variables aren’t read-only. The magic command is:

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

If I check my environment now, we’ve moved down (or up?) a shell level and those variables are no longer read-only.

I change them from rbash to the usual:

ayush@chaos:/var$ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
<l/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ayush@chaos:/var$ SHELL=/bin/bash
SHELL=/bin/bash

Now I’m ready to go. I get the user flag from the home directory and submit it, and start looking for root access.

The only really interesting folder in the home directory is a hidden folder ‘.mozilla’ that I check out.

The first thing I see is logins.json:

{“nextId”:3,”logins”:[{“id”:2,”hostname”:”https://chaos.htb:10000","httpRealm":null,"formSubmitURL":"https://chaos.htb:10000","usernameField":"user","passwordField":"pass","encryptedUsername":"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDSAazrlUMZFBAhbsMDAlL9iaw==","encryptedPassword":"MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECNx7bW1TuuCuBBAP8YwnxCZH0+pLo6cJJxnb","guid":"{cb6cd202-0ff8-4de5-85df-e0b8a0f18778}","encType":1,"timeCreated":1540642202692,"timeLastUsed":1540642202692,"timePasswordChanged":1540642202692,"timesUsed":1}],"disabledHosts":[],"version":2}

Port 10000 was running webmin, I’d actually forgotten about that (I picked at this box all week). But remembering the Metasploit module, if I can log in to webmin I can probably get a reverse shell as root.

I spend a lot of time trying to decrypt the password, when I realise that I could just try importing the file itself. I get rid of my own logins.json file and copy it over.

I restart firefox and nothing happens. This is because I forgot to grab the key4.db file that contains the key to decrypt the password.

The key3.db file store the encryption key that is used for encrypting and decrypting the passwords. The encrypted names and passwords are stored in the logins.json file.
https://support.mozilla.org/en-US/questions/1076170

I copy the key database over as well, and put them in my own firefox profile’s folder. I open webmin and Firefox asks me for a master password.
I try ‘jiujitsu’ and it works. I would complain about this, but the machine has taken so long by this point, so I’m glad.

The root account is the only user so it probably has all permissions, I check it anyway and see that it has the “Java File Manager” and “Upload and Download” permissions, which are needed for CVE-2019–9624 .
I grab the password from Firefox, set the rest of the module’s options and run it. Success!

I had to update my exploitdb folder for this, the exploit is quite new
Finished!

Although this machine felt like a long treasure hunt, there were a lot of satisfying problems to solve. In total, I estimate that it took:

  • Learning how to access Ayush’s mailbox and recover the message — 1 hour
  • Understanding the python script and writing a decryption function — 4 hours
  • Learning LaTeX commands and how pdftex has been implemented — 1 hour
  • Using Sebastian Neef’s blog post to get a reverse shell — 1 hour
  • Enumeration as web service and Ayush — 1 hour
  • Escaping the restricted shell and collecting the user flag — 2 hours
  • Session stealing and privesc through webmin — 1 hour

No doubt about it, it’s the longest machine I’ve pwned.
I want to make a habit of identifying what I’ve learned from each challenge. This time, I learned:

  1. Website content can be different, depending on whether you use hostname/IP
  2. How to navigate through POP3/IMAP mailboxes in the command line
  3. Understanding and reversing encryption scripts is always a good learning experience, and quite satisfying
  4. LaTeX exists, how to use it, also don’t rely on user input
  5. Never reuse credentials
  6. If you want to give a user a restricted shell, ensure that they can’t spawn child processes
  7. Browser saved session files are no joke
  8. Never reuse credentials

--

--