tanner@home:~$

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!

image

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

No. 352

Fire Type Pokemon Only

YARA Trainer Gym

Telekinetic Warfare

Doctors hate him!!

Straight Outta The SCIF


Mirror Unknown

image

For this challenge, we are given a singular PNG file: Mirror_Unknown.png.

image

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.

image

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:

image

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

image

For this challenge, we are given a singular JPG file: hide-n-seek.jpg. We are also given 2 passwords from reading the description.

image

Passwords:
  1. kecleon (pokemon #352)
  2. 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:

image

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:

image

Flag: UMDCTF{KECLE0NNNNN}


Fire Type Pokemon Only

image

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:

image

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.

image

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:

image

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:

image

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.

image

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.

image

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:

image

Flag: UMDCTF{its_n0t_p1kachu!!}


YARA Trainer Gym

image

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:

image

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
import "elf"
import "math"

rule rule1 {
    condition:
        uint32(0) == 0x464c457f
}

rule rule2 {
    strings:
        $rocket1 = "jessie"
        $rocket2 = "james"
        $rocket3 = "meowth"

    condition:
        all of ($rocket*)
}

rule rule3 {
    meta:
        description = "Number of sections in a binary"
     condition:
        elf.number_of_sections == 40
}

rule rule4 {
    strings:
        $hex1 = {73 6f 6d 65 74 68 69 6e 67 73 6f 6d 65 74 68 69 6e 67 6d 61 6c 77 61 72 65}
        $hex2 = {5445414d524f434b4554}
        $hex3 = {696d20736f207469726564}
        $hex4 = {736c656570792074696d65}

    condition:
        ($hex1 and $hex2) or ($hex3 and $hex4)
}

rule rule5 {
    condition:
        math.entropy(0, filesize) >= 6
}

rule rule6 {
    strings:
        $xor = "aqvkpjmdofazwf{lqjm1310<" xor
    condition:
        $xor
}

rule rule7 {
    condition:
        for any section in elf.sections : (section.name == "poophaha")
}

rule rule8 {
    condition:
        filesize < 2MB and filesize > 1MB
}

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 strings jessie, 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!

image

Flag: UMDCTF{Y0ur3_4_r34l_y4r4_m4573r!}


Telekinetic Warfare

image

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:

image

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.

image

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:

image

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:

image

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 program
  • decode 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 write bytes 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!

image

Flag: UMDCTF{wh0_n33d5_k1net1c_w4rfar3_anyw4ys}


Doctors hate him!!

image

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:

image

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:

image

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.

image

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:

image

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!

image

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:

image

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

image

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:

image

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.

image

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!

image

Flag: UMDCTF{COMMON_TEAM_ROCKET_L}