Suspicious Traffic
Suspicious Traffic
Platform: hackerlab | Category: Forensics | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-04-05 | Status: Solved Techniques: pcap_traffic_analysis, anomaly_hunting, tshark_field_extraction, morse_code_decoding
Summary
Task: analyze a PCAPNG capture with mostly normal QUIC/TLS/DNS traffic and identify the hidden exfiltration channel. Solution: isolate a tiny anomalous TCP stream, map payload bytes 00/20 to Morse dots and dashes, then decode the message to recover the flag.
Suspicious Traffic — HackerLab
Description
Подозрительный трафик
English summary: the archive contains a single task.pcapng capture. Most traffic looks like ordinary browsing from 192.168.138.129, but one very small TCP conversation hides a message that must be decoded to recover the flag.
Analysis
Before continuing, I followed the local knowledge-base workflow requirement: searched for similar covert-channel / PCAP exfiltration writeups and reviewed a related covert-channel PCAP writeup. The exact technique was different here, but the review helped confirm that small anomalous packet patterns are worth isolating early.
Initial reconnaissance showed a 4.5 MB PCAPNG capture with 5429 packets. Protocol statistics and endpoint/conversation summaries showed that most traffic was normal user activity: QUIC, TLS, and DNS generated by 192.168.138.129.
The important anomaly was a tiny one-way TCP conversation:
tcp.stream == 6192.168.1.99:12345 -> 192.168.1.1:80- exactly 42 packets
- all packets had ACK flag only (
tcp.flags == 0x0010) - all reused
seq=1,ack=1,ttl=64,ip.id=0xabcd,window=28944 - Wireshark/tshark marked them as suspected retransmissions
That pattern is not normal retransmission behavior for real application traffic. It strongly suggests a covert channel where header fields stay constant and only the minimal payload changes.
The payload lengths varied from 1 to 6 bytes, but each payload used only two byte values: 0x00 and 0x20. That ruled out normal ASCII and suggested symbolic encoding instead. The key insight was to treat:
- each
00byte as. - each
20byte as- - each packet as one Morse character
Decoding the 42 payloads in order produced:
HEYBROIHAVESECRETINFOCODEBY(REDACTED)
The message uses parentheses because { and } are not standard Morse punctuation in the expected form here. Converting them to braces yields the required flag format.
Solution
1. Inspect the capture
Useful commands:
bash read-only • copy allowedfile task.pcapng capinfos task.pcapng tshark -r task.pcapng -q -z io,phs tshark -r task.pcapng -q -z endpoints,ip -z conv,udp -z conv,tcp
This showed that the capture was mostly routine browsing traffic and that only one tiny TCP stream looked suspicious.
2. Isolate the abnormal stream
bash read-only • copy allowedtshark -r task.pcapng -Y "ip.addr == 192.168.1.99" -T fields -e ip.src -e ip.dst -e tcp.stream -e tcp.flags -e tcp.len tshark -r task.pcapng -Y "tcp.stream == 6" -T fields -e frame.number -e tcp.len -e tcp.payload
The extracted tcp.payload values, in order, were:
text read-only • copy allowed00000000, 00, 20002020, 20000000, 002000, 202020, 0000, 00000000, 0020, 00000020, 00, 000000, 00, 20002000, 002000, 00, 20, 0000, 2000, 00002000, 202020, 20002000, 202020, 200000, 00, 20000000, 20002020, 2000202000, 20002020, 00, 0000000020, 2020, 2020202020, 002000, 000000, 0000002020, 0000000020, 202000, 0020, 0020202020, 2000, 200020200020
3. Decode the covert Morse channel
python read-only • copy allowed#!/usr/bin/env python3 import subprocess MORSE = { '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z', '-----': '0', '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-.--.': '(', '-.--.-': ')' } def packet_to_morse(hex_payload: str) -> str: data = bytes.fromhex(hex_payload) return ''.join('.' if b == 0x00 else '-' if b == 0x20 else '?' for b in data) result = subprocess.run( [ 'tshark', '-r', 'task.pcapng', '-Y', 'tcp.stream == 6', '-T', 'fields', '-e', 'tcp.payload' ], capture_output=True, text=True, check=True ) symbols = [line.strip() for line in result.stdout.splitlines() if line.strip()] morse_chars = [packet_to_morse(s) for s in symbols] decoded = ''.join(MORSE[m] for m in morse_chars) print(decoded) print(decoded.replace('(', '{').replace(')', '}'))
Output:
text read-only • copy allowedHEYBROIHAVESECRETINFOCODEBY(REDACTED) HEYBROIHAVESECRETINFOCODEBY{REDACTED}
The human-readable text contains extra lead-in words, but the flag portion is clear.