Post

CAT CTF 2026 Writeup

CAT CTF 2026 Writeup

Phantom Extraction

“I know that a rev guy created this challenge, I just can’t prove it”

Category: Forensics / Network
Tools: Wireshark, CyberChef


First Look

We get a traffic.pcap file. Opening it in Wireshark, three protocol types show up immediately:

  • ICMP — 421 packets, all with the payload ping_noise
  • UDP — 478 packets with random-looking binary data
  • DNS — 541 packets, all querying subdomains of tunnel.local

The ICMP and UDP traffic is an obvious decoy. Everything has ping_noise as the payload or looks like random garbage — it’s clearly crafted noise meant to distract. The DNS traffic is where things get interesting.


Spotting the Tunnel

Filtering with:

1
dns && dns.flags.response == 0 && ip.src == 192.168.1.50

Every single DNS query follows the same pattern:

1
2
3
0.53454c4602010100...tunnel.local
1.00004000000000...tunnel.local
2.1f001e0006000000...tunnel.local

image

The structure is: [index].[hex_data].tunnel.local

541 queries, each carrying 60 hex characters as a subdomain, sent in order from index 0 to 540. This is DNS tunneling — encoding binary data inside DNS queries to exfiltrate it through a firewall. DNS is almost never blocked, which makes it a stealthy exfiltration channel.


Reassembling the Data

In CyberChef, I used the exported CSV from Wireshark with this recipe:

  1. Regular Expression — extract the hex chunks:
    1
    
    (?<=\d\.)[0-9a-f]{60}(?=\.tunnel)
    

    image

    Output: List matches

  2. Find / Replace — merge all lines into one continuous hex string:
    • Find: \n (Regex ON)
    • Replace: (empty)
  3. From Hex

Output: a 16,216 byte binary blob.


Identifying the Binary

The first four bytes of the output are 53 45 4C 46 — which in ASCII reads SELF.

That’s not a coincidence. A real ELF binary starts with 7F 45 4C 46 (\x7fELF). The challenge author swapped the first byte from 0x7F to 0x53 to obscure the file type. This is what the challenge description was hinting at — “I know that a rev guy created this” — it’s an ELF binary, a reverse engineering artifact, hidden inside a network forensics challenge.

Fixed it in CyberChef with Find / Replace:

  • Find: \x53\x45\x4c\x46 (at position 0)
  • Replace: \x7f\x45\x4c\x46

Now it’s a valid 64-bit x86-64 Linux ELF (shared object, compiled with GCC on Ubuntu 13.3.0).


Static Analysis

Added a Strings operation (minimum length 6) and scrolled through the output. Two things stood out immediately:

The source filename:

1
print_flag.c

This tells us exactly what the binary does — it prints a flag. And based on the filename it probably does it through some kind of encoding or encryption.

The XOR key: image

1
my_super_long_key_that_is_definitely_longer_than_the_message_123456

A 67-character key sitting in the .rodata section. That’s long on purpose — it’s longer than the message it encrypts so every byte of the ciphertext gets XORed with a unique key byte (no key cycling).


Finding the Ciphertext

Switched from Strings to To Hex in CyberChef and searched for the key in its hex representation:

1
6d795f737570657

(That’s just “my_super” in ASCII converted to hex — enough to locate it uniquely.)

Right after the key ends there’s a block of 00 00 00 00... null padding, then 35 bytes of non-printable data, then 47 43 43 3a which is GCC: in ASCII — the start of the compiler version string.

Those 35 bytes between the null padding and GCC: are the ciphertext:

1
2e380b350e450b416b07163154270d54152b065c15456f0746005413551b310d1a1611

The binary’s job at runtime was simple: load the key, load these 35 bytes, XOR them together, and call puts() to print the result. We’re just doing that step manually.


Decryption

Fresh CyberChef tab:

Input:

1
2e380b350e450b416b07163154270d54152b065c15456f0746005413551b310d1a1611

Recipe:

  1. From Hex
  2. XOR
    • Key: my_super_long_key_that_is_definitely_longer_than_the_message_123456
    • Key format: UTF-8
    • Scheme: Standard

Output:

1
CATF{5n34ky_3xf1ltr4t10n5_0v3r_dns}

Summary

The attack chain the challenge author built was actually pretty clever:

