Ph4nt0m 1ntrud3r picoCTF Writeup

When I first started the Forensics category in CTF, looking at a Wireshark screen filled with thousands of packets felt like trying to find a specific grain of sand on a beach. If you’ve ever felt that “analysis paralysis,” this guide is for you.

In this writeup for the picoCTF “Ph4nt0m 1ntrud3r” challenge, I’ll show you how to move past the “noise” and think like a digital forensic investigator. We aren’t just looking for a flag; we are reconstructing an attacker’s fragmented trail.


1. Challenge Strategy: Why This Article Matters

  • Challenge Name: Ph4nt0m 1ntrud3r
  • Category: Forensics (Network)
  • Difficulty: Easy
  • The Problem: An attacker hid a flag by splitting it into small pieces across multiple network packets.

My Experience

Many beginners get stuck manually clicking every packet. I wrote this to demonstrate that forensics is about pattern recognition and automation, not manual labor. I’ll share the exact “aha!” moment when the data started making sense to me.


2. The Investigation Log: My Thought Process & Failures

Before the “perfect” solution, there was a lot of trial and error. Here is the reality of my analysis:

StepAction TakenMy Internal MonologueThe Hurdle
1. ReconScrolling through Wireshark“TCP, HTTP… looks normal. Is there any weird protocol?”Too much noise. 1,000+ packets are impossible to check by eye.
2. DiscoverySorting by Length“Wait… why are there multiple packets with an odd size?”Identifying which packets were “padding” vs. “payload.”
3. HypothesisInspecting payloads“Those strings end in =. That’s a Base64 signature!Some fragments lacked =, making me doubt the encoding at first.
4. FailureManual stitching“I’ll just copy-paste these… wait, the flag looks like gibberish.”The packets were out of order. I forgot that network arrival $\neq$ logical order.
5. SuccessPython Scripting“Let’s use timestamps as the source of truth and automate it.”Formatting the timestamp data correctly for the script.

3. Step-by-Step Guide: Beyond the Basics


Step 1: Isolating the Anomalies in Wireshark

In real-world forensics, attackers hide data in plain sight. Open evidence.pcap and immediately sort by the Length column.

In this challenge, you’ll notice standard traffic uses specific lengths (e.g., 48 bytes), but our “intruder” packets stand out.

Pro Tip: Why search by length? In data exfiltration, attackers often use fixed-size “chunks” to bypass simple firewalls. These consistent but unusual packet sizes are your “smoking gun.”


Step 2: Recognizing the Base64 “Tell”

Base64 is a common way to encode binary data as text.
If you see random-looking letters ending in = or ==, that’s a strong hint that it’s Base64.

There is a lot of information, so look at length other than 48 bytes.

Here’s what some of the captured data looks like:

31.28.5788 bnRfdGg0dA==
31.28.5796 NjZkMGJmYg==
31.28.5786 ezF0X3c0cw==
31.28.5791 XzM0c3lfdA==
31.28.5784 cGljb0NURg==
31.28.5794 YmhfNHJfOQ==
31.28.5798 fQ==

Once you click on the suspicious packets, look at the Data pane. You’ll see strings like cGljb0NURg==.

Real-World Context: Malware often encodes C2 (Command & Control) instructions in Base64 to bypass Deep Packet Inspection (DPI) that looks for “cleartext” malicious commands.tes on 3-byte blocks, it uses the = sign as “padding” if the data doesn’t fit perfectly. Seeing a trailing equals sign is the #1 tell-tale sign of Base64.

The “Tell”: The = character at the end is called “padding.” Base64 groups binary data into 3-byte blocks; if the data doesn’t fit perfectly, = is added.


⏱ Step 3: Sorting the packets by time

The data seems out of order, so let’s sort it chronologically by the timestamp:

31.28.5784 cGljb0NURg==
31.28.5786 ezF0X3c0cw==
31.28.5788 bnRfdGg0dA==
31.28.5791 XzM0c3lfdA==
31.28.5794 YmhfNHJfOQ==
31.28.5796 NjZkMGJmYg==
31.28.5798 fQ==

Once we have them ordered correctly, we can decode each Base64 string.


Step 4: Automating the Reconstruction

Because the packets arrived slightly out of sequence, we need to sort them by their Arrival Time and then decode them. Here is a clean Python script to handle the heavy lifting:

import base64

cipher = [
    "cGljb0NURg==",
    "ezF0X3c0cw==",
    "bnRfdGg0dA==",
    "XzM0c3lfdA==",
    "YmhfNHJfOQ==",
    "NjZkMGJmYg==",
    "fQ=="
]

plain = ""
for c in cipher:
    decoded = base64.b64decode(c).decode()
    plain += decoded

print(plain)

Step 5: Running the Script

Run the script in your terminal:

$ python3 decode.py

Output:

picoCTF{1t_w4snt_th4t_34sy_tbh_4r_966d0bfb}

🎉 We’ve found the flag!


Final Flag

picoCTF{1t_w4snt_th4t_34sy_tbh_4r_966d0bfb}

4. Troubleshooting & Deep Dive (The “Expert” View)

“What if I don’t see any = signs?” Not all Base64 strings have padding. If you suspect Base64 but don’t see the =, check the character set. If it only uses A-Z, a-z, 0-9, +, /, try decoding it anyway!

“Why was my flag corrupted?” Network packets can arrive at different times due to latency. In this challenge, the Timestamp is your anchor. In more complex challenges, you might need to follow the TCP Sequence Numbers to ensure you are reading the data in the order the application intended.


5. Summary & Key Takeaways

  1. Context over Content: Don’t just look at what the data is; look at how it’s behaving (packet length, frequency, timing).
  2. Scripting is Mandatory: Manually decoding 7 fragments is easy, but what if there were 700? Always look for the scriptable path.
  3. The Base64 Habit: Whenever you see alphanumeric “gibberish,” Base64 should be your first guess.

Final Flag: picoCTF{1t_w4snt_th4t_34sy_tbh_4r_966d0bfb}


📚 Further Reading

Here are related articles from alsavaudomila.com that complement this challenge:

  1. DISKO 1 picoCTF Writeup: How to Analyze .dd.gz Disk Images and Find Hidden Flags
  2. Corrupted file picoCTF Writeup: How to Use Secret Binary Tactics for Direct Success
  3. Scan Surprise picoCTF Writeup: The Best Way to Reveal Hidden QR Secrets