TShark

TShark is Wireshark without the GUI: it reads a saved PCAP or captures live, decodes packets with the same dissectors, and prints results you can grep, script, and paste into notes. Reach for it on a headless box, or when you want a repeatable one-liner instead of clicking through the GUI: triaging a capture, pulling DNS or HTTP out of a CTF PCAP, or confirming what a service actually puts on the wire. Only capture on networks and hosts you own or are explicitly authorized to monitor.

Mental model: source -> filter -> output. Pick -r file or -i IFACE, narrow with -Y "filter", then take the default summaries or -T fields.

New to packet capture? Core concepts
  • Source: read a saved capture with -r file, or sniff live with -i IFACE
  • Capture filter (BPF): -f "tcp port 80", applied at the wire before decode, so unwanted traffic never hits disk
  • Display filter (Wireshark syntax): -Y "http.request", protocol-aware, applied after packets are decoded
  • Dissectors: Wireshark’s decoders that turn raw bytes into named protocol fields; TShark shares them
  • Fields: -T fields -e ip.src -e dns.qry.name prints named columns; confirm a name with -V first
  • Statistics taps: -z io,phs, -z conv,tcp, -z follow,..., -z credentials, with per-packet lines silenced by -q
  • Stream: a reassembled TCP conversation, both sides in order (follow,tcp,ascii,N)
  • Ring buffer: -b filesize: -b files: bounds a long capture to a fixed footprint
When to reach for TShark (and when not)

Reach for it to triage a PCAP on a headless box, pull DNS, HTTP, or cleartext creds out of a capture as scriptable columns, confirm what a service actually puts on the wire, or bake the whole analysis into a repeatable one-liner you can paste into notes.

Reach for something else when you need to grab raw packets on a minimal or constrained box (tcpdump, then hand the file back to TShark), follow a stream visually once the terminal gets cramped (Wireshark’s GUI shares the same dissectors and filters), or reshape the output (tidy columns with grep, awk, sort, uniq, or parse -T json with jq).

Install, update, verify#

sudo apt install -y tshark                    # Debian / Ubuntu / Kali
sudo dnf install -y wireshark-cli             # Fedora / RHEL
brew install wireshark                        # macOS (includes tshark)
sudo apt install --only-upgrade -y tshark     # update

tshark -v | head -n1    # version + build
command -v tshark        # confirm it is on PATH
tshark -D                # list capture interfaces (self-test)

On Debian/Kali the installer offers to let non-root users capture. Say yes, then add yourself and re-login so live capture needs no sudo:

sudo usermod -aG wireshark "$USER"    # then log out/in, or run: newgrp wireshark

On Windows, install Wireshark from wireshark.org (it bundles the Npcap driver for live capture and drops tshark.exe in the install dir).

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-rRead a PCAP-YDisplay filter (Wireshark syntax)
-iCapture on an interface-fCapture filter (BPF) at the wire
-DList capture interfaces-wWrite raw packets to a file
-c NStop after N packets-a duration:NStop after N seconds
-T fieldsColumn output-eField to print (with -T fields)
-EField format (header, separator)-zStatistics tap (add -q)
-qSuppress per-packet output-VFull decoded field tree
-xHex + ASCII dump-nDisable name resolution (faster)
-t adAbsolute timestamps-bRing buffer (filesize: / files:)
--export-objectsCarve files (http,dir)-2Two-pass analysis

Cheat sheet#

Every build-up starts simple and adds one flag at a time, so you can stop at the line that does the job.

# Read a PCAP: stop at the line you need
tshark -r capture.pcap                                      # 1. every packet, one summary line
tshark -r capture.pcap -Y "http"                            # 2. + narrow to a protocol
tshark -r capture.pcap -Y "http.request" \
  -T fields -e http.host -e http.request.uri                # 3. + just the columns you want
tshark -r capture.pcap -Y "http.request" \
  -T fields -e http.host -e http.request.uri | sort -u      # 4. + dedupe

# Live capture: stop at the line you need
sudo tshark -i eth0                                         # 1. everything on the wire
sudo tshark -i eth0 -f "tcp port 80"                       # 2. + BPF filter at the source
sudo tshark -i eth0 -f "tcp port 80" -w web.pcap           # 3. + save raw to disk
sudo tshark -i eth0 -f "tcp port 80" -a duration:60 -w web.pcap   # 4. + auto-stop after 60s

# Discover
tshark -D                                 # list capture interfaces
tshark -r capture.pcap -q -z io,phs       # protocol hierarchy: what is in the file
tshark -r capture.pcap -q -z conv,tcp     # busiest TCP conversations