1
2
3
4
5
6
7
Flag (plaintext)
  └── XOR encrypted with a long key
        └── Stored in .rodata of an ELF binary
              └── ELF magic byte tampered (7f → 53)
                    └── Binary hex-encoded and split into 60-char chunks
                          └── Sent as indexed DNS subdomains to tunnel.local
                                └── Hidden inside a pcap with ICMP + UDP noise

Three disciplines in one challenge: network forensics to spot the DNS tunnel, data carving to reassemble the binary, and basic reverse engineering to extract and decrypt the flag. The hint in the description was pointing directly at the ELF the whole time.


The Guy in the Video

“I can’t recall what the guy in that video was saying, and it’s pretty important. So, can you help?”

Category: Network Forensics
Difficulty: Easy
Tools: Wireshark, tshark, ffplay


First Look

We get a twitch.pcap file. The name alone feels like a hint — Twitch, as in the streaming platform. Opening it in Wireshark, we’re immediately looking at 12,703 packets. There’s no way to read through those one by one, so the first move is to get a high-level picture of what’s actually happening.


What Even Is a PCAP File?

Before jumping in, it helps to understand what we’re working with.

Think of your internet connection like a highway. Every piece of data travelling across it — a webpage loading, a video streaming, a ping — is like a car on that highway. A .pcap file is a recording of all those cars passing by at a specific moment in time.

Wireshark is the tool that lets you open that recording and look at every single packet that went through. It sounds overwhelming at first, and it is when you open it and see thousands of rows — but there are tricks to quickly find what matters.


Finding the Interesting Traffic

The first thing to do is go to Statistics → Conversations, then click the UDP tab and sort by the Bytes column.

One row was immediately obvious. A single UDP conversation was carrying around 4MB of data. Every other conversation was tiny — a few kilobytes at most. This massive flow was going through port 5000.

Everything else in the capture looked like noise:

  • Hundreds of ICMP packets — basically just “ping ping ping”, nothing useful
  • Some TCP traffic on ports 22, 80, 443, 8080 — normal-looking web and SSH traffic

All of that was there to distract. The 4MB UDP flow was the real story.


Identifying the Traffic

Filtering for the suspicious flow:

1
udp.port == 5000

Looking at the raw bytes of any packet in the detail panel reveals something immediately: every single packet starts with the byte 0x47.

That’s the MPEG Transport Stream sync byte — a signature that appears at the start of every packet in an MPEG-TS video stream. It’s the same format used in broadcast TV and internet video streaming. So someone was streaming a raw video over plain UDP on port 5000 with no encryption.

The challenge name suddenly made complete sense. twitch.pcap — like Twitch, a streaming platform. The entire capture was a video stream hiding in plain sight.


Extracting the Video

With the traffic identified, the next step is to pull the video bytes out of the packets and reassemble them into a playable file. Two terminal commands handle this:

1
tshark -r twitch.pcap -T fields -e data -Y "udp.port == 5000" | tr -d '\n' | xxd -r -p > stream.ts

What this does:

  • tshark reads the pcap file
  • -T fields -e data extracts just the raw payload bytes from each matching packet
  • tr -d '\n' joins all the hex output into one continuous stream
  • xxd -r -p converts the hex back into binary
  • The result is saved as stream.ts — a valid MPEG Transport Stream file

Then play it:

1
ffplay stream.ts

The Flag

The video played — about 8 seconds long. A guy was sitting on a messy couch surrounded by snack wrappers and energy drink cans, holding up a cardboard sign written in marker.

image

1
CATF{I_h4v3_b33n_w4tch1n9_t00_m4ny_str34m3r5_l4t3ly}

What Was the Trick?

The challenge was designed to make you overthink it. The distractions were deliberate:

  • ICMP packets with fake-looking payloads — there to make beginners waste time
  • TCP traffic on ports 22, 80, 443 — looks important but isn’t
  • The name “twitch” nudges you toward thinking about Twitch’s actual streaming protocol (RTMP), which runs on TCP port 1935 — but this stream uses plain UDP instead

The real signal was the size. One UDP flow dwarfed everything else in the capture. When one conversation is carrying 4MB and everything else is measured in kilobytes, that’s where you look. ```

This post is licensed under CC BY 4.0 by the author.