This page looks best with JavaScript enabled

UMDCTF 2021 Writeups

Hosted by UMDCSEC from 16 April - 18 April

 ·  ☕ 23 min read  ·  🌈🕊️ rainbowpigeon

I appreciate the organizers for putting this CTF together because I enjoyed it a lot. It was at a very appropriate difficulty level for novices. Many thanks to my teammates for helping out. Even though it was only basically 2 active players 🙂, we managed to get 8th place, which I am pretty satisfied with considering our little experience.
🎵 For this CTF I was listening to Kygo - Gone Are The Days ft. James Gillespie!

Graph of challenge solves over time
Category breakdown of challenge solves

Details Links
CTFtime.org Event Page https://ctftime.org/event/1288
Publicly-released challenges https://github.com/UMD-CSEC/UMDCTF-2021-Public-Challenges

Forensics

Phillip 1

Philip was working on a challenge when I took a snap of his vm. I found the flag, and I gonna be the only one to claim this flag since I scrubbed the flag from the raw image. MWAHAHA, more points for me >:)

Note: the dwarf and system map are the same for all Phillip challenges

hxxps://drive.google.com/drive/folders/1c6vdBabGu33edSLXQZY5s8JAeM8au8Uo?usp=sharing

author: drkmrin78

score: 3.5/10

Phillip 1 Google Drive Files

Since we are given a VM snapshot, we will do memory forensics with volatility. I’ve never done this for Linux memory dumps before, so this will be cool. We first make a custom profile with the .dwarf and .map provided as outlined here https://github.com/volatilityfoundation/volatility/wiki/Linux#making-the-profile.
We then use the linux_psaux plugin to list processes along with their full command line.

1
volatility --plugins=custom --profile=Linuxphillipx64 -f philip-1.raw linux_psaux

SSH connection in Linux process list

We see that an SSH connection is made to lubuntu@chals2.umdctf.io with -i meaning an identity file key is used.
With the linux_bash plugin, we also see that SCP was used to copy Phillip’s super-secret-flag onto the remote server.

1
volatility --plugins=custom --profile=Linuxphillipx64 -f philip-1.raw linux_bash

SCP and SSH in Linux bash commands

If we can obtain the SSH identity file key, we can SSH onto the remote server and retrieve the flag.
Thus, we use linux_enumerate_files and grep for key.

1
volatility --plugins=custom --profile=Linuxphillipx64 -f philip-1.raw linux_enumerate_files | grep -i key

SSH key file in Linux filesystem

!! The file is found at /cow/upper/home/lubuntu/key with an Inode value of 0xffff95f1c3139f60. We can now dump it to disk with:

1
volatility --plugins=custom --profile=Linuxphillipx64 -f philip-1.raw linux_find_file -i 0xffff95f1c3139f60 -O private_key

and use the identity file.

If you get an AttributeError: Struct xxx has no member xxx like I did initially, be sure to get the latest volatility from github.
Volatility AttributeError resolved by git cloning latest version

If we were to use the identity file straight away, we will encounter a permissions error. Fix it with chmod 600 private_key and we are good to go.

SSH bad permissions error resolved with chmod 600

And we are done!

Base64-decoding flag

Flag: UMDCTF-{G4ll4gh3r_4_1if3}

Phillip 2

Philip told me he found something to give him ctf flags for Dogecoin? I have to see for myself, but lets just take his!

Note: the dwarf and system map are the same for all Phillip challenges

hxxps://drive.google.com/drive/folders/1r6BGYZsTpfS0pWuIu2fWPjAqalsae6kx?usp=sharing

author: drkmrin78

score: 6/10

Utilizing the same custom profile we made in Phillip 1, we similarly look for processes and see the peculiar Mozilla Thunderbird email client running with process ID 2084:

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-2.raw linux_psaux

Mozilla Thunderbird in Linux process list

We can also check out network connections with linux_netstat to see thunderbird showing up again:

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-2.raw linux_netstat

Mozilla Thunderbird with established network connections in netstat

I was unfamiliar with what kind of files are related to Thunderbird, so let’s see the open file descriptors for its process with linux_lsof.

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-2.raw linux_lsof -p 2084

This showed us various .sqlite databases stored in ~/.thunderbird/<profile_name>/, for which some have their purpose documented here: http://kb.mozillazine.org/Files_and_folders_in_the_profile_-_Thunderbird

global-messages-db.sqlite listed as an open file descriptor for Thunderbird process

Thunderbird profile configurations and databases listed as open file descriptors

At first, I tried dumping the global-messages-db.sqlite and viewing its contents. While it does show us that there is an Invoice.zip and a password of bigchungus4life, my research apparently suggested that the actual attachment was not stored together in this SQLite database.

Thunderbird email messages retrieved from SQLite database

I found this Thunderbird Email Parser https://github.com/mdegrazia/Thunderbird-Email-Parser which seemed to be able to retrieve the actual attachment data but it required the entire Thunderbird user profile folder as an input argument. With this, I decided to write a quick & dirty Python script to dump all our needed files with volatility.
First, I retrieved the filepaths we needed under ~/.thunderbird/<profile_name>/ by copying-and-pasting the output of

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-2.raw linux_enumerate_files | grep thunderbird

into thunderbird.txt, which will end up looking something like this:

List of Thunderbird-related files retrieved from enumerate_files

Second, we make the output directories we need with mkdir -p.
Finally, we run the below script to dump the files:

1
2
3
4
5
6
7
8
import subprocess
with open("thunderbird.txt", 'r') as f1:
  contents = f1.read().split('\n')
for f in contents:
  inode = f.split(' ')[0]
  path = f[f.find('/'):]
  path = '\'' + path + '\''
  subprocess.run("./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-2.raw linux_find_file -i {} -o {}".format(inode, path), shell=True)

And then now we can run thunderbird_parser_v1.0.py specifying the Thunderbird user profile folder:

1
thunderbird_parser_v1.0.py -d "/cow/lubuntu/.thunderbird/<profile_name>" -o report

which will give us a nice spreadsheet as well as individual .eml files.

Thunderbird emails displayed in spreadsheet format

We are now able to carve out our Base64-encoded (specified by Content-Transfer-Encoding) invoice.zip file from UMDCTF Invoice_5.eml, decode it back into a .zip, and open the invoice.pdf within using the password we found earlier of bigchungus4life.

Extracting base64-encoded invoice.zip attachment via a hex editor

Decoding base64 to zip file using CyberChef

Unlocking zip file with password

Flag displayed in Invoice PDF

Flag: UMDCTF-{M3g4_Ch4#g4$}

Phillip 3

Philip was starting to write a vault for flags? I don’t know what that means, but he told me its going to be secure from my memory forensic antics. Total rubbish, lets show him who’s l33t here!

Note: the dwarf and system map are the same for all Phillip challenges

hxxps://drive.google.com/drive/folders/1tRYkwA-gyuAkqF5W8kVjAFKrtGRqVjPx?usp=sharing

author: drkmrin78

score: 7/10

Standard procedure is to list processes with linux_psaux, and flag-vault with process ID 9793 catches our eye.

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_psaux

Linux process list shows flag-vault program

linux_bash shows us that flag-vault was compiled from flag-vault.c.

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_bash

Compilation of flag-vault.c shown in Linux bash commands

Let’s grep for these files and dump them for inspection.

1
2
3
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_enumerate_files | grep -i -e vault -e flag
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_find_file -i 0xffff95f1db55b310 -O flag-vault
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_find_file -i 0xffff95f1c33f43f0 -O flag-vault.c

Search for flag-vault and flag-vault.c with enumerate_files

Source code of flag-vault shows that console input is stored in a linked-list data structure where each node contains a char.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

struct link {
    char c;
    struct link *next;
};

void add_link(char c, struct link *list) {
    struct link *curr = list;
    struct link *new_link = malloc(sizeof(struct link));

    new_link->c = c;
    new_link->next = 0;

    while (curr->next != 0) curr = curr->next;

    curr->next = new_link;
}

int main() {
    struct link head = {0};
    char c;

    while ((c = getc(stdin)) != EOF) {
        add_link(c, &head);
    }
}

Since malloc is used to dynamically allocate memory for each node (a.k.a link, line 11), this data will be stored on the heap. To locate the memory address of the heap, we use linux_proc_maps for the flag-vault process:

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_proc_maps -p 9793

Process memory mapping for the flag-vault process

We can now dump the heap with [heap]’s Start memory address and eyeball the flag directly from the dumped data:

1
./volatility/vol.py --plugins=custom --profile=Linuxphillipx64 -f philip-3.raw linux_dump_map -p 9793 -s 0x000055e46125e000 -D dump_map/

Flag text stored in the heap as linked list nodes

Flag: UMDCTF-{V0l$h311_isCool}

Protocol One and Zero

We have noticed some suspicious pings on localhost that appear to hide a message. Can you recover the message?

author: azeemsm

score: 4/10

Attached: protocol_one_and_zero.pcapng

We are given a network capture so let’s open it in Wireshark. The capture contains a bunch of ICMP ping requests and replies so let’s just filter for only requests since replies should just echo back the same data payload contained in requests.
So with the display filter icmp.type == 8, notice that the data transmitted usually either ends in a whole bunch of 00s or ffs.

Ping requests with data ending in 0s in Wireshark

Ping requests with data ending in fs in Wireshark

This seems to be a form of binary encoding (as also alluded to by the challenge title), so let’s extract that data with pyshark via a simple script and we get the flag:

1
2
3
4
5
6
7
8
9
import pyshark
cap = pyshark.FileCapture('protocol_one_and_zero.pcapng', display_filter='icmp.type == 8')
binary = ""
for packet in cap:
    if packet.icmp.data[-1] == "0":
        binary += "0"
    elif packet.icmp.data[-1] == "f":
        binary += "1"
print(binary)

Running python script outputs binary data

Decoding binary data in CyberChef gives flag

Flag: UMDCTF-{b1n_p1Ng_P0ng}

Steganography

Coldplay’s Flags

I just downloaded Coldplay’s latest song and noticed that my song file seems a bit odd. Can you help me figure out what’s up with my file?

Note: For the password, think words, not characters

hxxps://drive.google.com/drive/folders/1o58KD8CcCI5kTuHH8e56noPUAazRBGNh?usp=sharing

author: amanthanvi score: 5/10

We are given flags.wav which is audio of the song Coldplay - Flags except that its spectrogram differs slightly from the official copy. I attempted to do phase inversion and ran various steg tools but it proved to be futile.

I decided to randomly try binwalk on it and hey look, we obtain two .zip files with one containing hint.txt and the other being encrypted and holding flag.txt.

binwalk extracts zip and txt files from flags.wav

hint.txt appears to tell us that the password is in some format with some strange numbers.

1
2
Assume all characters are lowercase
password: (1:49)_(1:01)_(0:16)_(0:56)_(0:17)_(1:33)_a_(1:34)?

This should refer to the password required for the encrypted .zip with the flag. Let’s carve out the encrypted .zip portion from 246D04E.zip with a hex editor by deleting the second .zip that follows after, and we can confirm that it is indeed password-protected. This can also be done by binwalk that we used earlier.

Extracting zip file via hex editor

Zip file requires password

This is where I got stuck. I thought the numbers indicated some kind of charset or string length, but fortunately my teammate TheMythologist immediately identified them as timestamps, which suddenly made a lot of sense because we were given a song file. The logical thing to do was to listen to the song lyrics at each timestamp and identify the words to be used to form the final password. This sounded simple until we realized that the lyrics did not align nicely with the timestamp boundaries and we couldn’t get the right password. My other teammate fawl thus made this beautiful python script to generate some possible word combinations based on the lyrics:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def get_words(a, b, c, d, e, f, g):
    return f"{a}_{b}_{c}_{d}_{e}_{f}_a_{g}?"

a_combs = ["can", "you", "show", "me", "how"]
b_combs = ["pyotr", "tchaikovsky", "to"]
c_combs = ["the", "talk", "among"]
d_combs = ["to", "be", "whatever"]
e_combs = ["among", "the", "skeletons", "this"]
f_combs = ["telephoning","by", "a", "ouija"]
g_combs = ["by", "a","ouija"]

cursed = []

for a in a_combs:
    for b in b_combs:
        for c in c_combs:
            for d in d_combs:
                for e in e_combs:
                    for f in f_combs:
                        for g in g_combs:
                            cursed.append(get_words(a, b, c, d, e, f, g))


with open("words.txt", "w") as f:
    for word in cursed:
        f.write(f"{word}\n")

Password combinations generated from python script

I added some more words to the list because it still initially didn’t work, used zip2john to create a hash from the encrypted .zip and used hashcat to bruteforce based on our generated wordlist.

1
zip2john 0.zip | cut -d ':' -f 2 > hashes.txt
zip2john converts zip file to hash format
1
hashcat -m 17210 pkzip.txt words.txt

Cracking zip file password with hashcat

We get the password can_tchaikovsky_talk_to_skeletons_by_a_ouija?, which makes sense because it wasn’t nonsensical like the other combinations we generated. And so we get flag.txt from the .zip!

Flag: UMDCTF-{PY07r_11Y1CH_7CH41K0V5KY}

OSINT

Nikolai 1

My friend Nikolai went off to school in Wyoming this year and he hasn’t been responding to my texts. I’m worried about him since he has no family or friends out there. Can you find out what he has been up to?

author: matlac

score: 4/10

Google searches for these kind of challenges usually don’t give you anything, so I guess the way is to just search on various social media platforms. I knew beforehand that Twitter could filter tweets based on location, so let’s try that with nikolai near:"Wyoming"

Searching for tweets near Wyoming reveals Nikolai

Those tweets are relatively recent and the tweeter does sound like someone who has gone off the radar. Looking at @MercatorNikolai’s older tweets, we see that he has been using and contributing to OpenStreetMap.

Nikolai tweets about his discovery of OpenStreetMap

He indicates that his OSM account is linked in his bio, but this information was later removed due to apparent trolls. We can use the Wayback Machine at https://archive.org/web/ to see if any past webpages with his OSM account link was archived.

Wayback Machine archived pages of Nikolai's Twitter

Checking out the snapshot on Mar 25 which is before he cleaned his bio, we find the original link to his OSM account https://openstreetmap.org/user/NikolaiM99 where the flag resides.

Nikolai's OpenStreetMap link in his archived Twitter bio

Flag displayed in Nikolai's OpenStreetMap profile

Flag: UMDCTF-{4_m4pp3r5_p4r4d153}

Nikolai 2

Nikolai’s school recently contacted his parents since he hasn’t been seen in his dorm or classes for over a week. They know you are an OSINT CHAD and asked for your help. Can you figure out where he ran off to?

Flag Format: UMDCTF-{20_W_34th_St_New_York_NY_10001}

P.S. If you enjoy this challenge you might also enjoy https://www.tracelabs.org/

Hint: All of the accounts you found in Nikolai 1 may be important for this solve

Warning, you only have five guesses

author: matlac

score: 10/10

With Nikolai’s changesets contributed on OSM, we are basically able to trace the trail he took: he went southwards from Laramie to Colorado Springs. This also checks out with his OSM diary indicating his travel to Colorado.

Nikolai's OpenStreetMap changesets show his trail

Nikolai's OpenStreetMap diary entry on his travel to Colorado

Let’s retrieve the coordinates of 38.8920,-104.8146 of his latest changeset 102759933 and plug it into Google Maps for friendlier indication of nearby amenities and features.

Nikolai's most recent OpenStreetMap changeset coordinates

Coordinates from OpenStreetMap shown in Google Maps

Remember that according to his tweets,

  • there is a gas station within walking distance
  • there is a Domino’s within walking distance
  • there is a chair outside his room with no balcony
  • he stays at a lodge/motel

Nikolai's tweets containing location hints

Consolidating those bits of information, we can easily identify the place he is staying to be Aspen Lodge. We see the chair outside the room and the Domino’s Pizza nearby.

Location found on Google Maps

Flag: UMDCTF-{3990_N_Nevada_Ave_Colorado_Springs_CO_80907}

Vacation

My mom told me she went to this amazing brewing company in the Carribbean and when I asked her the name of the place, she sent me a picture of her ship. Can you help me find the name of this brewing company?

Flag format: UMDCTF-{City_Companyname}

Note: Companyname = first part of the company name

hxxps://drive.google.com/drive/folders/14eh2f13z32Uf7WFVNHWqHGnXE0P8y8G-?usp=sharing

author: itsecgary

score: 5/10

Cruise ship image in Google Drive

Zooming in we can make out the words “Rum Therapy” for the establishment in front of the Royal Carribean ship. Googling that, we get an image from Tripadvisor of the same location with the iconic ship which denotes the place to be “Rum Therapy Bar and Treatment Center, St. Lucia”

Google search for `Rum Therapy` shows matching Tripadvisor result

We put that into Google Maps and we can see that the brewery next to it is the Antilla Brewing Company.

Antillia Brewing Company beside Rum Therapy Bar & Treatment Centre in Google Maps

Flag: UMDCTF-{Castries_Antillia}

Justin 2

My friend is in danger and this was the only picture he could send me. Can you find the name of the street he is on? ex. UMDCTF-{Memory_Lane}

hxxps://drive.google.com/drive/folders/1nfFmXuEMRuCOY6BvAAox1AWQPSCqgIhn?usp=sharing

author: itsecgary

score: 5/10

Car dashboard camera capture of a street in Google Drive

In the image we can see Cyrillic characters on the signage to the right as well as a vehicle registration plate number of A686KY154. The 154 at the end turns out to be the regional code for Novosibirsk Oblast.

Wikipedia table for vehicle registration plate region codes in Russia

From here I struggled with reverse-image-searching to find the location, so I eventually had the idea to try to identify the unique tall structure depicted in the center. I used the superior yandex.ru’s image search and cropped out the street and cars which would only have polluted our results with noise. I also specified “Novosibirsk Oblast” as an additional search term.

Reverse image search using Yandex.ru with cropping

After much trawling through the results, I found what seemed to be the same structure! The linked post from where the image originates points us to the ice skating rink at Spartak stadium in Novosibirsk.

Image search result in Yandex.ru shows similar structure

Linked post on Smartik.ru says structure is located at the Spartak stadium, Novosibirsk

By going to Spartak stadium in Google Maps and circling around the perimeter of the stadium in Street View, we identify the street in the picture to be Ulitsa Kamenskaya.

Street around Spartak stadium is named Ulitsa Kamenskaya as shown in Google Maps

Exact location of Google Drive image found on Google Maps Street View

Flag: UMDCTF-{Ulitsa_Kamenskaya}

Justin 3

Justin only gave us this camera’s live feed and said “Hey guys look! I’m on TV!” Can you find the name of the street he’s on?

format: UMDCTF-{Memory_Lane}

we do not own any IP found besides the one listed below

curl hxxp://chals5.umdctf.io:8000

author: itsecgary

score: 6/10

We are given a webpage with an embedded streetcam livefeed. Checking out the HTML source, we see that this livestream is coming from http://136.169.226.46/1589793911/embed.html?token=xxxxx through the iframe’s src attribute.

HTML source code of challenge website with live cam feed

Let’s search up the IP address on Shodan.io, which tells us that it is related to something called Ufanet.

Shodan.io's information on IP 136.169.226.46

Googling that, we find http://maps.ufanet.ru which essentially shows you a map of all public livecams that the organization Ufanet owns.

Google search results for `ufanet camera`

Camera map by ufanet

Using the identifier 1589793911 from the iframe src URL we saw earlier to construct http://maps.ufanet.ru/ufa#1589793911 , we are able to view the specific previously-embedded livefeed on the official site! This gives us the official camera name of “Остановка Советская площадь Камера 2”.

Same live cam feed from earlier displayed on ufanet official site

We can then plug in the location stated in the camera name into Google Maps (2 locations are returned, but the other Sovetskaya Square result is not in Ufa but in another city), and visually cross-reference our location in Google Maps with the map provided at http://maps.ufanet.ru/. The “Smart stops” filter is used for ufanet’s map to show us bus stops.

Google Map search results for the live cam feed's location name in Russian

Ufanet map with Smart Stops filter turned on in the same location

We then now turn on the “Camera” filter to show us the various cameras for these bus stops around “Soviet Square” and we can find the exact one towards the west. Looking back at Google Maps, the street that it lies on is Ulitsa Pushkina.

Location of camera found in ufanet map with Camera filter turned on

Flag: UMDCTF-{Ulitsa_Pushkina}

MemeCTF 2 Electric Boogaloo

9c222530fc5822720b24a18e0c5200957fcbe169589915c963e765c99c7f5f3a

author: t0pc4r

score: 5/10

We google the given hash/hex data and find a Github repository by UMD-CSEC themselves.

Searching challenge hash on Google returns 1 Github result

Github repository with long name and challenge hash in description

In ./old_files/data/ we can see what’s reminiscent of files inside a typical .git directory.

.git directory files and folders in /old_files/data

After downloading all these files, we can use pigz to deflate the objects under ./old_files/data/objects (in my screenshot I renamed the data folder to .git). One of the objects contains the flag.

Using pigz to deflate .git objects shows blob with flag text

Flag: UMDCTF-{f0r_th3_m3m3}

Misc

ChungusBot

I wonder if ChungusBot has the flag.

Make sure to join the discord for the challenge!
hxxps://discord.gg/dDnQncKqzV

author: itsecgary

score: 3/10

ChungusBot's Discord status says 'Check out my code'

Since ChungusBot#0421’s status tells us to check out their code, I tried searching Github and was successful.

Github search for ChungusBot returns 1 result

To summarize, ChungusBot consists of a main chungus.py and 4 extensions under ./cogs/:

  • chungusboi.py
  • chungy.py
  • flag.py
  • gagagaga.py

Each of these extensions are responsible for handling a command sent via DM to the bot and performing some actions or checks. To retrieve the flag, commands need to be sent in a sequence and within a time limit.
First, chungusboi.py takes in a kyle command and writes the current time tick count with a newline character to a user-specific file. Current newline character count == 1.

1
2
3
4
5
6
    @chungusboi.command()
    @in_dms()
    async def kyle(self, ctx):
        tic = time.perf_counter()
        with open(f'userfiles/{ctx.author.name}_time', 'w') as f:
            f.write(f'{tic}\n')

Second, flag.py takes in a yeeee command and checks if there has been 1 newline character already written into the user-specific file. If it does, it will append the current time tick count with another newline character into the same user-specific file. Current newline character count == 2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    @flag.command()
    @in_dms()
    async def yeeee(self, ctx):
        tic = time.perf_counter()
        try:
            f = open(f'userfiles/{ctx.author.name}_time', 'r')
            stuff = f.read()
            count = len(stuff.split('\n'))

            if count == 2:
                f = open(f'userfiles/{ctx.author.name}_time', 'a')
                f.write(f'{tic}\n')
            else:
                await ctx.channel.send("Uh ohhh (count not 2)")
        except:
            await ctx.channel.send("Uh ohhh (no file)")

Third, chungy.py takes in a mcchungus command and checks if there are 2 newline characters already written into the user-specific file. If it does, it will use the same process as earlier to append data. Current newline character count == 3.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    @chungy.command()
    @in_dms()
    async def mcchungus(self, ctx):
        tic = time.perf_counter()
        try:
            f = open(f'userfiles/{ctx.author.name}_time', 'r')
            stuff = f.read()
            count = len(stuff.split('\n'))

            if count == 3:
                f = open(f'userfiles/{ctx.author.name}_time', 'a')
                f.write(f'{tic}\n')
            else:
                await ctx.channel.send("Uh ohhh (count not 3)")
        except:
            await ctx.channel.send("Uh ohhh (no file)")

Fourth, gagagaga.py takes in a chungusisgod command and checks for 3 newline characters already present in the user-specified file. If it does, it will use the same process as earlier to append data. Current newline character count == 4.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    @gagagaga.command()
    @in_dms()
    async def chungusisgod(self, ctx):
        tic = time.perf_counter()
        try:
            f = open(f'userfiles/{ctx.author.name}_time', 'r')
            stuff = f.read()
            count = len(stuff.split('\n'))

            if count == 4:
                f = open(f'userfiles/{ctx.author.name}_time', 'a')
                f.write(f'{tic}\n')
            else:
                await ctx.channel.send("Uh ohhh (count not 4)")
        except:
            await ctx.channel.send("Uh ohhh (no file)")

Fifth, gagagaga.py also takes in a giveittome command which calculates the time difference between the time tick count in the 1st step and that in the 4th step, and checks if this time difference is less than 15. So if we have sent our sequence of commands within 15 seconds, the bot will then send us the flag.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    @gagagaga.command()
    @in_dms()
    async def giveittome(self, ctx):
        try:
            f = open(f'userfiles/{ctx.author.name}_time', 'r')
            stuff = f.read()
            head = stuff.split('\n')[0]
            tail = stuff.split('\n')[3]

            if float(tail) - float(head) < 15:
                await ctx.channel.send('`' + flag + '`')
                await ctx.channel.send(file=discord.File("morris.PNG"))
                f = open(f'userfiles/{ctx.author.name}_time', 'w')
                f.write("")
        except:
            await ctx.channel.send("Uh ohhh (no file)")

No scripting is needed because we can send fast enough manually.

Direct-messaging ChungusBot on Discord with correct commands to get flag

Flag: UMDCTF-{I_th1nk_y0u_4r3_th3_b0t_n0w_sh3333333333sh}

John’s Return

I received this network traffic from John, but I don’t what he’s trying to say? Can you figure it out?

author:t0pc4r

score: 7/10

Attached: received.pcapng

Open the .pcapng file in Wireshark and we can see a capture of some Wireless 802.11 traffic. At the beginning, we can spot that there is the 802.11 authentication process with a EAPOL 4-way handshake for WPA-PSK. After that, data sent is encrypted. This documentation was very helpful in familiarizing me with the protocol: https://www.cisco.com/c/en/us/support/docs/wireless-mobility/80211/200527-Fundamentals-of-802-11-Wireless-Sniffing.html#anc60

802.11 Wireless traffic with encrypted data in Wireshark

Since we have the 4-way handshake captured, we just need to know the SSID name and the passphrase for the WLAN to decrypt the traffic. This is mentioned further down in the documentation linked above: https://www.cisco.com/c/en/us/support/docs/wireless-mobility/80211/200527-Fundamentals-of-802-11-Wireless-Sniffing.html#anc63. The SSID is known to be linksys as shown in packet #1. To bruteforce the password, we can use hcxpcaptool from hxctools to convert received.pcapng into a format for hashcat to crack.

1
2
apt install hcxtools
hcxpcaptool -E essidlist -I identitylist -U usernamelist -z output.16800 received.pcapng

hcxpcaptool used to convert pcapng to hash

Run hashcat with the standard rockyou.txt wordlist to crack the password as chocolate

1
hashcat -m 16800 output.16800 rockyou.txt

hashcat used to bruteforce the WLAN password

We can now specify this password chocolate to be used as a WPA decryption key in Wireshark’s Preferences, with key-type wpa-pwd.

Add WLAN password as WPA Decryption Key in Wireshark Protocol Preferences

The plaintext is our flag :)

Decrypted Wireshark traffic data contains plaintext flag

Flag: UMDCTF-{wh3r3_j0hn}

Satelite

My friend from Russia sent me this message, but all I see are cryllic characters?

author: itsecgary

score: 2.5/10

Attached: message.txt

We are given a text file which appears to be filled with the famous Lorem Ipsum text as Cyrillic characters. Based on the tone of uncertainty in the challenge description’s question, I hypothesized that there may be non-Cyrillic characters hidden within the ocean of text. I used a simple regular expression of [^a-zA-Z0-9\-_{}]+ to remove all characters that do not belong to the UMDCTF{} flag format. As suspected, what remained was the flag.

All Cyrillic characters highlighted with regex in Sublime Text editor

Flag text after Cyrillic characters removed

Flag: UMDCTF-{RIP_sPutN1k_1962}

Web

The Matrix

Oh no! It looks like the robots have taken over! Infilitrate their site and save the world! But beware, they only allow one of them to access it.

curl hxxp://chals5.umdctf.io:4000

author: t0pc4r score: 3/10

The Matrix webpage

If we visit /the-matrix, we are redirected to /403 which gives us an error message that the page is for robots only.

GET request sent to /the-matrix in Burp Suite returns 303 redirect to /403

GET request to /403 in Burp Suite returns 'This page is for robots only'

My first thought is that this could be something related to manipulating the User-Agent HTTP header field to make us appear as if we were a bot.
With that idea, I used Burp Intruder to send requests to /the-matrix with different User-Agent values.

Setting Burp Intruder Payload Options to create simple list from user-agents

Payloads like WordChampBot successfully give us the flag (I chose this because it is the shortest).

User-Agent value WordChampBot gives flag in HTTP response

Flag: UMDCTF-{r0b0t_r3b3ll!0n}

The Matrix Reloaded

Good job, you got rid of the robots. Bad news is that it looks like they fled to Iceland and have even higher security! Can you infilitrate >their site? But beware, they only allow one of them to access it.

curl hxxp://chals5.umdctf.io:4001

author: t0pc4r

score: 4/10

Setting Accept-Language to is which is the country code for Iceland allows us to access the page and retrieve the flag.

Setting Accept-Language for icelandic in Burp Suite gives flag in HTTP response

Flag: UMDCTF-{1c3l@nd1c_d0m1n@t!0n}

The Matrix Revolutions

The robots are done with your tricks. They’re sick of hiding in plain site. Now they’re going to hide in the shadows so no one, not even you can find them. You’ll have to find how their site works. I have a feeling there’s more to it than what we can see.

curl hxxp://chals5.umdctf.io:4002

author: t0pc4r score: 5/10

The flag is supposedly somewhere hidden, so I used Burp Suite’s directories and files scanner to bruteforce for content.

Burp Suite's 'Discover content' engagement tool

Funnily enough, hidden request paths in the form of incrementing numbers were discovered. Each response seems to contain one character of the flag.

Valid hidden directories named as incrementing numbers

We can save the server responses, extract it by removing content matching the basic regex of .+:.+|\n|HTTP.+ and we are left with the flag.

Save server responses from Burp Intruder results

Regex to remove HTTP headers from flag characters in the saved responses opened in Sublime Text editor

Flag text remaining

Flag: UMDCTF-{r0b0t5_43v3r}

IOT Project

IoT is so cool and secure! Check out my project from April 7 at hxxps://azeemsm-umdctf.github.io/

author: azeemsm

score: 5/10

Azeem's Github blog page describing his doorbell IoT project

Azeem should have a Github for his projects since he has a github.io page.

Azeem's Github repository containing code for his doorbell IoT project

In his repository, there is a short piece of Arduino code that simply prints ring! to the serial port upon a doorbell ring. Nothing special.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const int bellPin = 6;
void setup() {
  pinMode(bellPin, INPUT);
  Serial.begin(9600);

}

void loop() {
  int bellState = digitalRead(bellPin);
  if(bellPin){
    Serial.println("ring!");
  }

}

The more important script is program.py. A secret key is read from a file (line 8) and then used to send a POST request to an IFTTT Webhook endpoint /ring2/ each time the doorbell rings (line 25).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#Azeem's Code
import serial
import time
import requests

#Read secret key from file for security!
f=open("maker.key","r")
secret_key=f.read().strip()

#Connect to arduino through serial (mine is on /dev/ttyUSB0 but this may change depending on which port I plug into)
arduino = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
print("running...")

#Never stop!!!
while True:
    try:
        #read from arduino
        data=arduino.readline()
        if data:
            #decode the data as UTF-8 (normal text) and remove new line
            message = data.decode('UTF-8').rstrip('\n')
            if "ring!" in message: #check if it says ring! (not sure what else it could be at the moment looking at the arduino code)
                #send request to webhooks so ifttt can do its thing!
                #send post request through ifttt webhook (this will send me email)
                r=requests.post("https://maker.ifttt.com/trigger/ring2/with/key/"+secret_key)
                print(r.text)
                
    except:
        #there was an error uh oh lets exit
        arduino.close()
        break

Commit history showed that the secret key file maker.key was originally uploaded to the repository with the value of iWEKorMwH5rgGjyHb4jzedP9m9LA7yNkTZ0dvfzDSUO, and the POST request was originally sent to the /ring/ endpoint where we could also specify the email address to receive from the IFTTT Webhook via the value1 parameter.

Github commit showing plaintext maker.key

Github commit showing removal of plaintext maker.key and modification of IFTTT ring endpoint

We try triggering this old /ring/ endpoint and supplying an email of our own, and we successfully get an email with the flag.

1
curl.exe https://maker.ifttt.com/trigger/ring/with/key/iWEKorMwH5rgGjyHb4jzedP9m9LA7yNkTZ0dvfzDSUO -X POST -i -d "value1=xxx@xxx.xxx&value2=xxx"

HTTP response when curl.exe in Powershell used to send POST request to ring endpoint

Flag sent to our specified email address

Flag: UMDCTF-{g!t_h00k3d}

IOT Project 2

Yeah im such a good engineer, got another project in just under a week later on the 13th. My resume is going to look so good with all these projects! Check out my project at hxxps://azeemsm-umdctf.github.io/ (don’t get the account suspended or there will be disqualifications)

author: azeemsm

score: 6/10

Azeem's Github blog page describing his tweeting toaster project

In the source code of the .github.io blog, it is mentioned that there are extra notes in robots.txt. These notes hint that the parameters value1=normal text and value2=hashtag are used for the toaster to tweet.

HTML source code comment in blog page says there are notes in robots.txt

Content of robots.txt provides toaster's tweeting parameters

Keeping that in mind, I just searched Twitter for "normal text", sorted by Latest and it returned a pretty conspicuous account. The account’s bio had the flag in hexadecimal format.

Twitter search result for `normal text` shows weirdly-long-named account

Suspicious hexadecimal data found in Twitter bio

Decoding hexadecimal data in CyberChef to produce flag

Flag: UMDCTF-{tw3et!ng_4nd_t0@st1ng}

Return of the Flag Bay

We loved the flag bay so much, that we decided to bring it back. Although, something is a bit different about this one. We can’t figure out which username and password is correct from the list of users. Can you help us figure it out?

curl hxxp://chals5.umdctf.io:4004

author: WittsEnd2

score: 7/10

Return of the Flag Bay login page

A login page means that there could be SQL injection vulnerabilities, so let’s test some payloads.
Sending username='; S gives an SQL error, suggesting that the SQL SELECT statement used is vulnerable.

MySQL syntax error response with malformed username input in Burp Suite

MySQL syntax error reflects back our input

Sending username=';''A OR '1'='1'; showed that the OR operator was filtered out. This is fine though, because we can use || in place of OR since it is not blacklisted and removed.

'OR' keyword input disappeared from error output in Burp Suite

Pipe character '|' in input does not disappear from error output in Burp Suite

Thus, we can do an authentication bypass with username='|| '1'='1';# , which succeeds because '1'='1' is always true and the password check condition is commented out with #. The flag is returned Base64-encoded.

Malicious username input bypasses authentication and gets base64-encoded as response in Burp Suite

Base64-decoded flag

Flag: UMDCTF-{84y_w425_3p150d33_23v3n93_0f_7h3_f149_84y}

Top of the Charts

I found this site which seems to boast about itself too much. It claims to be above everything else but I’m not so sure. I think the success has gone to its head.

curl hxxp://chals5.umdctf.io:4003

author: t0pc4r score: 5/10

Sending a HEAD request specified with -I for curl.exe gets you the flag. It’s a guessy challenge for those who do not already have prior knowledge of the different possible HTTP request methods - they wouldn’t be able to pick up the hint in the challenge description.

Flag in HTTP Header response when HEAD request is sent with curl.exe in Powershell

Flag: UMDCTF-{h3@d1ng_t0w@rd5_th3_l1ght}


Thanks for reading!

Share on

rainbowpigeon
WRITTEN BY
rainbowpigeon
OSCP | OSED | Burp Suite Certified Practitioner