From d45fb08b6d587bd285c9bb5ffa4c8607cde86209 Mon Sep 17 00:00:00 2001 From: anti Date: Sun, 10 May 2026 04:10:47 -0400 Subject: [PATCH] docs: add Fingerprinting page covering sniffer, prober, and Caddy fp module --- Fingerprinting.md | 165 ++++++++++++++++++++++++++++++++++++++++++++++ _Sidebar.md | 1 + 2 files changed, 166 insertions(+) create mode 100644 Fingerprinting.md diff --git a/Fingerprinting.md b/Fingerprinting.md new file mode 100644 index 0000000..08247e6 --- /dev/null +++ b/Fingerprinting.md @@ -0,0 +1,165 @@ +# Fingerprinting + +DECNET builds a multi-layer fingerprint of every attacker from three +independent sources: **passive wire capture**, **active probing**, and +**inline HTTP inspection**. Each layer contributes distinct evidence; +together they let you tell a curl script from a Metasploit operator from a +nation-state implant even when the source IP changes. + +All fingerprint data is stored as `bounty` rows in the DECNET database and +surfaces in the **Attacker detail** page under the *Fingerprints* tab. + +--- + +## Layer 1 — Passive sniffer (network layer) + +The sniffer runs fleet-wide on the host interface and reads raw packets +without touching any decky service. It fires on the first packet of each +connection, so it captures the attacker's stack signature before any +application-level exchange. + +| Fingerprint | What it captures | Algorithm | +|---|---|---| +| **JA3 / JA3S** | TLS ClientHello / ServerHello cipher suite and extension order | MD5 of normalised fields per Salesforce spec | +| **JA4 / JA4S / JA4L** | TLS 1.3-aware version; JA4L adds latency timing | FoxIO JA4 spec | +| **TCP SYN OS** | MSS, window scale, TCP option order from the SYN | Mini-p0f classifier (`decnet/sniffer/p0f.py`) | +| **JA4-QUIC** | QUIC Initial ClientHello — QUIC-specific extensions and transport params | FoxIO JA4-QUIC spec | +| **Flow timing** | Round-trip latency and inter-packet timing | Raw timestamps from the sniffer | + +Sniffer events land as `attacker.observed` or `attacker.fingerprinted` bus +events consumed by the correlator and ingester. + +> **Limitation:** the sniffer only sees the TLS handshake — it cannot read +> HTTP headers or QUIC stream frames inside an encrypted session. Layers 2 +> and 3 fill that gap. + +--- + +## Layer 2 — Active prober (application layer) + +After a new attacker is first observed, the prober worker reaches back +out to the attacker's IP on a set of default ports to collect +application-level fingerprints. + +| Fingerprint | Protocol | Ports probed | +|---|---|---| +| **JARM** | TLS (any HTTPS-ish service) | 443, 8443, 8080, 4443, 50050, 2222, 993, 995, 8888, 9001 | +| **HASSH** | SSH server | 22, 2222, 22222, 2022 | +| **TCP fingerprint** | TCP SYN response analysis | 22, 80, 443, 8080, 8443, 445, 3389 | + +Active probes are stealthy: they look like ordinary clients, carry no +DECNET-specific banner, and use the same port-rotation patterns an +informed scanner would use. See [Security-and-Stealth](Security-and-Stealth). + +When a fingerprint changes between probes, a `attacker.fingerprint_rotated` +bus event fires — that is a strong signal of infrastructure churn (VPS +swap, cert rotation, banner rewrite). + +--- + +## Layer 3 — Inline HTTP fingerprinting (Caddy fp module) + +The `http` and `https` decky templates ship with a custom Caddy module +(`decnet_fp`) that intercepts connections at the byte level, before +Caddy's HTTP parser sees them. This gives wire-accurate fingerprints +that cannot be faked by HTTP-level header manipulation. + +### JA4H (HTTP request header order) + +The `decnet_fp` listener wrapper taps the raw TLS stream and buffers the +first request headers of each connection before replaying them to Caddy's +parser. + +- **h1:** headers are split by `\r\n` in arrival order. +- **h2:** a per-connection HPACK decoder maintains the dynamic table and + emits headers in HPACK decode order — pseudo-headers + (`:method`, `:path`, `:scheme`, `:authority`) appear first, then regular + headers in the order the client encoded them. + +The ordered list feeds `_compute_ja4h` in `syslog_bridge.py`, which +produces a JA4H hash per the FoxIO spec. + +> Map-iteration order in Go is randomised; DECNET captures order at the +> *byte level*, not from `http.Header`, so the JA4H is reproducible and +> meaningful. + +### H2 SETTINGS + +During the h2 connection preface, the client sends a `SETTINGS` frame +listing its implementation parameters. The fp module parses the raw +6-byte `(id, value)` tuples in wire order and records: + +- `settings` — map of setting name → value + (e.g. `HEADER_TABLE_SIZE`, `MAX_CONCURRENT_STREAMS`, `INITIAL_WINDOW_SIZE`) +- `frame_order` — setting IDs in the exact order the client sent them + +Different HTTP/2 implementations (curl, Chrome, Firefox, Go net/http, +Java HttpClient) have characteristic SETTINGS maps and orderings. + +### H3 SETTINGS + +For HTTP/3, the QUIC server is Caddy with native h3 support. Caddy +exposes the client's h3 SETTINGS frame via the `http3.Settingser` +interface on the `ResponseWriter`. The fp module captures: + +- `EnableDatagrams` — whether the client advertised H3 datagram support +- `EnableExtendedConnect` — extended CONNECT (used by WebTransport) +- `Other` — any additional settings (including GREASE entries) + +### Source port as fingerprint signal + +`remote_addr` in every fp record is the full `host:port` string from +Go's network layer. The collector strips the port before resolving +attacker identity (so 50 connections from the same IP do not produce 50 +attackers), but preserves it as `remote_port` in the structured fields. + +An attacker whose tooling consistently originates from the same source +port (or a narrow range) is a meaningful signal — some NAT devices, VPN +clients, and C2 frameworks exhibit this behaviour. `remote_port` is +stored in the `fingerprint` bounty payload and visible in the Attacker +detail page. + +--- + +## Where fingerprints are stored + +Every fingerprint event produces a `bounty` row: + +| Bounty `fingerprint_type` | Source | Key discriminating fields | +|---|---|---| +| `ja3` / `ja4` / `ja4s` | Sniffer | `hash`, `tls_version`, `ciphers` | +| `ja4_quic` | Sniffer | `ja4_quic`, `sni`, `alpn` | +| `tcp_os` | Sniffer | `os_guess`, `mss`, `window_scale` | +| `jarm` | Prober | `jarm_hash`, `port` | +| `hassh` | Prober | `hassh_server`, `port` | +| `tcpfp` | Prober | `tcp_fp_hash`, `port` | +| `ja4h` | Caddy fp module | `ja4h`, `protocol`, `method`, `remote_port` | +| `http2_settings` | Caddy fp module | `settings`, `frame_order`, `remote_port` | +| `http3_settings` | Caddy fp module | `settings`, `remote_port` | + +Bounties are deduplicated per `(attacker_uuid, fingerprint_type, hash)` so +repeated connections from the same attacker produce one row, not thousands. + +--- + +## Enabling inline HTTP fingerprinting + +The Caddy fp module is **built into the `http` and `https` decky templates +automatically** — no extra configuration is needed. The module activates +when the template is deployed. + +For HTTP/3, ensure `http/3` is listed in the service's `http_versions` +setting. Caddy's native h3 stack handles UDP/443; the fp module hooks into +it via the `http3.Settingser` interface. + +--- + +## Related pages + +- [Identity-Resolution](Identity-Resolution) — how fingerprints are + clustered into attacker identities +- [OS-Fingerprint-Spoofing](OS-Fingerprint-Spoofing) — how DECNET spoofs + *its own* OS fingerprint to look like the target OS +- [Security-and-Stealth](Security-and-Stealth) — probe stealth measures +- [Logging-and-Syslog](Logging-and-Syslog) — how fp socket records flow + through syslog_bridge to the collector diff --git a/_Sidebar.md b/_Sidebar.md index db43cce..1a3e40d 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -27,6 +27,7 @@ - [Database-Drivers](Database-Drivers) - [Systemd-Setup](Systemd-Setup) - [Logging-and-Syslog](Logging-and-Syslog) +- [Fingerprinting](Fingerprinting) - [Service-Bus](Service-Bus) - [Realism](Realism) - [Web-Dashboard](Web-Dashboard)