UMD CTF
The following are writeups for all 7 Forensics
challenges I completed during this past weekend’s UMDCTF
hosted by the University of Maryland!
The entire CTF was Pokémon
themed, so you will see a lot of references to the game / TV show in these writeups.
Challenges:
Mirror Unknown
For this challenge, we are given a singular PNG file: Mirror_Unknown.png
.
Knowing a little bit about Pokemon, these are the Pokemon Unown
. Unown has 28 different forms, 1 for each character in the English alphabet, and 2 for ?
and !
.
We could find a picture of all the Unown forms and decode the hidden message manually, but luckily dCode.fr has an Unown Pokemon Alphabet
decoder, which we can use to our advantage on this challenge.
Plugging in the different Unown forms into the decoder, some of them are inverted but still are clearly supposed to be a specific form. We are returned with the value HOJNISSNIUR
.
If we reverse this decoded string, we get the string RUINSSINJOH
. Knowing what I know about Pokemon, this sounded like a place from the game, when I googled it I found the following location:
looking back at the challenge description, I noticed that they mentioned finding the symbols in a nearby collapsed cave, using this I attempted to wrap SINJOHRUINS
with the flag format, and was correct!
Flag: UMDCTF{SINJOHRUINS}
No. 352
For this challenge, we are given a singular JPG file: hide-n-seek.jpg
. We are also given 2 passwords from reading the description.
Passwords:
kecleon
(pokemon #352)timetofindwhatkecleonishiding
Knowing that we have passwords and an image file, I immediatly thought of steganography. I know that steghide
is a popular tool for hiding data within an image using a password, so I attempted to use that.
Running steghide with the first password of kecleon
I extracted the following image, kecleon.jpg
, but since we have 2 passwords we aren’t done yet:
Next, I tried steghide again since it worked the last time, I ran steghide on the new image with the password timetofindwhatkecleonishiding
. This time it returned a file flag.txt
Viewing the flag.txt file, I successfully got a flag!, the following is the list of commands ran to achieve this flag:
Flag: UMDCTF{KECLE0NNNNN}
Fire Type Pokemon Only
For this challenge, we are given a pcapng file: fire-type-pokemon-only.pcapng
.
I started by opening this file in Wireshark and first viewing the protocol hierarchy:
From this we can see a large amount of different protocol types, with a total of just over 34k packets.
Seeing packets with FTP protocol, I went to extract FTP-DATA to see if there was any files that we could extract from the packet capture.
Here we can see 4 different files being sent in this packet capture, some of them with suspicious names like secretpic1.png
and secret
.
Viewing the 2 .png files, they are both just pictures of Pokemon and don’t seem useful to finding the flag. Since we don’t know what the other two files are, I used the linux file
command to determine their file types:
knowing the file types, I changed the extensions on each of them to represent what files they were, I first attempted to view hmmm
however it was just another picture of a Pokemon.
I then attempted to extract the zip file secret
, however the file zip file was locked with a password:
From this we can see that there is a video file wisdom.mp4
inside the zip file, however we do not have a password just yet, so I went back to the pcap file to attempt to find it’s password.
I first started by searching for the string secret
. Since that was the file name I presumed that it could have data around the packet that would lead us towards the password.
Finding a packet that mentioned the secret
file, I started by viewing the TCP stream of that file and here we can see what looks like terminal input for an FTP server.
Here we can see that a user pokemonfan1
logged in with a password of pika
, I looked around the TCP stream and couldn’t find anything else that resembled a password, so I tried using pika
as the password for the zip.
The password pika
successfully opened the zip file and here we see the wisdom.mp4
file from before!
Opening the video file, we see a short video that has a flag shown in the bottom right corner:
Flag: UMDCTF{its_n0t_p1kachu!!}
YARA Trainer Gym
For this challenge, we are given a file containing different YARA signature rules: yara_rules.yar
, as well as the webpage shown in the image above.
Let’s first view the website before diving into the yara rules:
From this we can see that we have to upload a file, and beat 8 different Gyms, similar to how Pokemon is played. However, we will have to view the yara_rules.yar
file to get a better understanding of what our upload must contain.
Viewing the file we can see the following YARA rules:
Click to view YARA
let’s break this down first before I get into my solution:
rule1
: This rule is hex little endian format for “ELF”. Meaning our file must be an ELF file.rule2
: This rule simply means that our file must contain the stringsjessie
,james
, &meowth
.rule3
: This rule means that our binary (ELF) file must contain 40 different sections within it.rule4
: This rule means that our file must contain hex1 & hex2 OR hex3 & hex4. Decoding the hext we get “TEAMROCKET” and “somethingsomethingmalware” for hex1 & hex2.rule5
: This rule specifies that our file entropy must be 6 or higher. Entropy means randomness, so the data within our file must be random.rule6
: This rule means that a string in our file must be XORed with the value “aqvkpjmdofazwf{lqjm1310<”.rule7
: This rule specifies that one of the sections in our binary must have a title of “poophaha”.rule8
: This rule just means that our file must be greater than 1MB and smaller than 2MB in size.
Now that we covered all of the rules, I can explain my solution:
To start, I created 8 different files, each with 200k random bytes from /dev/urandom
.
This command satisfies the entropy requirement of rule5
and the file size requirement of rule8
:
head -c 200000 /dev/urandom > test2 #(repeat for test2 - test9)
Next, I created an object file with a simple main() function, in this main function I called puts to print values that satisfy the three different string rules.
This command satisfies rule2
, rule4
, and rule6
:
echo 'int main() { puts ("jessie james meowth TEAMROCKET somethingsomethingmalware aqvkpjmdofazwf{lqjm1310<"); }' | gcc -x c - -c -o yara.o
After I created the object file, I used objcopy
to add the following sections to the object file, I added 9 sections in total since there is already 31 sections by default in the executable, I verified this through the command readelf -S yara
.
This command satisfies rule3
and rule7
:
objcopy --add-section poophaha=mydata --add-section test2=test2 --add-section test3=test3 --add-section test4=test4 --add-section test5=test5 --add-section test6=test6 --add-section test7=test7 --add-section test8=test8 --add-section test9=test9 \
--set-section-flags poophaha=noload,readonly yara.o yara2.o
Finally, we compiled the data into an executable file using gcc.
This command satisfies rule1
:
gcc yara2.o -o yara
After we complete all these commands, we have our ELF file named yara
, we can put this command into the website from before and successfully defeated all 8 gyms!
Flag: UMDCTF{Y0ur3_4_r34l_y4r4_m4573r!}
Telekinetic Warfare
For this challenge, we are given a gif file: bruh.gif
.
looking at the a still image from the gif we can see that the gif contains QR codes, however they are changing so fast it is impossible to scan them:
We can also view the file size and see that it is 129MB, way larger than any normal gif would be.
My first step in solving this would have to be to extract each of the frames from the gif into a separate image so that we could scan each of the QR codes, I used the following ffmpeg
command to do just that.
I made sure to do this inside of a new directory, since there was likely a large number of QR codes inside the gif:
ffmpeg -i ../bruh.gif newimage%05d.png
Once the ffmpeg command finished running, I looked at the files and there was a total of 14,666 images, and the gif was over 24 minutes long! Definitely not something we can do by hand.
After I extracted this I started by scanning the first QR code to try and get an idea of how I would find the flag. I used zbarimg
to scan the first QR image and return the results:
Hmmm, it seems the QR code is base64 encoded, I tried the same command on a few other QR codes and they all seemed to be base64 encoded.
I piped the output of the zbarimg
command into base64 --decode
and was returned with what looked like the start of a PDF file:
I tried this with a few other files, and from the information that was returned to me, I ultimately assumed that a PDF file was embedded into the gif and base64 encoded.
My first attempt at reconstructing the PDF file was using the zbarimg
command to read each of the QR codes from PDF files and output their result to a new PDF file.
After running the command it seemed to work, however the PDF was corrupted and was missing some data, as well as a flag.
I tried messing around with the resulting PDF for a little but eventually concluded that there was no flag anywhere to be found in the PDF, so my next idea was to script the zbarimg command myself and recreate the PDF using python.
The following is my code for reading out each QR code, decoding them, and then printing the output to a PDF file. You can read through it first and then I will explain my process:
from PIL import Image
from pyzbar.pyzbar import decode
import base64
f = open('flag.pdf', 'wb')
for i in range (1,14667):
if len(str(i))<5:
newi = ('0'*(5-(len(str(i))))) + str(i)
else:
newi = str(i)
img = base64.b64decode(decode(Image.open('./gif/newimage' + newi + '.png'))[0][0].decode())
f.write(img)
print('done')
Code Breakdown
Imports
Image
was imported from PIL and was used to view the image of each QR code and read them in to the programdecode
was imported from pyzbar.pyzbar and was used to decode the QR codes and return the data contained within them.base64
was imported to decode the base64 data that was contained inside each QR code.
Once I had everything imported, I started by using f = open('flag.pdf', 'wb')
to open a new pdf wile with the wb
tag to w
rite b
ytes to the file.
Next I started a loop from 1, 14667 to count from 1 to 14666 for each image that I had.
The if else statement was used to turn the count into a string and verify that it was 5 characters long, which is what the length of the numbers in the QR code image files were.
I then used decode(Image.open())
to read in the data from each image and decode the data contained within them.
I had to add [0][0]
on the end of the decode function to specify which field I wanted, this field contained the decoded data from the QR code.
After this I used .decode()
to change the value into a string since decode()
returned the values as bytes.
Take note of the .
in .decode()
it is different from the imported decode()
that we used previously.
Finally I used base64.b64decode()
to decode the data from base64 into ASCII and wrote that data to the end of the file.
base64decode decodes base64 into bytes, since some ASCII characters have a hard time being decoded from bytes to a string, it was easier to just write data to the file as bytes.
At the end of the file I simply have a print statement that prints “done” so I know once my loop has finished running for all 14,666 QR code images.
After all was said and done I opened the PDF and this was the result!
Flag: UMDCTF{wh0_n33d5_k1net1c_w4rfar3_anyw4ys}
Doctors hate him!!
For this challenge, we are given a zip file titled: Doctors-Hate-Him.zip
.
Once we extract this file we are returned with Doctors-Hate-Him.chm
, which is an MS Windows HtmlHelp Data
file.
Before we attempt to view this file it is also important to note that there was a disclaimer associated with this challenge:
As we can see, the disclaimer tells us that the challenge is designed to emulate live malware, so it is important for us to run it within a VM.
After a bit of research on .chm
files, I found a command that allowed us to extract all of the data from the file: extract_chmLib Doctors-Hate-Him.chm .
from this command we are returned with multiple files, but the most important one is test.html
. When we view this HTML file in our browser we are returned with the following page:
The page definitely looks suspicious, I avoided clicking that button at the bottom of the page and instead viewed the source code to see if I could find out any more information.
One of the first things we see when we scroll down in the source code is a pretty obvious base64 encoded comment, VU1EQ1RGezE5OTdfY2FsbGVkXw==
. If we decode this using base64 we are given part of the flag - UMDCTF{1997_called_
This definitely is not the full flag we need, so I continued looking. In the image above we can also see a very suspicious PowerShell script that seems to execute if we click that button from before.
The payload of the PowerShell command seems to be encoded, if we throw it into CyberChef we can see the actual payload below:
Definitely some sketchy PowerShell payload going on here. However, if we view the last string in the payload we can see 'gurl_jnag_gurve'
.
If we put this into a monolithic substitution solver such as boxentriq, we can decode another part of the flag!
Against my better judgement, the next step I took to finding more parts of the flag was going to the sketchy URL that was inside the encoded PowerShell payload: http://dns-server.online:6969/
.
I made sure not to include the explore.exe
file incase it included something malicious, and I was brought to a server showing 2 files on it:
We can see the explore.exe
file from before, which is likely some type of malware, but we also see a present.txt
file which intrigued me. Viewing present.txt
we can see the following string of data:
U2xpdmVyIHJlYWxseSBkb2VzIHNvdW5kIGxpa2UgYSBwb2tlbW9uLi4uIGFueXdheXMgcGV3IHBldyEgUGFydCAzOiBfbWFsd2FyZV9iYWNrX2Jvem99
Whatever it was, it seemed to be encoded. Even though it doesn’t have the obvious =
on the end as base64 usually would, I determined this string to also be base64 encoded and decoded it into the following message:
Sliver really does sound like a pokemon... anyways pew pew! Part 3: _malware_back_bozo}
With that string decoded, it looks like we finally have all 3 parts of the flag! All we need to do now is put them all together and we have our complete flag.
Flag: UMDCTF{1997_called_they_want_their_malware_back_bozo}
Straight Outta The SCIF
For this challenge, we are given a zip file: sots.zip
.
Extracting the zip file we are given a 15 page long PDF titled top-secret-team-rocket.pdf
.
It took me a while to figure out what to do, at first I converted the PDF to a word document, then extracted data from the word document by converting it to a zip file, and saw hundreds of images of random yellow dots in the media
section:
For a while I wasn’t really sure what to do with these dots, I thought they could be morse code, or even braile, but nothing seemed to work.
After a lot of trial and error, and some digging, I managed to find this writeup that looked awfully similar to what we were trying to do.
I started by using the pdftoppm
command to split each page apart and put them into their own directory:
pdftoppm ../top-secret-team-rocket.pdf pdf -png
Once I had all the pages separated, I used deda to read for any patterns of dots using deda_parse_print
for each PNG image of the pages.
I noticed the value at the bottom printer 00850077
and tried to start decoding the values from decimal using CyberChef.
I started with 85 77
from the image above, then the next page gave me 68 67
and quickly I had the start of the flag format UMDCTF{
.
With this, I knew I was on the right track, I continued decoding each page one by one until I had decoded all 15 pages and put them into CyberChef.
Once I was done entering all the numbers, I decoded the flag and recieved the following from CyberChef!
Flag: UMDCTF{COMMON_TEAM_ROCKET_L}