Skip to main content

Triage d'incident reseau/app/OS : analyse une capture et rend un verdict argumente avec preuves

Project description

netverdict

PyPI Python CI License

The packet capture says whether the problem is the network, the application or the host โ€” with the evidence, and a suggested fix.

๐Ÿ‡ซ๐Ÿ‡ท Version franรงaise : README.fr.md

No more "it's the network / it's the app / it's the server" blame game. netverdict reads a pcap (plus, when available, a snapshot of the host state taken at the same moment), extracts the TCP signals that don't lie, and returns an argued verdict:

$ netverdict analyze capture.pcapng --lang en

18 packets read โ€” 18 TCP, 0 ICMP, 0 non-IP, 0 unreadable โ€” 1 conversations
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  APP โ€” 10.0.0.42:51006 -> 10.0.0.5:5432 [high confidence] โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Slow application response, reception proven by a fast ACK                   โ”‚
โ”‚   * 3 exchanges: server ACK in 5 ms but response in 800 ms (p50), 0 loss    โ”‚
โ”‚                                                                             โ”‚
โ”‚ Suggested fix:                                                              โ”‚
โ”‚   The network delivered the request (immediate ACK) and then waited for     โ”‚
โ”‚   the application. Look on the APPLICATION SIDE of 10.0.0.5:5432:           โ”‚
โ”‚   1. Internal processing time (application logs at the same timestamp)      โ”‚
โ”‚   2. Downstream dependencies: database, third-party API, DNS resolution     โ”‚
โ”‚      done BY the server โ€” the stall is often one hop further back           โ”‚
โ”‚   3. Exhausted thread/connection pool (requests queuing up).                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The reasoning is what an expert applies when reading a pcap in Wireshark โ€” encoded in a deterministic rule engine:

Observed signature Verdict
Repeated SYN with no answer NETWORK (silent DROP or unreachable host)
ICMP admin-prohibited NETWORK (explicit REJECT, the device is identified)
Immediate RST to SYN APP (nothing listening on that port)
Massive retransmissions NETWORK (loss on the path)
Zero window HOST (the application stopped reading its socket)
Fast ACK but slow response APP (the delay is inside the server, evidence attached)
ICMP fragmentation-needed NETWORK (MTU/tunnel)
RST mid-session AMBIGUOUS (firewall timeout, IPS, or crash โ€” who sent it?)

Install

pip install netverdict                # analysis: no system dependency
pip install "netverdict[explain]"     # + narrative summary via the Claude API (optional)
pip install "netverdict[evtx]"        # + direct reading of binary .evtx files

From source (to contribute):

git clone https://github.com/serytia/netverdict
cd netverdict
pip install -e ".[dev]"
pytest

100% Python (dpkt). No Wireshark/tshark needed, neither on the analysis machine nor on the servers.

Output language

