Why tshark, not Wireshark GUI
Wireshark is a powerful thing for visual analysis, but it has three system restrictions on the CTF. First: resources. The 500 MB dump is loaded into the entire operating room. On a weak virtual with 2 GB RAM it turns into a slideshow. Tshark handles the same file streaming and does not try to draw a million lines in the interface. Read more in our guide to creating ctf tasks.
The second is repeatability. Need to test the hypothesis on ten dumps in a row? In GUI, it's a manual routine: opened - filtered - looked - closed - opened the next. In CLI you write for f in *.pcap; do tshark -r "$f" -Y 'frame matches "flag"'; done and get the result in seconds.
The third is scripting. The tshark output is a regular text. It can be driven through grep, awk, sort | uniq -c | sort -rn and get the statistics instantly. The result of the GUI is a screenshot or manual copying. On the CTF, where time determines the place in the table, the difference is murderous.
Tshark is included in the Wireshark package. On Kali and Parrot stands out of the box. On Ubuntu — sudo apt install tshark. The syntax of display filters completely coincides with Wireshark: you know one - you know both. Tshark uses the same libpcap library and the same protocol disassembly engine as the graphical version – the difference is only in the way you interact.
Primary Intelligence: What's Inside the Pcap File
The first action with any dump is a general picture, not reading packages one at a time. Team tshark -r challenge.pcap -q -z io,phs Protocol Hierarchy Statistics: percentage distribution of TCP, UDP, HTTP, DNS, TLS. If 95% of traffic is TLS and 0.3% is HTTP in the open, it is these 0.3% that need to be picked first. The same window as in Wireshark via Statistics → Protocol Hierarchy, but without GUI.
For the list of participants (who spoke to) – tshark -r challenge.pcap -q -z conv,tcp. Outputs all TCP conversations with the amount of data transmitted and the number of packets. The anomaly catches the eye at once: one host sent 50 KB to port 1337, while the rest worked on 80 and 443. This is a sign of non-standard ports – technology Non-Standard Port (T1571 by MITRE ATT&CK). Breakdown by intervals — tshark -r challenge.pcap -q -z io,stat,10 – will show the number of packages every 10 seconds. The splash at a specific mark marks the beginning of interesting activity.
Another useful flag at the start – tshark -r challenge.pcap -q -z endpoints,ip. Show all IP addresses in the dump with the number of bytes sent and received. A host that sent significantly more than it received is a potential source of exfiltration (T1041, Exfiltration Over C2 Channel).
Display filters tshark to search for evidence
Basic Syntax and Rough Flag Search
Reading filters via option -Y – the main noise weeding tool. By Protocol: tshark -r challenge.pcap -Y "http". By IP address: tshark -r challenge.pcap -Y "ip.addr == 10.0.2.15". Combination: tshark -r challenge.pcap -Y "http.request.method == POST and ip.dst == 192.168.1.100" – only POST requests to a specific server.
For CTF critically useful filter frame matches. He searches for a string throughout the contents of the package, including payload. Team tshark -r challenge.pcap -Y 'frame matches "(?i)flag\{"' finds any package where the “flag{” pattern meets in any register. It was this filter that solved the problem in 90 seconds instead of 40 minutes of manual flipping. In CTF-writeups by PicoCTF frame matches described as the first step of analyzing simple network tucks — and for good reason.
Another option is contains, which operates at the level of a specific protocol field: tshark -r challenge.pcap -Y 'http.file_data contains "CTF{"' looking for a flag pattern inside the body of HTTP responses. For binary signatures - hex-literals: tshark -r challenge.pcap -Y 'data contains 50:4b:03:04' finds a ZIP signature in data-payload.
Filters by protocol for CTF tasks
DNS analysis: dns.qry.type == 16 filters only TXT records - frequent channel DNS-exfiltration (T1071.004 by MITRE ATT&CK). Filter dns.qry.name.len > 50 will cling queries with abnormally long domain names – DNS tunneling marker. In CTF-drags, the DNS channel is so common that checking the length of subdomains should be included in a standard checklist.
HTTP: filter http.response.code == 200 and http.content_type contains "octet-stream" shows successful downloads of binary files. Expression http.request.uri contains ".php" catches requests to PHP scripts – often these are webhells or data output points. Non-standard User-Agent like python-requests/2.28 or curl/7.88 in traffic, where the usual browser dominates – the marker of automated queries (T1071.001, Web Protocols).
FTP: ftp.request.command == "PASS" – intercepting passwords in the open. There is nothing to comment on here – FTP drives letters letters, and this is a gift for CTF.
ICMP: icmp.type == 8 – echo-requests. CTF payload ICMP packets often contain hidden data (T1095, Non-Application Layer Protocol).
Extracting fields through -T fields
Flag -T fields combined with -e – what turns tshark from a packet viewer into a structured data generator. Instead of a full text dam, you get only the necessary fields in a tabular format ready for processing unix-utilities.
DNS queries and DNS tunneling detection
Extract all DNS requests by one team: tshark -r challenge.pcap -Y "dns.qry.name" -T fields -e dns.qry.name. Each line is one domain requested. Further | sort | uniq -c | sort -rn | head -20 builds the top 20 requested domains by frequency. If one domain is requested 500 times and the rest is 1-5, it is a red flag.
DNS tunneling in CTF is usually implemented as follows: data is encoded in subdomains. Request to aGVsbG8gd29ybGQ.evil.com – this is base64-line “hello world” packed in a subdomain. Full Pipline for Extraction and Decoding:
tshark -r challenge.pcap -Y "dns.qry.name contains evil.com" \
-T fields -e dns.qry.name | \
awk -F. '{print $1}' | \
tr -d '\n' | base64 -d
Parsement by Item: tshark filters requests to evil.com and displays a full domain name. awk -F. cuts into points and takes the first fragment (coded data). tr -d '\n' glues the lines. base64 -d decodes the result. Depending on the task, hex-decoding may be required (xxd -r -p) instead of base64 - determined by the type of data.
For TXT responses (when the server returns data via DNS TXT records): tshark -r challenge.pcap -Y "dns.txt and dns.flags.response == 1" -T fields -e dns.txt. TXT records are the second most popular DNS-exfiltration channel: the server puts a portion of data in a TXT response, the client collects fragments.
HTTP metadata and POST bodies
Full HTTP request log in CSV format, ready to import into the table or process through awk:
tshark -r challenge.pcap -Y "http.request" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e http.request.method -e http.host \
-e http.request.uri -e http.user_agent \
-E header=y -E separator=,
Option -E header=y adds a string with column names, -E separator=, sets the divider. At the outlet - a structured table: who, where, by what method and with which User-Agent addressed. To extract unique User-Agent: tshark -r challenge.pcap -Y "http.user_agent" -T fields -e http.user_agent | sort -u.
POST data (sent forms, credentials, downloaded files): tshark -r challenge.pcap -Y "http.request.method == POST" -T fields -e http.file_data. Everything that was transmitted in the body of the POST request will be in the conclusion. If the task has a login form sent via HTTP without TLS credentials, there will be something mother gave birth to.
Extracting files and TCP streams from pcap
Exporting facilities and credential hunting
Tshark knows how to do the same thing as Wireshark via File → Export Objects → HTTP, but from the terminal: tshark -r challenge.pcap --export-objects http,./exported/. The team creates a directory exported and folds there all files transferred over HTTP - pictures, archives, scripts, HTML pages. Next standard check: file exported/* will show real file types (the extension can lie), and strings exported/* | grep -i flag run through the text data in search of the flag. Similarly working --export-objects smb,./smb_files/ and --export-objects tftp,./tftp_files/ for SMB and TFTP transmissions.
Each TCP connection in the dump has a stream number. List of unique numbers: tshark -r challenge.pcap -T fields -e tcp.stream | sort -un. View specific stream in ASCII: tshark -r challenge.pcap -q -z follow,tcp,ascii,0 – will show the contents of the stream number 0 as text. For binary data ascii replaced by hex or raw. Export raw payload stream to file: tshark -r challenge.pcap -q -z follow,tcp,raw,5 | xxd -r -p > stream5.bin – useful when the file was transmitted without an HTTP wrapper (via FTP-data or custom protocol).
A separate block is the credential hunting (T1040, Network Sniffing by MITRE ATT&CK). For FTP: tshark -r challenge.pcap -Y "ftp.request.command == USER or ftp.request.command == PASS" -T fields -e ftp.request.command -e ftp.request.arg output login/password pairs. For HTTP Basic Auth: tshark -r challenge.pcap -Y "http.authorization" -T fields -e http.authorization – result of the species Basic dXNlcjpwYXNzd29yZA==, which is decoded through echo "dXNlcjpwYXNzd29yZA==" | base64 -d. SMTP-credentials: tshark -r challenge.pcap -Y "smtp.req.command == AUTH" -T fields -e smtp.req.parameter. In CTF, the flag is often sewn into the intercepted password.
Hidden channels: ICMP, DNS TXT, custom protocols
Classic CTF – data hidden in payload ICMP echo requests. Extraction: tshark -r challenge.pcap -Y "icmp.type == 8" -T fields -e data.data outputs the hex content of each package. Convert to text: paipline | tr -d ':\n' | xxd -r -p glue the pieces and translate from hex to ASCII. If the data is bit byte (one character per package), each ICMP package carries exactly one letter, the order is determined by timetamp. If blocks are concatenation without dividers. According to ctf.support, ICMP and DNS tunneling are the two most frequent vectors of hidden channels in network CTF tasks.
For DNS TXT-exfiltration (data in server responses) the command has already given above. Nuance: sometimes the data is scattered over several TXT responses with serial numbers in the subdomains (1.exfil.evil.com, 2.exfil.evil.com). Here you need to sort by number before the save – bash-one-liner can no longer cope, you need Python.
Custom protocols. If tshark -r challenge.pcap -q -z conv,tcp shows activity on port 1337 or 31337, with a probability of 90% this is the custom task protocol. View stream: tshark -r challenge.pcap -Y "tcp.port == 1337" -q -z follow,tcp,ascii,N, where N is the number of the stream. Often inside there is a text protocol with commands and answers, among which the flag hides. I have never seen anything legitimate at such ports – 1337 and 31337 in the CTF it’s like a neon sign “dig here”.
Pcap parsing automation: pipelines and scripts
Cheklist and single-installers for CTF tasks
On each CTF, I run the commands in a fixed order. The algorithm is developed for dozens of tasks on the network of the primary:
Step 1 – rough search for the flag: tshark -r task.pcap -Y 'frame matches "(?i)(flag|ctf)\{"' -x. Flag -x adds a hex dump for context. It's amazing how often the flag is rolling in the open.
Step 2 – The Protocol Hierarchy: tshark -r task.pcap -q -z io,phs. Identify the dominant and rare protocols.
Step 3 – Export HTTP objects: tshark -r task.pcap --export-objects http,./objects/. Check through file and strings.
Step 4 – DNS Anomalies: tshark -r task.pcap -Y "dns.qry.name" -T fields -e dns.qry.name | sort | uniq -c | sort -rn | head -20.
Step 5 – list of TCP streams: tshark -r task.pcap -T fields -e tcp.stream | sort -un. Every suspicious stream is viewed through -z follow,tcp,ascii,N.
Step 6 – ICMP payload: tshark -r task.pcap -Y "icmp" -T fields -e data.data.
Step 7 –: credentials tshark -r task.pcap -Y "ftp.request.command == PASS or http.authorization or smtp.req.command == AUTH".
Step 8 – file signatures in payload: tshark -r task.pcap -Y 'data contains 50:4b:03:04 or data contains 89:50:4e:47 or data contains ff:d8:ff' – ZIP, PNG and JPEG respectively.
This algorithm covers most Easy–Medium CTF tasks. It looks trite, but saves 70% of the time, because it covers the typical scenarios before you start thinking. For Hard, a TLS transcript is added (if the task is given a key through -o tls.keylog_file:keys.log) and the analysis of custom binary protocols.
Package processing of the dam series: for f in challenges/*.pcap; do echo "=== $f ===" && tshark -r "$f" -Y 'frame matches "(?i)flag"' 2>/dev/null; done. Redirect 2>/dev/null suppresses file format warnings.
Wireshark is a powerful thing for visual analysis, but it has three system restrictions on the CTF. First: resources. The 500 MB dump is loaded into the entire operating room. On a weak virtual with 2 GB RAM it turns into a slideshow. Tshark handles the same file streaming and does not try to draw a million lines in the interface. Read more in our guide to creating ctf tasks.
The second is repeatability. Need to test the hypothesis on ten dumps in a row? In GUI, it's a manual routine: opened - filtered - looked - closed - opened the next. In CLI you write for f in *.pcap; do tshark -r "$f" -Y 'frame matches "flag"'; done and get the result in seconds.
The third is scripting. The tshark output is a regular text. It can be driven through grep, awk, sort | uniq -c | sort -rn and get the statistics instantly. The result of the GUI is a screenshot or manual copying. On the CTF, where time determines the place in the table, the difference is murderous.
Tshark is included in the Wireshark package. On Kali and Parrot stands out of the box. On Ubuntu — sudo apt install tshark. The syntax of display filters completely coincides with Wireshark: you know one - you know both. Tshark uses the same libpcap library and the same protocol disassembly engine as the graphical version – the difference is only in the way you interact.
Primary Intelligence: What's Inside the Pcap File
The first action with any dump is a general picture, not reading packages one at a time. Team tshark -r challenge.pcap -q -z io,phs Protocol Hierarchy Statistics: percentage distribution of TCP, UDP, HTTP, DNS, TLS. If 95% of traffic is TLS and 0.3% is HTTP in the open, it is these 0.3% that need to be picked first. The same window as in Wireshark via Statistics → Protocol Hierarchy, but without GUI.
For the list of participants (who spoke to) – tshark -r challenge.pcap -q -z conv,tcp. Outputs all TCP conversations with the amount of data transmitted and the number of packets. The anomaly catches the eye at once: one host sent 50 KB to port 1337, while the rest worked on 80 and 443. This is a sign of non-standard ports – technology Non-Standard Port (T1571 by MITRE ATT&CK). Breakdown by intervals — tshark -r challenge.pcap -q -z io,stat,10 – will show the number of packages every 10 seconds. The splash at a specific mark marks the beginning of interesting activity.
Another useful flag at the start – tshark -r challenge.pcap -q -z endpoints,ip. Show all IP addresses in the dump with the number of bytes sent and received. A host that sent significantly more than it received is a potential source of exfiltration (T1041, Exfiltration Over C2 Channel).
Display filters tshark to search for evidence
Basic Syntax and Rough Flag Search
Reading filters via option -Y – the main noise weeding tool. By Protocol: tshark -r challenge.pcap -Y "http". By IP address: tshark -r challenge.pcap -Y "ip.addr == 10.0.2.15". Combination: tshark -r challenge.pcap -Y "http.request.method == POST and ip.dst == 192.168.1.100" – only POST requests to a specific server.
For CTF critically useful filter frame matches. He searches for a string throughout the contents of the package, including payload. Team tshark -r challenge.pcap -Y 'frame matches "(?i)flag\{"' finds any package where the “flag{” pattern meets in any register. It was this filter that solved the problem in 90 seconds instead of 40 minutes of manual flipping. In CTF-writeups by PicoCTF frame matches described as the first step of analyzing simple network tucks — and for good reason.
Another option is contains, which operates at the level of a specific protocol field: tshark -r challenge.pcap -Y 'http.file_data contains "CTF{"' looking for a flag pattern inside the body of HTTP responses. For binary signatures - hex-literals: tshark -r challenge.pcap -Y 'data contains 50:4b:03:04' finds a ZIP signature in data-payload.
Filters by protocol for CTF tasks
DNS analysis: dns.qry.type == 16 filters only TXT records - frequent channel DNS-exfiltration (T1071.004 by MITRE ATT&CK). Filter dns.qry.name.len > 50 will cling queries with abnormally long domain names – DNS tunneling marker. In CTF-drags, the DNS channel is so common that checking the length of subdomains should be included in a standard checklist.
HTTP: filter http.response.code == 200 and http.content_type contains "octet-stream" shows successful downloads of binary files. Expression http.request.uri contains ".php" catches requests to PHP scripts – often these are webhells or data output points. Non-standard User-Agent like python-requests/2.28 or curl/7.88 in traffic, where the usual browser dominates – the marker of automated queries (T1071.001, Web Protocols).
FTP: ftp.request.command == "PASS" – intercepting passwords in the open. There is nothing to comment on here – FTP drives letters letters, and this is a gift for CTF.
ICMP: icmp.type == 8 – echo-requests. CTF payload ICMP packets often contain hidden data (T1095, Non-Application Layer Protocol).
Extracting fields through -T fields
Flag -T fields combined with -e – what turns tshark from a packet viewer into a structured data generator. Instead of a full text dam, you get only the necessary fields in a tabular format ready for processing unix-utilities.
DNS queries and DNS tunneling detection
Extract all DNS requests by one team: tshark -r challenge.pcap -Y "dns.qry.name" -T fields -e dns.qry.name. Each line is one domain requested. Further | sort | uniq -c | sort -rn | head -20 builds the top 20 requested domains by frequency. If one domain is requested 500 times and the rest is 1-5, it is a red flag.
DNS tunneling in CTF is usually implemented as follows: data is encoded in subdomains. Request to aGVsbG8gd29ybGQ.evil.com – this is base64-line “hello world” packed in a subdomain. Full Pipline for Extraction and Decoding:
tshark -r challenge.pcap -Y "dns.qry.name contains evil.com" \
-T fields -e dns.qry.name | \
awk -F. '{print $1}' | \
tr -d '\n' | base64 -d
Parsement by Item: tshark filters requests to evil.com and displays a full domain name. awk -F. cuts into points and takes the first fragment (coded data). tr -d '\n' glues the lines. base64 -d decodes the result. Depending on the task, hex-decoding may be required (xxd -r -p) instead of base64 - determined by the type of data.
For TXT responses (when the server returns data via DNS TXT records): tshark -r challenge.pcap -Y "dns.txt and dns.flags.response == 1" -T fields -e dns.txt. TXT records are the second most popular DNS-exfiltration channel: the server puts a portion of data in a TXT response, the client collects fragments.
HTTP metadata and POST bodies
Full HTTP request log in CSV format, ready to import into the table or process through awk:
tshark -r challenge.pcap -Y "http.request" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e http.request.method -e http.host \
-e http.request.uri -e http.user_agent \
-E header=y -E separator=,
Option -E header=y adds a string with column names, -E separator=, sets the divider. At the outlet - a structured table: who, where, by what method and with which User-Agent addressed. To extract unique User-Agent: tshark -r challenge.pcap -Y "http.user_agent" -T fields -e http.user_agent | sort -u.
POST data (sent forms, credentials, downloaded files): tshark -r challenge.pcap -Y "http.request.method == POST" -T fields -e http.file_data. Everything that was transmitted in the body of the POST request will be in the conclusion. If the task has a login form sent via HTTP without TLS credentials, there will be something mother gave birth to.
Extracting files and TCP streams from pcap
Exporting facilities and credential hunting
Tshark knows how to do the same thing as Wireshark via File → Export Objects → HTTP, but from the terminal: tshark -r challenge.pcap --export-objects http,./exported/. The team creates a directory exported and folds there all files transferred over HTTP - pictures, archives, scripts, HTML pages. Next standard check: file exported/* will show real file types (the extension can lie), and strings exported/* | grep -i flag run through the text data in search of the flag. Similarly working --export-objects smb,./smb_files/ and --export-objects tftp,./tftp_files/ for SMB and TFTP transmissions.
Each TCP connection in the dump has a stream number. List of unique numbers: tshark -r challenge.pcap -T fields -e tcp.stream | sort -un. View specific stream in ASCII: tshark -r challenge.pcap -q -z follow,tcp,ascii,0 – will show the contents of the stream number 0 as text. For binary data ascii replaced by hex or raw. Export raw payload stream to file: tshark -r challenge.pcap -q -z follow,tcp,raw,5 | xxd -r -p > stream5.bin – useful when the file was transmitted without an HTTP wrapper (via FTP-data or custom protocol).
A separate block is the credential hunting (T1040, Network Sniffing by MITRE ATT&CK). For FTP: tshark -r challenge.pcap -Y "ftp.request.command == USER or ftp.request.command == PASS" -T fields -e ftp.request.command -e ftp.request.arg output login/password pairs. For HTTP Basic Auth: tshark -r challenge.pcap -Y "http.authorization" -T fields -e http.authorization – result of the species Basic dXNlcjpwYXNzd29yZA==, which is decoded through echo "dXNlcjpwYXNzd29yZA==" | base64 -d. SMTP-credentials: tshark -r challenge.pcap -Y "smtp.req.command == AUTH" -T fields -e smtp.req.parameter. In CTF, the flag is often sewn into the intercepted password.
Hidden channels: ICMP, DNS TXT, custom protocols
Classic CTF – data hidden in payload ICMP echo requests. Extraction: tshark -r challenge.pcap -Y "icmp.type == 8" -T fields -e data.data outputs the hex content of each package. Convert to text: paipline | tr -d ':\n' | xxd -r -p glue the pieces and translate from hex to ASCII. If the data is bit byte (one character per package), each ICMP package carries exactly one letter, the order is determined by timetamp. If blocks are concatenation without dividers. According to ctf.support, ICMP and DNS tunneling are the two most frequent vectors of hidden channels in network CTF tasks.
For DNS TXT-exfiltration (data in server responses) the command has already given above. Nuance: sometimes the data is scattered over several TXT responses with serial numbers in the subdomains (1.exfil.evil.com, 2.exfil.evil.com). Here you need to sort by number before the save – bash-one-liner can no longer cope, you need Python.
Custom protocols. If tshark -r challenge.pcap -q -z conv,tcp shows activity on port 1337 or 31337, with a probability of 90% this is the custom task protocol. View stream: tshark -r challenge.pcap -Y "tcp.port == 1337" -q -z follow,tcp,ascii,N, where N is the number of the stream. Often inside there is a text protocol with commands and answers, among which the flag hides. I have never seen anything legitimate at such ports – 1337 and 31337 in the CTF it’s like a neon sign “dig here”.
Pcap parsing automation: pipelines and scripts
Cheklist and single-installers for CTF tasks
On each CTF, I run the commands in a fixed order. The algorithm is developed for dozens of tasks on the network of the primary:
Step 1 – rough search for the flag: tshark -r task.pcap -Y 'frame matches "(?i)(flag|ctf)\{"' -x. Flag -x adds a hex dump for context. It's amazing how often the flag is rolling in the open.
Step 2 – The Protocol Hierarchy: tshark -r task.pcap -q -z io,phs. Identify the dominant and rare protocols.
Step 3 – Export HTTP objects: tshark -r task.pcap --export-objects http,./objects/. Check through file and strings.
Step 4 – DNS Anomalies: tshark -r task.pcap -Y "dns.qry.name" -T fields -e dns.qry.name | sort | uniq -c | sort -rn | head -20.
Step 5 – list of TCP streams: tshark -r task.pcap -T fields -e tcp.stream | sort -un. Every suspicious stream is viewed through -z follow,tcp,ascii,N.
Step 6 – ICMP payload: tshark -r task.pcap -Y "icmp" -T fields -e data.data.
Step 7 –: credentials tshark -r task.pcap -Y "ftp.request.command == PASS or http.authorization or smtp.req.command == AUTH".
Step 8 – file signatures in payload: tshark -r task.pcap -Y 'data contains 50:4b:03:04 or data contains 89:50:4e:47 or data contains ff:d8:ff' – ZIP, PNG and JPEG respectively.
This algorithm covers most Easy–Medium CTF tasks. It looks trite, but saves 70% of the time, because it covers the typical scenarios before you start thinking. For Hard, a TLS transcript is added (if the task is given a key through -o tls.keylog_file:keys.log) and the analysis of custom binary protocols.
Package processing of the dam series: for f in challenges/*.pcap; do echo "=== $f ===" && tshark -r "$f" -Y 'frame matches "(?i)flag"' 2>/dev/null; done. Redirect 2>/dev/null suppresses file format warnings.