docs: add Fingerprinting page covering sniffer, prober, and Caddy fp module
165
Fingerprinting.md
Normal file
165
Fingerprinting.md
Normal file
@@ -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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user