Find Malicious Traffic in PCAPs

To find malicious traffic in PCAPs quickly, run a wide detector pass with Suricata and Zeek, then hunt each technique with targeted Wireshark display filters and tshark or Zeek one-liners: long or high-volume DNS names for tunneling, regular connection intervals for beaconing, unusual TLS handshakes, cleartext credentials, SMB and RDP fan-out for lateral movement, and outbound byte imbalance for exfiltration. Every hit is a lead that still needs packet-level confirmation.
This page is the command reference. For the investigation method behind it, read our playbook on how to find malicious traffic in a PCAP.
Cheat sheet at a glance
| Technique | Wireshark display filter | Command-line starting point | Main false positive |
|---|---|---|---|
| DNS tunneling | dns.qry.name.len > 50 | count unique subdomains per domain | CDNs, anti-malware lookups, telemetry |
| Beaconing | tcp.flags.syn == 1 && tcp.flags.ack == 0 | inter-connection intervals per pair | updaters, monitoring agents |
| Suspicious TLS | tls.handshake.type == 1 && !tls.handshake.extensions_server_name | Zeek ssl.log and x509.log | IP-only services, internal PKI |
| Cleartext credentials | ftp.request.command == "PASS" || http.authorization | tshark -z credentials | lab systems, legacy devices |
| Lateral movement | smb2.tree contains "$" | SYN fan-out to 445 and 3389 | admin jump hosts, deployment tools |
| Exfiltration | Statistics, Conversations | outbound minus inbound bytes per pair | backups, cloud sync, uploads |
Setup: one wide pass first
Run the detectors once and keep the output next to the capture:
mkdir -p suri
suricata -r capture.pcap -l ./suri
jq -r 'select(.event_type=="alert") | .alert.signature' suri/eve.json | sort | uniq -c | sort -rn | head
zeek -C -r capture.pcap local
The -C flag tells Zeek to ignore bad checksums, which are common in captures taken on the sending host because of checksum offload. The local argument loads the site policy, which includes certificate validation for ssl.log.
Before hunting, check that the capture includes both directions and the time window you expect. A one-sided capture breaks byte ratios and handshake checks. The Suricata documentation describes the EVE fields used above.
DNS tunneling
Tunnels encode data in query names or responses, which produces long names, many unique subdomains under one domain, and unusual record types.
Wireshark display filters:
dns.qry.name.len > 50
dns.qry.type == 16 || dns.qry.type == 10
dns.flags.rcode == 3
The second filter shows TXT and NULL queries; the third shows NXDOMAIN responses.
Count unique subdomains per parent domain with tshark:
tshark -r capture.pcap -Y "dns.flags.response == 0" -T fields -e dns.qry.name \
| sort -u | awk -F. 'NF>2{print $(NF-1)"."$NF}' | sort | uniq -c | sort -rn | head
The same from Zeek:
zeek-cut query < dns.log | sort -u | awk -F. 'NF>2{print $(NF-1)"."$NF}' | sort | uniq -c | sort -rn | head
False positives: CDNs, anti-malware cloud lookups, and telemetry services generate long and unique labels legitimately. The two-label awk split also groups names under shared suffixes such as co.uk, so check the top results by hand. Confirm a tunnel by looking at query rate, payload-like labels, and response sizes for one domain.
Beaconing
Beacons connect on a regular interval, often with small and similar request sizes. Start by finding pairs with many connections:
zeek-cut id.orig_h id.resp_h id.resp_p < conn.log | sort | uniq -c | sort -rn | head -20
Then measure the interval between connection starts for one pair:
tshark -r capture.pcap \
-Y "ip.src == 10.0.0.25 && ip.dst == 203.0.113.10 && tcp.flags.syn == 1 && tcp.flags.ack == 0" \
-T fields -e frame.time_epoch \
| awk 'NR>1{printf "%.1f\n", $1-p} {p=$1}' | sort -n | uniq -c | sort -rn | head
A tight cluster of intervals, for example most values near 60 seconds with a few seconds of jitter, is a beaconing lead. In Wireshark, Statistics > I/O Graphs filtered to the pair shows the pattern visually. For long captures, dedicated tools such as RITA score beacon likelihood across Zeek logs.
False positives: software updaters, EDR and monitoring agents, NTP, mail clients, and health checks all beacon by design. Compare the host with its peers. A pattern seen on every workstation is likely an approved agent; a pattern on one host to a rare destination deserves more attention.
Suspicious TLS
Without decryption, focus on handshake properties and certificates.
tls.handshake.type == 1 && !tls.handshake.extensions_server_name
tls.handshake.type == 1 && !(tcp.dstport in {443, 8443})
tls.alert_message
The first shows ClientHellos without a server name, the second shows TLS on unexpected ports. Recent Wireshark releases also expose a JA4 field, tls.handshake.ja4, that you can add as a column to group clients.
From Zeek, list rare server names and find self-signed certificates:
zeek-cut server_name < ssl.log | sort | uniq -c | sort -n | head -20
zeek-cut certificate.subject certificate.issuer < x509.log | awk -F'\t' '$1==$2' | sort -u
zeek-cut id.orig_h server_name validation_status < ssl.log | awk -F'\t' '$3 != "ok" && $3 != "-"'
False positives: internal services often use private CAs or self-signed certificates, and some legitimate clients connect by IP without SNI. TLS 1.3 encrypts the certificate, so x509.log mostly reflects TLS 1.2 and earlier sessions. Fingerprints are correlation keys, not malware verdicts. The sibling guide on TLS analysis solutions for 2026 covers what remains visible without secrets.
Cleartext credentials
Credentials in clear text indicate either a weak protocol in use or an attacker harvesting them.
ftp.request.command == "USER" || ftp.request.command == "PASS"
http.authorization
urlencoded-form.key contains "pass"
ldap.simple
telnet
ntlmssp.messagetype == 0x00000003
The last filter shows NTLM authenticate messages, which carry the user and domain names, though not a cleartext password. Wireshark's Tools > Credentials collects supported credentials in one list. The tshark equivalent:
tshark -r capture.pcap -q -z credentials
Zeek records FTP usernames in ftp.log; passwords show as <hidden> unless the user is anonymous or password capture is enabled. HTTP basic authentication usernames appear in http.log, and password logging is also off by default.
False positives: lab networks, printers, and legacy industrial devices often use cleartext protocols legitimately. The finding is still a risk, but not necessarily an intrusion. Handle any recovered credential as sensitive data.
SMB and RDP lateral movement
Look for administrative shares, remote service creation, and one host contacting many peers.
smb2.tree contains "ADMIN$" || smb2.tree contains "C$"
smb2.cmd == 5 && smb2.filename contains ".exe"
svcctl
tcp.dstport == 3389 && tcp.flags.syn == 1 && tcp.flags.ack == 0
tcp.dstport in {5985, 5986}
These cover admin share mounts, executable file creates over SMB2, the Windows service control RPC interface often used by remote execution tools, new RDP connections, and WinRM.
Find fan-out, meaning sources that open SMB connections to many distinct hosts:
tshark -r capture.pcap -Y "tcp.flags.syn == 1 && tcp.flags.ack == 0 && tcp.dstport == 445" \
-T fields -e ip.src -e ip.dst | sort -u | cut -f1 | uniq -c | sort -rn | head
Zeek gives the same picture in structured form:
zeek-cut id.orig_h id.resp_h path < smb_mapping.log | sort -u
zeek-cut id.orig_h id.resp_h endpoint operation < dce_rpc.log | grep -i svcctl
zeek-cut id.orig_h id.resp_h cookie result < rdp.log
The RDP cookie field often holds the username from the connection request.
False positives: domain controllers, jump hosts, vulnerability scanners, backup servers, and software deployment tools all touch admin shares and many peers. Build a short list of known administrative sources before hunting. Also check the capture point, because an internet-edge capture usually cannot see same-subnet SMB.
Exfiltration volume
Look for sessions where the internal host sends far more than it receives.
tshark -r capture.pcap -q -z conv,tcp
zeek-cut id.orig_h id.resp_h orig_bytes resp_bytes < conn.log \
| awk '{k=$1" -> "$2; o[k]+=$3; r[k]+=$4} END{for(k in o) if(o[k]>r[k]) print o[k], r[k], k}' \
| sort -rn | head
In Wireshark, Statistics > Conversations sorted by bytes from A to B shows the same. Also check covert channels:
icmp && data.len > 64
dns.qry.name.len > 50 && ip.src == 10.0.0.25
False positives: backups, cloud file sync, video calls, and software uploads are large and outbound by design. Zeek derives orig_bytes from TCP sequence numbers, so a capture with missing packets can inflate it; compare with orig_ip_bytes. Volume shows that data moved, not what the data was.
From hit to finding
Each command above produces candidates. Turn one into a finding by checking the full session in Wireshark, the activity before and after, the peers that behave the same way, and the benign explanations listed for that technique. Write down what the capture cannot see.
When the hunt produces more leads than you can verify by hand, PacketSafari malicious-traffic analysis runs capture-wide Triage across these techniques and more, then verifies the conclusions against exact packets with explicit coverage. It investigates a selected PCAP and complements live NDR and EDR rather than monitoring continuously. For forensic reporting and chain of custody, see the sibling guide on PCAP analysis solutions for network forensics.
