Skip to main content

shark on wire 2 picoCTF 2019 Solution

Analyze a network packet capture to uncover hidden data smuggled inside specific traffic streams.

Published: April 2, 2026Updated: August 25, 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 1Isolate UDP traffic from the relevant source host
    Observation
    The description says the flag is encoded in UDP source port numbers from one specific host. So filter Wireshark to UDP and look for the source IP whose ports change with every packet, rather than following a normal 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 field, so searching payloads returns nothing and makes it look like the pcap holds 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 2Read the ASCII values from the source port numbers
    Observation
    The source ports from that host all fall in 5032-5126, which is 5000 plus the printable ASCII range 32-126. So subtract 5000 from each, and use tshark field extraction to pull them out 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'

    Every packet in this stream shares the same destination port (22), so -e udp.dstport gives a column of identical values that decodes to nothing. The encoding is in the source port; only -e udp.srcport yields the varying values that carry the characters.

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

    tcpdump -x shows raw packet bytes including headers, but reading the source port out of hex by hand is error-prone and skips the clean field extraction tshark already offers. -e udp.srcport returns one decimal port per line, which is exactly what the Python script wants.

    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 3Assemble the flag from the port number sequence
    Observation
    tshark gives a clean list of decimal ports in packet order, and 5112 - 5000 = 112, which is 'p'. That confirms the base-5000 offset, so a short Python loop converts the whole sequence into the flag.
    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

    Guess the wrong base and you get non-printable or nonsensical characters, and may throw away the right source. The base is 5000: 5112 decodes to 'p' (112), 5105 to 'i' (105). Checking that 5112 - 5000 = 112 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 holds UDP traffic from several hosts. Feed every source port into the decode loop and unrelated port numbers mix in, producing garbled output. Filter to the single source host first so only the covert-channel packets get 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 in protocol header fields rather than payloads, exploiting the fact that most monitoring inspects content but not metadata. UDP source ports, IP ID numbers, TTL values, and TCP sequence numbers are all freely set by the sender and rarely inspected by firewalls or IDS signatures. Catching this kind of channel takes statistical analysis of field distributions, not payload pattern matching.

Related reading

Useful tools for Forensics

Where to go next