# Capture filters (BPF, at the wire, with -f)
sudo tshark -i eth0 -f "port 53"                  # by port
sudo tshark -i eth0 -f "host TARGET_IP"           # by host
sudo tshark -i eth0 -f "net 192.168.56.0/24"      # by subnet
sudo tshark -i eth0 -f "src host TARGET_IP"       # by direction
sudo tshark -i eth0 -f "udp"                      # by protocol
sudo tshark -i eth0 -f "not arp and not stp"      # negation

# Display filters (Wireshark syntax, after decode, with -Y)
tshark -r capture.pcap -Y "dns"                             # one protocol
tshark -r capture.pcap -Y "ip.addr == TARGET_IP"           # one host, either direction
tshark -r capture.pcap -Y "tcp.port == 445"                # one port
tshark -r capture.pcap -Y "http.response.code == 200"      # a field value
tshark -r capture.pcap -Y "tcp.flags.syn==1 && tcp.flags.ack==0"   # SYN, no ACK (scan)
tshark -r capture.pcap -Y 'frame contains "password"'      # payload keyword

# Extract clean columns
tshark -r capture.pcap -Y "dns.flags.response == 0" \
  -T fields -e ip.src -e dns.qry.name                      # who asked, for what
tshark -r capture.pcap -Y "http.request" \
  -T fields -e ip.src -e http.host -e http.request.uri     # source, host, path
tshark -r capture.pcap -Y "http.request" -T fields \
  -E header=y -E separator=, -e frame.time -e ip.src -e http.host   # CSV with a header row

# Streams, stats, loot
tshark -r capture.pcap -q -z follow,tcp,ascii,3    # reassemble stream index 3
tshark -r capture.pcap -q -z credentials           # cleartext creds it can spot
tshark -r capture.pcap -q -z endpoints,ip          # traffic totals per host
tshark -r capture.pcap -q -z io,stat,1             # bytes/packets per second
mkdir -p loot && tshark -r capture.pcap --export-objects http,loot   # carve HTTP files

# Read, re-filter, write files
tshark -r big.pcap -Y "dns" -w dns-only.pcap       # slice matching packets to a new file
sudo tshark -i eth0 -b filesize:10240 -b files:5 -w ring.pcap   # ring buffer, bounded disk

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Read a saved capture#

tshark -r capture.pcap

One summary line per packet. The fastest way to see what is in a file before you start filtering.

See what protocols a PCAP contains#

tshark -r capture.pcap -q -z io,phs

A protocol-hierarchy tree with packet and byte counts per layer. Start every triage here: if the traffic you want is not in the tree, it is not in the file.

Capture only web traffic to disk#

sudo tshark -i eth0 -f "tcp port 80" -w pcaps/web.pcap

-f is a BPF capture filter applied at the wire, so non-HTTP never hits disk and the file stays small. Read it back later with -r.

Filter a PCAP down to one protocol#

tshark -r capture.pcap -Y "dns"
tshark -r capture.pcap -Y "http.request or http.response"

-Y is protocol-aware (Wireshark syntax), so it understands fields that BPF cannot. Combine terms with and, or, and not.

List every DNS name that was looked up#

tshark -r capture.pcap -Y "dns.flags.response == 0" \
  -T fields -e ip.src -e dns.qry.name | sort -u

Two columns: who asked, and for what. response == 0 keeps queries and drops the answers; sort -u collapses repeats.

Map HTTP requests (who asked for what)#

tshark -r capture.pcap -Y "http.request" \
  -T fields -e ip.src -e http.host -e http.request.uri

Source IP, Host header, and path: a quick map of web activity in the capture.

Follow a TCP stream as readable text#

tshark -r capture.pcap -Y "http.request" -T fields -e tcp.stream | head   # find the index
tshark -r capture.pcap -q -z follow,tcp,ascii,3                           # reassemble index 3

Prints both sides of the conversation reassembled in order: the CLI equivalent of Wireshark’s “Follow TCP Stream.”

Pull cleartext credentials from a capture#

tshark -r capture.pcap -q -z credentials

A table of usernames and passwords TShark can spot in cleartext protocols (FTP, HTTP basic, IMAP, POP, SMTP). For FTP specifically, filter -Y "ftp.request.command == \"USER\" or ftp.request.command == \"PASS\"".

Carve files out of an HTTP session#

mkdir -p loot && tshark -r capture.pcap --export-objects http,loot

Reassembles and writes every file transferred over HTTP into loot/: images, downloads, exfiltrated documents. The same tap works for smb, tftp, and imf.

Extract the hostnames from TLS handshakes#

tshark -r capture.pcap -Y "tls.handshake.type == 1" \
  -T fields -e tls.handshake.extensions_server_name

