Offline passive OT PCAP triage helper using tshark.
Project description
ot-pcap-triage
Offline passive OT PCAP triage helper using tshark.
ot-pcap-triage reads existing .pcap / .pcapng files and generates a lightweight triage report for passive OT/ICS network assessment.
It does not actively scan, inject packets, or connect to any target.
What it does
ot-pcap-triage helps analyze passive OT traffic by generating:
- HTML and Markdown reports;
- finding candidates and review points;
- endpoint and conversation summaries;
- passive asset inventory;
- industrial protocol observations;
- risky/external communication indicators;
- redacted sensitive/plaintext evidence;
- timeline and communication graphs;
- suggested metadata for follow-up context-aware analysis.
The tool is intended for triage and analyst support, not automatic vulnerability confirmation.
Fedora install
sudo dnf install -y git wireshark-cli python3 python3-pip graphviz p7zip p7zip-plugins
Debian / Kali install
sudo apt install -y git tshark wireshark-common python3 python3-pip graphviz p7zip-full
Python install with virtualenvwrapper
Clone the repository:
git clone https://github.com/h0ek/ot-pcap-triage
cd ot-pcap-triage
Create and install in a virtualenv:
mkvirtualenv ot-pcap-triage -p python3
pip install -e .
Verify:
ot-pcap-triage --help
ot-pcap-triage --check-deps
Basic usage
PCAP-only mode:
ot-pcap-triage capture.pcapng
PCAP with factory/site metadata:
ot-pcap-triage capture.pcapng --metadata capture.yml
Custom output directory:
ot-pcap-triage capture.pcapng -o output/test1
More evidence frames per rule:
ot-pcap-triage capture.pcapng --max-evidence 25
Graph size tuning:
ot-pcap-triage capture.pcapng --graph-top-n 25
Dependency-only preflight:
ot-pcap-triage --check-deps
Metadata template
A default metadata template is provided:
metadata_template.yml
Copy it for a specific capture:
cp metadata_template.yml plant1-line2-capture.yml
nano plant1-line2-capture.yml
Use it:
ot-pcap-triage capture.pcapng --metadata plant1-line2-capture.yml -o output/plant1-line2
Recommended workflow
Best workflow when the factory/site can provide context:
1. Ask the factory/site to fill metadata_template.yml.
2. Run PCAP-only mode first:
ot-pcap-triage capture.pcapng -o out-pass1
3. Review out-pass1/suggested_metadata.yml.
4. Compare observed/inferred data with the factory-provided metadata.
5. Create reviewed metadata:
cp factory_metadata.yml reviewed_metadata.yml
nano reviewed_metadata.yml
6. Add useful observations from suggested_metadata.yml manually.
7. Run context-aware mode:
ot-pcap-triage capture.pcapng --metadata reviewed_metadata.yml -o out-pass2
8. Use out-pass2/report.html and findings.json for manual validation.
When no metadata exists:
ot-pcap-triage capture.pcapng -o out-pass1
cp out-pass1/suggested_metadata.yml reviewed_capture.yml
nano reviewed_capture.yml
ot-pcap-triage capture.pcapng --metadata reviewed_capture.yml -o out-pass2
Metadata behavior
factory metadata = expected architecture
suggested_metadata.yml = observed/inferred helper
reviewed_metadata.yml = human-reviewed combination
The generated suggested_metadata.yml is not authoritative. It must be reviewed.
If --metadata is provided, the provided metadata is used for expected-vs-observed checks, but suggested_metadata.yml is still generated as observed/inferred enrichment.
Output files
Default output directory:
output/<pcap-name>-<timestamp>/
report.html
report.md
summary.json
findings.json
endpoints.csv
conversations.csv
protocols.csv
sensitive_hits_redacted.csv
suggested_metadata.yml
commands_used.txt
timeline_packets.png
communication_graph.png
industrial_risk_graph.png
industrial_protocol_graph.png
external_communication_graph.png
Some files may be missing if there is no matching data or an optional renderer is unavailable. For example, graph images require Graphviz dot.
Report sections
The HTML and Markdown reports include:
- PCAP Summary;
- Baseline Mode Notice, when no metadata YAML was provided;
- NIST SP 800-82r3 Review Context;
- MITRE ATT&CK for ICS Review Context;
- Top Risky Observations;
- Observed Industrial Protocols;
- Top S7/COTP Communication Paths, when S7 traffic is observed;
- Extended Protocol Candidates;
- Traffic Timeline;
- Communication Graph;
- Industrial / Risk Communication Graph;
- Industrial Protocol Graph;
- External Communication Graph;
- Graph Legend;
- Passive Asset Inventory;
- Cleartext Management Surface;
- IT/OT Convergence Indicators;
- Engineering / Scanner Candidates;
- Analyst Questions;
- Capture Quality Notes;
- Finding Candidates;
- Top Endpoints;
- Top Conversations;
- Rule Results;
- Protocol Hierarchy Raw.
Baseline mode without metadata
When no metadata YAML is provided, the tool still reports generic OT/ICS best-practice review points.
Examples:
- public DNS;
- public NTP;
- VPN-like traffic;
- plaintext protocols;
- discovery/name-resolution noise;
- TCP connection anomalies;
- observed industrial protocols.
Without metadata, these are usually candidate or informational observations that require validation with the local OT/site owner.
Metadata does not make the tool “work”; metadata increases confidence and enables expected-vs-observed checks.
Finding candidates
A finding candidate is not automatically a confirmed vulnerability.
Typical statuses:
| Status | Meaning |
|---|---|
candidate |
Potential issue detected automatically. |
confirmed_by_rule |
Strong direct evidence, for example cleartext credential indicator. |
informational |
Useful observation, not a finding by itself. |
Manual validation is required.
OT enrichment
The report includes OT-specific enrichment sections:
Passive Asset Inventory— inferred roles based on observed protocols and ports;Cleartext Management Surface— Telnet/FTP/HTTP/SNMP/RSH-like management exposure candidates;IT/OT Convergence Indicators— public communication, VPN-like traffic, cross-subnet industrial communication;Engineering / Scanner Candidates— hosts that initiate industrial protocol communication to controllers/devices.
These sections are heuristic. Without metadata, they are review points. With metadata, they help validate expected-vs-observed architecture.
Protocol catalog enrichment
protocol_catalog.yml contains a curated list of industrial, utility, and building automation protocol port mappings.
The catalog is used for enrichment only:
- it helps label possible industrial/building/utility protocol candidates;
- it does not automatically create high-severity findings;
- matches based only on ports are shown as candidates;
- analyst validation is required.
The report section Extended Protocol Candidates shows catalog matches with:
- protocol name;
- category;
- matched port;
- packet/byte counters;
- confidence;
- note.
This is useful when a protocol is not decoded by Wireshark/tshark but the traffic uses a known industrial port.
NIST SP 800-82r3 review context
Reports include an optional NIST SP 800-82r3 Review Context section.
This maps observed PCAP findings to high-level OT security themes such as:
- network segmentation and boundary protection;
- remote access control;
- plaintext protocol exposure;
- anomaly and event monitoring;
- industrial protocol security;
- asset inventory validation;
- IT/OT convergence review.
This is not a compliance certification. It is analyst guidance that helps connect packet evidence to recognized OT security review areas.
Source: https://doi.org/10.6028/NIST.SP.800-82r3
MITRE ATT&CK for ICS review context
Reports include an optional MITRE ATT&CK for ICS Review Context section.
This maps observed PCAP findings to related ATT&CK for ICS techniques to consider, such as:
- Remote Services;
- Internet Accessible Device;
- Remote System Discovery;
- Port Scan;
- Broadcast Discovery;
- Multicast Discovery;
- Standard Application Layer Protocol;
- Commonly Used Port;
- Rogue Master;
- Unauthorized Message;
- Program Upload / Program Download context where later evidence supports it.
This is not proof of adversary activity. It is a contextual mapping to help analysts think about how observed network behavior could relate to known adversary techniques.
MITRE context is generated from:
mitre_ics_context.yml
Source: https://attack.mitre.org/matrices/ics/
Traffic timeline
Reports include a Traffic Timeline section with a static PNG chart generated from the PCAP.
Behavior:
- bucket size is selected automatically;
- captures up to roughly 2 hours use 1-minute buckets;
- larger captures use 5-minute buckets;
- series shown: S7/COTP, DNS, NTP, ICMP, VPN, Discovery, and Other.
Output file:
timeline_packets.png
Communication graphs
Reports include Graphviz-based static communication maps:
communication_graph.png— top communication paths by packet count;industrial_risk_graph.png— industrial, external, or otherwise high-interest paths;industrial_protocol_graph.png— industrial-only communication view;external_communication_graph.png— public/external communication view.
Graph behavior:
- graph size is controlled with
--graph-top-n; - node labels include IP plus a short inferred role when available;
- nodes are grouped into subnet clusters;
- edge labels show protocol, packet count, and byte count;
- graph legend is included in the report.
Public test PCAPs
Public ICS/SCADA PCAPs are available at Netresec:
https://www.netresec.com/?page=PcapFiles
Useful sections:
SCADA/ICS Network Captures;4SICS ICS Lab PCAP files;DigitalBond S4x15 ICS Village CTF PCAPs.
Performance notes
Main packet aggregation is streamed from tshark line-by-line instead of loading all packet rows into memory.
This reduces Python memory usage for large PCAP files.
The tool still runs additional tshark passes for rule evidence and counts, so large files can still take time.
Temporary partial files are written to:
output/<pcap-name>/.work/
The .work directory is removed after a successful run. If the tool crashes, it may remain for debugging.
Limitations
- Passive PCAP only.
- Traffic not visible at the capture point cannot be assessed.
- Short captures may miss scheduled jobs, vendor access, or engineering activity.
- SPAN/mirror oversubscription may cause missing packets.
- Encrypted payloads cannot be fully inspected.
- Findings are candidates/review points until manually validated.
- Absence of evidence is not evidence of absence.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ot_pcap_triage-0.3.8.tar.gz.
File metadata
- Download URL: ot_pcap_triage-0.3.8.tar.gz
- Upload date:
- Size: 46.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3618eff444e3c19610d3cb5d059a21b4424bdb5d3ffcbeb9b03a1d2424badd2
|
|
| MD5 |
1468bf2452989904450eaff7aeff1f5d
|
|
| BLAKE2b-256 |
503189b6b8197cb16b3842b66c8f4bbb5134e3a14f0db423612903c7bf3f1af9
|
Provenance
The following attestation bundles were made for ot_pcap_triage-0.3.8.tar.gz:
Publisher:
publish.yml on h0ek/ot-pcap-triage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ot_pcap_triage-0.3.8.tar.gz -
Subject digest:
c3618eff444e3c19610d3cb5d059a21b4424bdb5d3ffcbeb9b03a1d2424badd2 - Sigstore transparency entry: 1463107825
- Sigstore integration time:
-
Permalink:
h0ek/ot-pcap-triage@0adb87062c59e56bebfe3c70ffadcda2959257d8 -
Branch / Tag:
refs/tags/v0.3.8 - Owner: https://github.com/h0ek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0adb87062c59e56bebfe3c70ffadcda2959257d8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ot_pcap_triage-0.3.8-py3-none-any.whl.
File metadata
- Download URL: ot_pcap_triage-0.3.8-py3-none-any.whl
- Upload date:
- Size: 49.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63421eb7e87a831c2c8d32b7dad053e036dd65dc70fbcabee359bb8c0db10ee0
|
|
| MD5 |
16b7ded6dfc82198f56ffb2e8fa58068
|
|
| BLAKE2b-256 |
a1192d656515eb6b26bf7aed2362a2b237f8ca270723cb48bdf62fa56c789373
|
Provenance
The following attestation bundles were made for ot_pcap_triage-0.3.8-py3-none-any.whl:
Publisher:
publish.yml on h0ek/ot-pcap-triage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ot_pcap_triage-0.3.8-py3-none-any.whl -
Subject digest:
63421eb7e87a831c2c8d32b7dad053e036dd65dc70fbcabee359bb8c0db10ee0 - Sigstore transparency entry: 1463107845
- Sigstore integration time:
-
Permalink:
h0ek/ot-pcap-triage@0adb87062c59e56bebfe3c70ffadcda2959257d8 -
Branch / Tag:
refs/tags/v0.3.8 - Owner: https://github.com/h0ek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0adb87062c59e56bebfe3c70ffadcda2959257d8 -
Trigger Event:
push
-
Statement type: