May 6th 2019
13 min read


Team RAMROD wins again - back-to-back champs! This post details my experiences with the CTF contest at THOTCON 0xA on May 3rd and 4th, 2019.

There are lot of really interesting talks and other things to see and do at the conference, but I always find doing the contest to be time well-spent. It's both easy to get into, and it covers so many disciplines that anyone can feel like a 1337 H4x0r by the closing ceremonies. Maybe next year I'll get to some of those talks, though...

Team RAMROD members this year included me, Landon Turner, Claude, Evan White, and Patrick "Yolo" McDonald.

Stage 0 - Freebie

Some effort is required to get your team registered and tokens submitted, so this sanity check is helpful. Once you claim this freebie token, you can be sure that all of your admin work is done. Back at THOTCON 0x7, we spent some time writing scripts to register, submit tokens, and check the scoreboard. Fortunately, they haven't changed how the contest works, and we don't have to worry about this each year.

Each token gets hashed and combined with the stage number to create the url of the next stage.


echo -n '<token-goes-here>' | sha256sum

Stage 1 - :gun::zipper_mouth_face::flag-it:

An image was posted to the Twitter page...

Examining the file, it's clear that there's more to it than just the image itself. Running strings yields a lot of printable ascii you wouldn't expect in a jpg, and hexdump shows significant data AFTER the FF D9 end-of-jpg magic number.

The emojis in the image itself seem to suggest that it's a zip file and that we should "gUN ZIP IT". Attempting to unzip doesn't error, but it does require a passphrase to get the hidden files out. Trying some of the more reasonable text from our strings output as passphrases, we finally extract what we're looking for - a text file with a flag.


hexdump ambiguous.jpg | grep 'ff d9'
strings ambiguous.jpg | awk 'length>10'

Stage 2 - Happy Hunting

A link to is given, along with a clue saying that you should spend some time playing your FAVorite game. A fully-playable javascript implementation of Quake 3 is loaded at that page, though it's tough to play an FPS with a touchpad. But that's not why we're here, not yet at least. Let's check the favicon (as per the clue).

Watching the network console of the browser, we notice it's requesting 4 different images in a loop. The images are numbers - 209, 097, 149, and 011. Sure looks like an IP (ignoring the leading 0's), but there's nothing on port 80. Maybe it's the IP of a multiplayer Quake server we need to connect to in-game? It is. Maybe there's no password required? Yeah, right!

After reading way too much about the Quake 3 console and trying to dump configs, browse the filesystem, and enable special cheats, we were almost out of ideas. Viewing source on the original page, however, yields this:

// serverpass: EndgameSpoiler

Of course. :facepalm: Joining the server at that IP with the password puts you into a game with a handful of suspiciously named bots.

Ignoring gibeflagz and Noob Slayer69, the rest are obviously what we're looking for. After a little bit of trial and error with the ordering, we're on our way with the next flag.

Stage 3 - Quiz Time

An obvious IP address and port is given as the clue for this stage: 0x03 dot 0x0E dot 0x7C dot 0x62 colon 0x539. After conversion, hitting in the browser yields the following:


Unfortunately 404's, and doesn't work either. Instead of guessing paths or what format to POST, we connected with telnet. Responding to the 1+1 prompt with 2 here gets us a new question: 1+2. Answering correctly again gets another question, and after 11 questions the whole quiz repeats. We decided to script it, and some quick googling suggested expect.


spawn telnet 1337

expect 1+1
send "2\r"

expect 1+2
send "3\r"

expect 1-1
send "0\r"


After the script rips through a handful of iterations of the quiz, the server spits out something about being good at math and produces a flag.

Stage 4 - From Russia with Love

We're only given a link to the following YouTube video:

The title and description of the video aren't helpful. The transcript is somewhat wrong and most definitely incomplete, and it eventually even devolves into a rickroll. After slowing the video and listening extra closely, you can hear an overlayed audio track of somebody saying Russian letters/numbers. Time to go learn some Russian. After several translation efforts, including a few by a fluent speaker, we had enough to play "GUID Mastermind" with the puzzle creator and figure out the flag.

Getting the full flag took significantly longer than expected (this was a third of the way into day 2 at this point), and it caused some reshuffling of the later stages to be necessary.

Stage Chocolate/Not-Chocolate

We noticed somebody on the scoreboard with an odd number of points. Since we knew each flag was worth 10, something was up. Our investigation led us to a table of chocolates, some of which were edible, some not. The game: "Chocolate/Not-Chocolate". Pick up a "chocolate", and put it in your mouth - it's as simple as that. Mine unfortunately turned out to be a chocolate-covered package of mints. Landon enjoyed a chocolate-covered plastic dinosaur. There were some real chocolates too, and the flag-containing winner turned out to be a chocolate-covered Oreo with a tiny slip of paper inside.

A+++ would Chocolate/Not-chocolate again

Stage 5 - :gift::gift::gift:

The generous folks at the table gave us a puzzle to put together. We completed it relatively quickly, and here's the finished product:

To prevent anybody else from sneaking a peek, we took this photo and quickly destroyed the evidence. It was only after returning the disassembled puzzle that we converted the binary to ascii and found an unfortunate phrase.


After completing the puzzle a second time (and painstakingly flipping it), we could almost make out a QR code, written in nearly impossible to see ink. We took several photos with different lightings and angles, and we messed with levels/curves/saturation/contrast/etc in GIMP. We got close, but it wasn't good enough. After experimenting a bit more with different lightings, a nearby blacklight saved the day - it was glow-in-the-dark paint and lit up nicely.

The QR gave us another site to visit, and hitting it in the browser yielded the following message:

you didnt say the magic number - 23423

We threw the number on the end of the path and tried again, which produced yet another number. After some trial and error, we figured out that the new number is the port of the next request to make, and the "magic number" was the path. It went on and on... - say the magic number - 23423 - 2217 - say the magic number - 6559 - 3776 - say the magic number - 11276

This continued for a long time - longer than I'd like to admit. I thought I might be able to get to the bottom of it manually, but luckily Landon came through with some quick Python. Eventually, a flag was returned.

Stage 6 - Warp Zone

This stage was unfortunately skipped due to Stage 4 being unexpectedly time-consuming for all teams.

Stage 7 - The Plot Thickens

Because of the aforementiond time contraints, stages 7, 8, and 9 were combined and presented as different options for stage 7. We were told we could choose any, but each path had a flag worth 10 points - so why not attempt them all?

Part A - Eight Oh Too Eleven

Our new friend Claude worked this one. Hopefully he's willing to contribute to the post!

Part B - Forensics Examination

We were given access to a stack of 7 100mb zip disks and a zip drive.

Being a forensics challenge, I figured a good first start would be to make images of them. After dd ing them to my disk (at a speed not seen since 1995), it was time to figure out what to do with them.

My first thought was that 7 x 100 = 700 mb = the capacity of a CD - I should concatenate all the images together and mount it as an disk image or maybe a bootable LiveCD. It was a decent guess, actually, but unfortunately not completely correct.

Running file on any of the images tells us it's of type EWF/Expert Witness/EnCase image file format.

I'd used EnCase before. I remembered that it's used to forensically image a disk, and it had an option to split the image into multiple pieces. My original guess was correct, I'd have to merge these files together. Before I could continue along this path, though, I had to find out what order the images belonged in.

Looking at the picture of the disks, it might be completely obvious to those familiar with electronics. Each was labeled with a bunch of colors, resembling the bands on resistors. Renaming the disk images disk.E0[1-7], using resistor values for ordering, was a success. Running ewfverify confirmed this, and it was time to mount it. ewfmount capable of mounting these files, and that too worked like a charm.

Unfortunately, this is when time ran out for the whole contest. I did follow up to find out how the rest was supposed to go. Inside the mounted EWF file was a filesystem. On the filesystem, there was a git repo in the home directory. In the git history for that repo, there was a commit mentioning the removal of the flag. Reverting/showing that commit would've given it up. Bummer - I was almost there!

Part C - GradeCom Enterprise

Long-time RAMROD team member Landon worked this one. You can find all the juicy details about it on his write-up.


So that's the story of our 81 points - 8 flags and a chocolate. The 3-way tie for 2nd place all had 61 points, so they were all working on Stage 7, as well.

A few of these were our alt accounts - testing things out, playing mind games, etc

If you'd like to take a look at some of these challenges, here's the page for all stages. I can't promise that everything will still be live (I'm not hosting), but it's all live at the time of this writing.

THOTCON and the challenge are always a good time. Whether you go to talks, hang out in a room all day solving puzzles like us, or some combination of both - you won't be disappointed. And if you're not sure how to get started with the CTF, just walk up and ask someone what they're working on. Come sit down with us, your reigning champs, Team RAMROD!