shark on wire 2 picoCTF 2019 Solution

Published: April 2, 2026

Description

Find what is being sent across the network. The flag is encoded in the UDP source port numbers sent by a specific host.

Download the pcap file and open it in Wireshark.

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Isolate UDP traffic from the relevant source host
    Observation
    I noticed the challenge description said the flag is encoded in UDP source port numbers from a specific host, which suggested filtering Wireshark to UDP traffic and scanning for the one source IP whose source ports change with every packet rather than following a typical ephemeral-port pattern.
    Open the pcap in Wireshark. Filter for UDP traffic and look at the source port numbers. One source IP (e.g., 10.0.0.66) sends many UDP packets where the source port is 5000 plus the ASCII value of each character. Filter by that source address with 'ip.src == <source_ip>'.
    bash
    wireshark capture.pcap
    bash
    # In Wireshark filter bar: ip.src == <source_ip> && udp
    What didn't work first

    Tried: Searching the packet payloads for the flag string using 'Edit > Find Packet > String'

    The UDP packets carry no meaningful payload - the flag is not in the data bytes at all. The covert channel encodes each character in the source port number field, so searching payloads returns nothing and leads you to think the pcap contains no flag.

    Tried: Filtering for TCP traffic instead of UDP

    The encoding uses UDP packets specifically. A TCP filter like 'tcp' returns unrelated connection traffic from the capture and hides the relevant UDP stream entirely. Switching to 'udp' and then narrowing by source IP is the correct path.

    Learn more

    Network covert channels encode data in protocol fields that are normally ignored. UDP source port numbers are freely chosen by the sender. In this challenge packets all target destination port 22, while the source port encodes the flag: each source port is 5000 plus the ASCII value of one character. Using source ports as a covert channel produces traffic that looks like normal application chatter to a basic traffic monitor.

  2. Step 2
    Read the ASCII values from the source port numbers
    Observation
    I noticed the source ports from the filtered host were all in the range 5032-5126 (matching 5000 plus ASCII printable values 32-126), which suggested subtracting 5000 from each port number and using tshark field extraction to pull them in order.
    Sort the filtered packets by time. Read each UDP source port number in order and subtract 5000 to get the ASCII character code. Extract them with tshark and process in Python.
    bash
    tshark -r capture.pcap -Y 'udp && ip.src == <source_ip>' -T fields -e udp.srcport
    What didn't work first

    Tried: Extracting the destination port instead of the source port with '-e udp.dstport'

    All packets in this stream share the same destination port (22), so '-e udp.dstport' produces a column of identical values that decodes to nothing. The encoding is in the source port field; only '-e udp.srcport' yields the varying values that carry the flag characters.

    Tried: Using tcpdump with '-x' to dump hex payload bytes instead of tshark field extraction

    tcpdump -x shows the raw packet bytes including headers, but reading the source port out of hex output by hand is error-prone and bypasses the clean field-extraction that tshark provides. The '-e udp.srcport' flag in tshark returns one decimal port number per line, which is exactly what the Python script needs.

    Learn more

    Look for start and stop markers in the port sequence (e.g., a port value of 'start' or a distinctive marker port). The characters between the start and end markers spell the flag.

  3. Step 3
    Assemble the flag from the port number sequence
    Observation
    I noticed that tshark output gave a clean list of decimal port numbers in packet order, and that 5112 - 5000 = 112 = chr('p'), which confirmed the base-5000 offset and suggested a short Python loop to convert the full sequence into the flag string.
    Subtract 5000 from each source port number to get the ASCII character code, then concatenate in packet order.
    python
    python3 << 'EOF'
    # Paste source port numbers from tshark output
    ports = [5112, 5105, 5099, 5111, 5067, 5084, 5070, ...]  # example
    
    flag = ''
    for p in ports:
        code = p - 5000  # source port minus base 5000 = ASCII value
        if 32 <= code <= 126:
            flag += chr(code)
    print(flag)
    EOF

    Expected output

    picoCTF{p1LLf3r3d_data_v1a_st3g0}
    What didn't work first

    Tried: Subtracting a different base (e.g., 4000 or 5001) because the first port number looked off

    If you guess the wrong base you get non-printable or nonsensical characters and discard the correct source. The base is 5000: ports like 5112 decode to 'p' (112), 5105 to 'i' (105). Confirming that 5112 - 5000 = 112 = chr('p') validates the offset immediately.

    Tried: Including all captured UDP packets rather than filtering to the specific source IP before running the Python decode

    The pcap contains UDP traffic from multiple hosts. Feeding all source ports into the decode loop mixes in unrelated port numbers and produces garbled output that does not resemble a flag. Filtering to the single source host first ensures only the covert-channel packets are decoded.

    Learn more

    This covert channel is difficult to detect with basic network monitoring because packet sizes and frequencies look normal. The data is hidden in the port number field rather than the payload - tools that only inspect payloads for signatures would miss it entirely.

Interactive tools
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.

Flag

Reveal flag

picoCTF{p1LLf3r3d_data_v1a_st3g0}

The flag is encoded in the UDP source port numbers - extract source ports from the relevant source host, subtract 5000 from each, and convert each result to its ASCII character.

Key takeaway

Network covert channels hide data inside protocol header fields rather than payloads, exploiting the fact that most monitoring tools inspect content but not metadata values. Fields like UDP source ports, IP ID numbers, TTL values, and TCP sequence numbers are all freely controlled by the sender and rarely inspected by firewalls or IDS signatures. Detecting this class of channel requires statistical analysis of field distributions, not payload pattern matching.

Related reading

Want more picoCTF 2019 writeups?

Useful tools for Forensics

What to try next