How to check a PCAP against your threat-intel (IOC) lists

An indicator list is only useful if it is checked against the traffic you actually have. Matching a capture by hand is quick for one list and one file, but the details matter: subdomains, address ranges, names that only appear in TLS, and files that only show up as bytes in a stream.

By Stuart Mathieson8 minute readtshark + shellUpdated 12 October 2026
The quick way

In PCAPNG Analyzer Pro you paste or load a list once, in whatever format the feed uses, and every capture is checked against it automatically: addresses, domains in DNS, TLS, QUIC and HTTP, file hashes and fingerprints. Re-scan applies a new list to the captures you’ve already analysed.

See a sample incident report →
In this guide
  1. Prepare the lists
  2. Match addresses
  3. Match domain names
  4. Match file hashes
  5. Match TLS and SSH fingerprints
  6. Judge a hit
  7. Manual vs automated workflow

1. Prepare the lists

Feeds and incident reports rarely arrive in a form a filter accepts. Before matching, put one indicator per line and split them by type: ips.txt (addresses and CIDR ranges), domains.txt, hashes.txt (SHA-256) and fingerprints.txt (JA4, JA3 or HASSH). Along the way:

2. Match addresses

tshark accepts a set of addresses and ranges, separated by commas. paste builds it from the file:

tshark -r capture.pcapng -Y "ip.addr in {$(paste -sd, ips.txt)}" \
  -T fields -e frame.time -e ip.src -e ip.dst -e _ws.col.Protocol

CIDR ranges such as 203.0.113.0/24 work inside the set. Use ipv6.addr for an IPv6 list. A match tells you an address appeared in a packet, in either direction; the judging step decides what that means.

3. Match domain names

A name can appear in three places: a DNS query, a TLS or QUIC server name, and an HTTP Host header. Check all three, and match subdomains too: a listing for evil.example should catch cdn.evil.example. This builds one filter covering all of them from domains.txt:

re=$(sed 's/\./\\./g' domains.txt | paste -sd'|')
filter=$(printf 'dns.qry.name matches r"(^|\\.)(%s)$" || tls.handshake.extensions_server_name matches r"(^|\\.)(%s)$" || http.host matches r"(^|\\.)(%s)$"' "$re" "$re" "$re")
tshark -r capture.pcapng -Y "$filter" \
  -T fields -e frame.time -e ip.src -e dns.qry.name \
  -e tls.handshake.extensions_server_name -e http.host

The r"..." raw strings keep the regular expression's backslashes intact. When a DNS query matches, note the addresses in its answer and match those too: the connection that follows a lookup is the activity that matters, and it shows only an address.

4. Match file hashes

File hashes can only be matched for files that crossed the wire in clear text, which in practice means plain HTTP. Export every HTTP object, hash the files and compare:

tshark -r capture.pcapng -q --export-objects http,objects
sha256sum objects/* | grep -i -F -f hashes.txt

A file must be complete to match. A download that the capture caught only part of, or one cut short, hashes differently from the original. Files fetched over HTTPS are invisible to this step, however the list is written.

5. Match TLS and SSH fingerprints

Fingerprints identify client software rather than infrastructure, so they keep matching when an attacker moves servers. Quote each value and test the fields that apply:

set=$(sed 's/.*/"&"/' fingerprints.txt | paste -sd,)
tshark -r capture.pcapng \
  -Y "tls.handshake.ja4 in {$set} || tls.handshake.ja3 in {$set} || ssh.kex.hassh in {$set}" \
  -T fields -e frame.time -e ip.src -e ip.dst -e tls.handshake.extensions_server_name

The JA3 and JA4 guide explains why a JA3 from a modern browser rarely matches twice.

6. Judge a hit

A match is the start of the investigation. For each hit, find the conversation and answer:

A list is someone else's judgement.

Feeds contain errors, stale entries and over-broad ranges. Record which list and which entry produced each hit, so the conclusion can be traced and challenged later.

Manual method vs PCAPNG Analyzer

Manual method

  1. Clean and split each list by type.
  2. Run address, name, hash and fingerprint filters.
  3. Follow the DNS answers to the connections that came next.
  4. Judge each hit by direction, completion and volume.
  5. Repeat for every new capture and every list update.

With PCAPNG Analyzer

  1. Create a list in Settings → Threat Intel, and paste or load the indicators, defanged or not.
  2. Analyse captures as usual: a match raises a finding naming the list, the indicator and where it appeared. Re-scan all checks older captures.
  3. Follow the stream or export the matching packets, and click the indicator to see every capture it appears in.
What the app matches

Lists accept the formats feeds actually use: bare addresses (with or without a port), CIDR ranges, domains (each covering its subdomains), URLs reduced to their host, defanged indicators, hosts-file blocklists and CSV rows. They also accept SHA-256 file hashes and JA3, JA4 and HASSH fingerprints. Matching covers IP traffic, DNS lookups, TLS and QUIC server names, HTTP Host headers, the hashes of files transferred over plain HTTP, and TLS and SSH client fingerprints. Each list has its own severity, and the finding takes the most severe list that matched. Lists are only ever imported by you; the app never fetches a feed, and nothing about your traffic leaves the server.

Check every capture against your own intelligence

Import your lists once and every capture is checked automatically, with the lists and the traffic kept on your own computer or server.

Compare Free and ProSelf-host it