> UofTCTF 2026 Review + Baby Exfil writeup!

(01/11/2026), 3 minute read ⏰

UofTCTF was a pretty cool event. It was 48 hours long, yet I was basically done after the first 4ish hours. Our team's goal (IRL Anime Girls, yes, that's us) was to complete 1 challenge from each category, which we were able to achieve, and that's all that matters to me. I was our forensics guy and to my DISMAY, there were only two forensics challenge. Baby Exfil, and this jank Pokemon Card theft image analysis one, whose name shall remain forsaken.

I actually completed Baby Exfil and another Crypto challenge, Gambler's Fallacy, but my teammate submitted the flag, since I did not have shared clipboard enabled for my VM (I hate virt-manager/qemu, just use VMWare).

Baby Exfil was not hard at all, and you'll find out why, starting now!


We're given a PCAP file. Any forensic guy/gal knows the drill at this point. Shove it into Wireshark and scroll and/or filter traffic. From first sight, it's a bunch of generic web traffic (tracking websites and all that is terrible in this world). However, if we continue scrolling, we can see that there is some weird web traffic to a remote IP, 35.238.80.16:8000.

A few frames later, we can see a GET request for a very odd Python script, which has its source code completely revealed in the following frame. To put it simply, this script will walk through the user's Desktop directory (linux prevails once more), and grab any files that are .docx, .png, and .jpeg.

It takes each file independently and XORs them with a plaintext key before sending off the hex encoded data to the server. A basic principle of XOR is that XORing with the same key twice will return the original plaintext. Knowing this, we (and I mean ChatGPT) can put together a simple decryption script. All we have to do is load the entire hex blob that is sent off (after each POST request to the server, containing the hex streams that are sent off) into a text file to be decrypted.

def xor_decrypt_from_hex(hex_data: str, key: str) -> bytes:
    encrypted_bytes = bytes.fromhex(hex_data)
    key_bytes = key.encode()
 
    result = bytearray()
    for i in range(len(encrypted_bytes)):
        result.append(encrypted_bytes[i] ^ key_bytes[i % len(key_bytes)])
 
    return bytes(result)
 
key = "G0G0Squ1d3Ncrypt10n"
 
with open("C:\\Users\\lain\\Downloads\\first.txt", "r") as f:
    hex_data = f.read().strip()
 
original_data = xor_decrypt_from_hex(hex_data, key)
 
with open("C:\\Users\\lain\\Downloads\\recovered_file.bin", "wb") as f:
    f.write(original_data)
 
print("File successfully recovered")

From here, we can just follow the TCP streams after/at each POST request sent to the server, and copy/paste the entire encoded blob into our text file to decrypt. Afterwards, we want to take the recovered_file.bin file and turn it into a BMP (bitmap file) to render the recovered data as an image. Doing so for the first POST reveals the following image.


We can follow the second stream as we did with the first and The second stream reveals some Windows 7 desktop. Let's continue to the third stream! I wonder what we're going to get?????

Awesome sauce. Points for the team and mission accomplished!


In the end, our team finished 319th place!

My next post will be some Go reversing, which should be fun! I'll be experimenting w/ GoReSym and showcase a "new" protocol that I'll be following from now on. It'll take some time to come out, since uni is beating me up a bit right now.

Thanks for reading, and have a good day!


cd . cd .. cd ../notes-and-resources cd ~