Console output defaults to French (the tool's original audience). One flag โ€” or one environment variable, so you never have to repeat it โ€” switches everything a human reads to English:

netverdict analyze capture.pcap --lang en
export NETVERDICT_LANG=en                 # Windows: $env:NETVERDICT_LANG="en"

--lang translates verdict titles, evidence, suggested fixes, the timeline, --help, error messages, and the language requested from the model by --explain. --lang beats $NETVERDICT_LANG, which beats the default.

What --lang deliberately never changes: the verdict tokens (RESEAU, APP, OS, HOTE, AMBIGU, RAS), confidence values and JSON keys. Those are identifiers, not prose: they appear in user --rules files and in scripts that filter the --json output. Translating them would break a verdict == "RESEAU" check the day someone exports NETVERDICT_LANG=en โ€” with no signal at all. The displayed console label follows the language (NETWORK, HOST...); the data doesn't move.

Custom rules (--rules my-rules.yaml) accept sibling fields title_en / evidence_en / remediation_en. A rule without a translation falls back to its French text whatever the requested language, without error.

Usage

# Analyze an existing capture
netverdict analyze capture.pcapng
netverdict analyze capture.pcapng --json          # machine output
netverdict analyze capture.pcapng --explain       # + narrative summary (Claude API)

# Cross-reference with what changed in the infrastructure (timeline)
netverdict analyze capture.pcapng --events events.xml --syslog fw01.log
#   --events : Windows events (.evtx with the [evtx] extra, or an XML export:
#              wevtutil qe System /f:xml > events.xml)
#   --syslog : flat syslog files (mixed RFC3164/RFC5424 accepted)

# Timezone of RFC3164 lines (the format carries NONE). Required whenever the
# syslog does not come from a machine set like the analysis host: otherwise
# events shift out of the window, silently.
netverdict analyze capture.pcapng --syslog central.log --syslog-tz UTC
netverdict analyze capture.pcapng --syslog fw01.log    --syslog-tz Europe/Paris
netverdict analyze capture.pcapng --syslog fw01.log    --syslog-tz +02:00
#   UTC / IANA name / fixed offset. The IANA name handles DST.
#   No effect on RFC5424 lines, which carry their own timezone.

# WHERE are packets getting lost? Two captures of the same traffic, two points.
netverdict compare upstream.pcap downstream.pcap
#   upstream = near the client, downstream = near the server, captured
#   SIMULTANEOUSLY. A segment seen upstream and missing downstream was lost
#   BETWEEN the two points; if all are accounted for, the middle path is
#   cleared and the search moves past it. It is the only way to settle the
#   question without guessing. The two machines' clocks do not need to be
#   synchronized: the offset is estimated, and the tool stays silent about
#   latency rather than inventing one when it cannot estimate it.

# WHO owned the socket? The answer EVEN IF the process is already dead.
netverdict analyze capture.pcap --audit /var/log/audit/audit.log   # Linux
netverdict analyze capture.pcapng --events sysmon.xml              # Windows (Sysmon)
netverdict analyze capture.pcapng --events security.xml            # Windows (native WFP)
#   WFP = native Windows auditing, with NO agent to install:
#     auditpol /set /subcategory:"Filtering Platform Connection" /success:enable
#     wevtutil qe Security /f:xml > security.xml
#     auditpol /set /subcategory:"Filtering Platform Connection" /success:disable
#   (very verbose: enable only for the duration of the diagnosis. 5157 also
#    names the process whose connection got BLOCKED โ€” Sysmon stays silent.)
#   The host snapshot is taken at ONE instant: it misses a process that
#   exited before the capture ended. A journal dates every connection at
#   establishment time โ€” attribution becomes retroactive.
#   Linux  : rule to load (once) โ€”
#            auditctl -a always,exit -F arch=b64 -S connect -k netverdict_connect
#            (persistent: a file in /etc/audit/rules.d/)
#   Windows: Sysmon with NetworkConnect enabled โ€”
#            sysmon -c netverdict/capture/sysmon-netverdict.xml
#   If the source is present but the rule is missing, the tool SAYS SO
#   instead of returning a silent report.
# The report adds the changes from the 15 minutes before the capture
# (service installed, firewall rule reloaded, switch to battery power...)
# and flags those that closely precede the incident.
#
# Relevant changes are ALSO attached to the affected flow, inside its verdict
# panel, under "Check first". A `*` marks a change type that can produce that
# exact verdict (firewall rule -> NETWORK, service crash -> APP,
# battery -> OS). It is a RANKING of suspects, never a causality conclusion:
# unrelated changes stay listed, further down.

# Assisted capture: traffic + host state in one go (admin/root console)
netverdict capture --duration 60                  # Windows: pktmon (native) / Linux: tcpdump

# List the verdict rules
netverdict rules

Exit code: 0 = nothing abnormal, 1 = at least one verdict, 2 = error.

Assisted capture is truncated by default (128 bytes/packet on Windows, 96 on Linux): enough for the analysis, and light.

This is NOT a guarantee of credential absence, contrary to what this README claimed before 2026-07-25. Truncation cuts at N bytes from the start of the frame โ€” a packet shorter than N is therefore captured IN FULL, payload included. Measured:

Payload -s 96 128 B
PASS hunter2 (cleartext POP3/FTP) complete complete
USER admin + PASS ... complete complete
{"token":"eyJhbGciOi..."} complete complete
Authorization: Basic ... header (51 B) 42/51 B complete

In other words: truncation removes large transfers, not short secrets โ€” and cleartext authentication protocols are precisely short. Treat a bundle as sensitive data: review it before sharing it, and reserve --full-packets for when it is necessary and deliberate.

--explain never sends the pcap: only the JSON report (signals and verdicts).

How it works

Two strictly separated stages, like decoders/rules in Wazuh:

  1. Measurement (pcap.py, flows.py, signals.py): reading the capture, rebuilding TCP conversations, computing the signals โ€” retransmissions (excluding capture duplicates and keepalives), RTT, zero window, application request->response delay, server ACK delay, attached ICMP. Facts only, no judgment.
  2. Verdict (rules/): declarative YAML rules โ€” conditions on the signals, verdict, confidence, interpolated evidence, written remediation. Every threshold is commented with its justification.

Add your own rules: netverdict analyze ... --rules my_rules.yaml (same format as netverdict/rules/builtin.yaml).

Platforms

Analysis (analyze) Assisted capture Process <-> flow join
Linux yes capture.sh (tcpdump + ss) --audit (auditd)
Windows yes capture.ps1 (pktmon, native) --events: Sysmon EID 3, or WFP 5156/5157 with no agent
macOS yes no โ€” capture with tcpdump, then analyze no

compare (two captures, two points) works on all three.

CI: Linux/Windows/macOS x Python 3.11-3.13, plus one job under a shifted timezone, one with the extras installed, one against the built package.

Validation status

  • Validated: 323 automated tests, green on Linux, Windows and macOS (Python 3.11 to 3.13) and under a shifted timezone.
  • Validated against a kernel: 8 failure scenarios reproduced by a real Linux kernel (netem, iptables, real sockets โ€” lab/), plus the auditd join against a real auditd journal. The resulting pcaps serve as fixtures.
  • Validated on a real incident: full Windows capture chain (pktmon -> analysis) against a slow service, a closed port and a filtered port โ€” all three verdicts exact.
  • Not done yet: production incidents suffered (not provoked). Verdicts are an instrumented starting point, not an oracle โ€” AMBIGUOUS is an owned verdict, and the report says what it could not read.

What these validations cost, and why they are listed here: each one found defects that tests on fabricated data could not see โ€” retransmission detection broken by TSO/GSO, pktmon announcing one frame type and writing another, auditd whose default format is not the one in its documentation, and Windows failures triggered by Linux data. Hand-written fixtures describe the tool you imagine; real execution describes the one that exists.

Known limits (v1)

  • TCP/IPv4-IPv6 only (no UDP/QUIC, no fragment reassembly).
  • RTT p95 polluted by delayed ACKs (~40-200 ms): min and p50 are reliable. No rule therefore returns a NETWORK verdict on p95 alone. A high p95 is not ignored either: a healthy median with a significant tail produces an explicit AMBIGUOUS verdict ("latency spikes the capture cannot attribute") that names both possible causes โ€” path jitter or delayed ACK โ€” and gives what is needed to separate them. Neither a false network verdict, nor a false "transport healthy".
  • Client/server direction estimated heuristically if the capture starts mid-session (flagged in the report).
  • The host snapshot comes from a single machine (the one where the capture was launched). It is taken at ONE instant: it misses a process already dead by the end of the capture. The Sysmon join (Event ID 3) is retroactive and recovers it.
  • Process <-> flow join: match on the EXACT four-tuple (both directions tested), TCP only, with 60 s of clock tolerance between capture and journal. A client port reused during the capture yields several candidates: the closest to the flow start is kept, and the report flags the ambiguity rather than hiding it.
  • RFC3164 syslog (no timezone): by default timestamps are interpreted in the analysis machine's timezone, and the affected timestamps are marked ~ in the report. A UTC source read from a machine on local time then shifts OUT of the window, and the report shows "no infrastructure changes detected" โ€” to be read as "nothing was retained", not "nothing changed". Fix with --syslog-tz (see Usage): timestamps become exact, the ~ disappears and the delay before the incident is given to the second.
  • --syslog-tz with a FIXED offset (+02:00) is wrong on either side of a DST switch: a file that crosses the fall-back transition will be misdated on one half. Prefer an IANA name (Europe/Paris), which handles DST. On an ambiguous hour (the one that exists twice at fall-back), the first occurrence is kept.

Roadmap

  • v1.1 (done): multi-source timeline โ€” Windows events (EVTX/XML) + syslog to answer "what changed in the infrastructure just before?".

  • v1.2 (done): --syslog-tz, change->verdict correlation, retroactive process<->flow join via Sysmon Event ID 3. To enable the source (admin console):

    sysmon -i -accepteula <path>\netverdict\capture\sysmon-netverdict.xml
    

    This configuration enables ONLY Event ID 3 (NetworkConnect), disabled by default in the Sysmon shipped with Windows 11 24H2. The 21 other event types are left onmatch="include" with no rule, which keeps them off โ€” you don't turn on a full journal for one join.

  • English output (--lang en) โ€” done, see above.

  • v2: capture driven from both sides (client AND server) and comparison.

License

GPL-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

netverdict-0.6.0.tar.gz (170.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

netverdict-0.6.0-py3-none-any.whl (117.9 kB view details)

Uploaded Python 3

File details

Details for the file netverdict-0.6.0.tar.gz.

File metadata

  • Download URL: netverdict-0.6.0.tar.gz
  • Upload date:
  • Size: 170.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for netverdict-0.6.0.tar.gz
Algorithm Hash digest
SHA256 f83c991a6c5f6368af21b23ddaf07dd5d1f4e8c88ddd616205b90f5f9d511e63
MD5 df1a1076c52c5f9a9fcc90e74880c796
BLAKE2b-256 a4bd1e49e8412e1e69bcef29c79ba1a6a19c753ae4aa2dcb022cfce6079df250

See more details on using hashes here.

Provenance

The following attestation bundles were made for netverdict-0.6.0.tar.gz:

Publisher: release.yml on serytia/netverdict

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file netverdict-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: netverdict-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 117.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for netverdict-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5bcef09f7a36a5efd1543de3b5c0a7d0d7757bb4279716c7322bd9e2db2497e8
MD5 35951ece030c15090086b1e78702f129
BLAKE2b-256 ee89c31ac6eff3602a7145450259afaccb12384813c7f1d1a64e8e173c2c099e

See more details on using hashes here.

Provenance

The following attestation bundles were made for netverdict-0.6.0-py3-none-any.whl:

Publisher: release.yml on serytia/netverdict

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page