The SNI in each Client Hello: where the encrypted sessions were headed, even though you cannot read the payloads.

Grep packet payloads for a keyword#

tshark -r capture.pcap -Y 'frame contains "password"'

frame contains matches the raw bytes of the whole frame, so it finds a string regardless of protocol. Reach for it when you do not yet know which field holds the value.

List the busiest conversations#

tshark -r capture.pcap -q -z conv,tcp

A table of TCP conversations with packet and byte totals per pair: the fast way to spot a bulk transfer or a beaconing host. Use conv,udp or endpoints,ip for other cuts.

Watch ARP and DHCP live on your subnet#

sudo tshark -i eth0 -Y "arp or dhcp"

Live view of who is joining, requesting leases, and announcing MACs: the L2/L3 chatter of a homelab segment.

Save a long capture without filling the disk#

sudo tshark -i eth0 -b filesize:10240 -b files:5 -w pcaps/ring.pcap

A ring buffer: up to 5 files of about 10 MB each (filesize is in KB), oldest overwritten. Runs indefinitely with a bounded footprint.

Slice a big PCAP into a smaller one#

tshark -r big.pcap -Y "ip.addr == TARGET_IP" -w host.pcap

-Y selects and -w writes only the matching packets to a new capture: hand a colleague the 200 packets that matter instead of a 2 GB file.

Produce CSV for a spreadsheet or script#

tshark -r capture.pcap -Y "http.request" -T fields \
  -E header=y -E separator=, -e frame.time -e ip.src -e http.host

-E header=y adds a header row and -E separator=, makes it CSV instead of the default TSV.

Inspect one packet’s raw bytes#

tshark -r capture.pcap -c 1 -V -x

-V prints the full decoded field tree, -x adds a hex and ASCII dump. Verify a field name here before trusting a whole -e column.

Reading the output#

The default is one summary line per packet:

 12   1.043   192.168.56.10 -> 192.168.56.1   DNS   74   Standard query 0x1a2b A example.com
ColumnIsNotes
No.Packet index in the fileJump back with -Y "frame.number == 12"
TimeSeconds since capture start-t ad shows absolute date/time instead
Source -> DestinationAddresses (IP, or MAC at L2)-n disables name resolution, faster and numeric
Protocol / LengthTop decoded protocol, frame bytesHighest layer the dissector reached
InfoThe dissector’s one-line summaryThe most useful column for scanning

Encrypted traffic decodes only to the transport: you see TLS, not the HTTP inside, unless you supply keys (-o "tls.keylog_file:keys.log").

Troubleshooting#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall; open a fresh shell
dumpcap ... Permission deniedNot in the capture groupsudo usermod -aG wireshark "$USER", re-login
tshark -D lists nothingNo permission or no interfacesUse sudo, or fix the wireshark group
-Y errors with a syntax messageBPF written where Wireshark syntax is expectedPut BPF in -f; use Wireshark syntax in -Y
-Y returns nothingProtocol absent or field name wrongConfirm with -q -z io,phs; verify names via -V
Live capture drops packetsHeavy dissection on a busy linkCapture raw with -w, analyze offline
Only TLS visible, no HTTP insideTraffic is encryptedExpected without keys; read the handshake only
Stats buried under packet linesMissing -qAdd -q alongside -z

Gotchas#

  • -f is BPF, -Y is Wireshark syntax: -f "tcp port 80" versus -Y "http.request". Swap them and you get a syntax error or silent empty output. This is the number-one time sink.
  • Capture filters cannot see application fields: BPF runs before dissection, so -f "http.host" is invalid. Capture broadly with -f "tcp port 80", then narrow with -Y on read.
  • Verify a field with -V before trusting a whole -e column: a wrong or renamed field name yields an empty column that reads like “no data,” not an error.
  • -w saves raw packets; -T fields prints to stdout: you cannot -w a field extraction. Redirect stdout (> out.tsv or | tee) to keep column output.
  • filesize is in kilobytes: -b filesize:10240 is about 10 MB, not 10 GB. Off by a thousand and the ring either fills or starves.
  • Every PCAP may hold secrets: scrub credentials, tokens, and PII out of extracted output before pasting it anywhere.

Pairs with#

tcpdump grabs raw packets on a minimal or constrained box, then you hand the PCAP to TShark for protocol-aware filtering and field extraction. Wireshark’s GUI opens the same file for visual stream-following when the terminal gets cramped; the two share dissectors and filter syntax. Pipe columns through grep, awk, sort, and uniq to tidy them, and jq to parse -T json output. When you only need to grab and rotate captures unattended, dumpcap (which ships with Wireshark) is the lean engine underneath.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop