Description
Find what is being sent across the network. The flag is encoded in the UDP source port numbers sent by a specific host.
Setup
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.
Step 1Isolate UDP traffic from the relevant source host
ObservationThe 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>'.bashwireshark capture.pcapbash# In Wireshark filter bar: ip.src == <source_ip> && udpWhat 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.
Step 2Read the ASCII values from the source port numbers
ObservationThe 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.bashtshark -r capture.pcap -Y 'udp && ip.src == <source_ip>' -T fields -e udp.srcportWhat 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.
Step 3Assemble the flag from the port number sequence
Observationtshark 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.pythonpython3 << '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) EOFExpected 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.