Table of Contents
- Fingerprinting
- Layer 1 — Passive sniffer (network / TLS layer)
- Layer 2 — Active prober (application layer)
- Layer 3 — Inline protocol inspection (decky services)
- HTTP header fingerprinting (Caddy decnet_fp module)
- JA4H (HTTP request header order)
- Header order and header quirks
- HTTP/2 SETTINGS frame
- HTTP/3 SETTINGS
- User-Agent classification
- IP leak / source IP signals
- Source port as fingerprint signal
- VNC
- SSH / Telnet — session recording and keystroke dynamics
- Layer 4 — SMTP / email identity signals
- Layer 5 — TTP and tool detection
- Layer 6 — Inter-event timing and phase sequence
- Where fingerprints are stored
- Enabling inline HTTP fingerprinting
- Related pages
Fingerprinting
DECNET builds a multi-layer fingerprint of every attacker from four independent sources: passive wire capture, active probing, inline HTTP/protocol inspection, and behavioural profiling of interactive sessions. 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 (type fingerprint) or
ObservationRow entries in the DECNET database and surfaces in the
Attacker detail page.
Layer 1 — Passive sniffer (network / TLS 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.
TLS ClientHello fingerprints
| Fingerprint | Description | Key fields |
|---|---|---|
| JA3 | MD5 of normalised ClientHello fields (cipher suites, extensions, elliptic curves) | ja3, tls_version, sni, raw_ciphers, raw_extensions |
| JA3S | MD5 of the ServerHello response | ja3s |
| JA4 | TLS 1.3-aware successor to JA3 (FoxIO spec) | ja4, alpn, dst_port |
| JA4S | ServerHello counterpart to JA4 | ja4s |
| JA4L | JA4 + latency: client TTL and measured RTT | ja4l, rtt_ms, client_ttl |
| TLS certificate | Server cert metadata — useful when the attacker runs their own TLS service | subject_cn, issuer, self_signed, not_before, not_after, sans, cert_sha256, sni, target_ip, target_port |
| TLS resumption | Session resumption mechanisms advertised (tickets, session IDs) | mechanisms |
Network stack fingerprints
| Fingerprint | Description | Key fields |
|---|---|---|
| TCP SYN OS | Passive OS classifier from SYN options (mini-p0f) | os_guess, mss, window_scale, sack_ok, timestamp, options_order |
| JA4-QUIC | QUIC Initial ClientHello — QUIC-specific extensions and transport params | ja4_quic, sni, alpn, raw_ciphers |
| Flow timing | Inter-packet timing and RTT from the first few packets | stored as tcp_flow_timing event |
The sniffer only sees the TLS handshake — it cannot read HTTP headers or QUIC stream frames inside an encrypted session. Layers 3 and 4 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. Probes are stealthy — no DECNET banner, ordinary client behaviour. See Security-and-Stealth.
| Fingerprint | Protocol | Ports probed | Key fields |
|---|---|---|---|
| JARM | TLS server fingerprint — 10 hand-crafted ClientHellos, 62-char hash of the responses | 443, 8443, 8080, 4443, 50050, 2222, 993, 995, 8888, 9001 | hash, target_ip, target_port |
| HASSH | SSH server fingerprint — MD5 of kex;encryption;mac;compression from the server KEXINIT |
22, 2222, 22222, 2022 | hash, ssh_banner, kex_algorithms, encryption_s2c, mac_s2c, compression_s2c, target_ip, target_port |
| TCP fingerprint | TCP/IP stack OS probe — SYN response TTL, window, options | 22, 80, 443, 8080, 8443, 445, 3389 | hash, raw, ttl, window_size, df_bit, mss, window_scale, options_order |
When any fingerprint changes between probes, an attacker.fingerprint_rotated
bus event fires — a strong signal of infrastructure churn (VPS swap, cert
rotation, banner rewrite).
Layer 3 — Inline protocol inspection (decky services)
HTTP header fingerprinting (Caddy decnet_fp module)
The http and https decky templates ship with a custom Caddy module
that intercepts connections at the byte level, before Caddy's HTTP
parser. This gives wire-accurate fingerprints that cannot be faked by
HTTP-level middleware.
JA4H (HTTP request header order)
The listener wrapper taps the raw TLS stream:
- HTTP/1.1: headers split by
\r\nin arrival order. - HTTP/2: 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, producing a
JA4H hash per the FoxIO spec. Stored with: ja4h, protocol, method,
path, remote_port.
Map-iteration order in Go is randomised; DECNET captures order at the byte level, so the JA4H is reproducible and meaningful.
Header order and header quirks
Beyond the JA4H hash, the raw ordered list of header names is stored
(headers_ordered). This lets you cluster:
- Presence/absence of headers — curl sends no
Accept-Encodingon certain invocations; browsers always send it. - Header ordering — different HTTP clients and frameworks have characteristic orderings even when they send the same headers.
- Header casing — some tools send
content-type(lowercase), others sendContent-Type; stored verbatim before normalisation.
HTTP/2 SETTINGS frame
During the h2 connection preface the client sends a SETTINGS frame.
Stored: settings (map of name → value) and frame_order (IDs in wire
order). Different h2 implementations have characteristic SETTINGS maps
and orderings.
Known settings captured by name:
HEADER_TABLE_SIZE, ENABLE_PUSH, MAX_CONCURRENT_STREAMS,
INITIAL_WINDOW_SIZE, MAX_FRAME_SIZE, MAX_HEADER_LIST_SIZE.
HTTP/3 SETTINGS
For HTTP/3, the module reads client SETTINGS via the http3.Settingser
interface: EnableDatagrams, EnableExtendedConnect, and any additional
settings (including GREASE entries stored as GREASE_<hex>).
User-Agent classification
Every HTTP request captures the User-Agent header and classifies it:
| Signal | Description |
|---|---|
| Tool category | browser, scanner, curl, python-requests, Go net/http, Java, custom, unknown |
| Tool name | specific tool if detectable (e.g. Nikto, sqlmap, Masscan) |
| Signals | flags such as headless_browser, vuln_scanner, exploit_framework |
Stored as bounty type fingerprint, fingerprint_type: "http_useragent".
IP leak / source IP signals
Proxy and forwarding headers are inspected on every HTTP request:
-
ip_leak— the attacker's real public IP appeared inX-Forwarded-For,Forwarded,X-Real-IP,CF-Connecting-IP, orTrue-Client-IP. This happens when an attacker routes through a misconfigured proxy. Fields:claimed_ip,header_name,source_ip. -
spoofed_source— a non-routable IP (RFC1918, loopback, link-local, reserved) appeared in a proxy header — a WAF bypass attempt. Fields:claimed_ip,header_name,category.
Source port as fingerprint signal
remote_addr from Go's network layer is host:port. The collector
strips the port before resolving attacker identity (so 50 connections from
the same IP do not produce 50 attacker rows), but preserves it as
remote_port in the bounty payload. An attacker whose tooling
consistently originates from the same source port is a meaningful signal
(some NAT devices, VPN clients, and C2 frameworks exhibit this behaviour).
VNC
| Signal | Description | Field |
|---|---|---|
| VNC client version | RFB protocol version string from the VNC client's greeting | value |
SSH / Telnet — session recording and keystroke dynamics
The sessrec module records the full PTY byte stream of every interactive
shell session. Two signals are extracted:
Commands executed
Every command entered at the shell prompt is captured with:
command— the raw command stringtimestamp,session_id,attacker_ip,decky,service- Aggregated on session end into a command list on the
session_recordedevent.
Command content reveals intent directly: reconnaissance (id, whoami,
uname -a, cat /etc/passwd), lateral movement (ssh, scp),
persistence (crontab -e, echo >> ~/.bashrc), exfiltration
(curl, wget, base64, scp).
Keystroke dynamics (BEHAVE-SHELL)
The BEHAVE-SHELL engine (decnet/profiler/behave_shell/) extracts 37
attribution primitives across six domains from the PTY stream: motor
(typing cadence, error correction, shell mastery), cognitive (planning depth,
feedback loop engagement, tool vocabulary), temporal (session duration,
escalation pattern, landing and exit rituals), environmental (shell type,
keyboard layout, terminal multiplexer), operational (objective, OPSEC
discipline, multi-actor detection), and emotional valence (stress response,
frustration venting).
Each primitive feeds a per-(identity_uuid, primitive) state machine
(unknown → stable → drifting → conflicted → multi_actor). When two or
more primitives independently reach multi_actor, an
attribution.profile.multi_actor_suspected bus event fires.
See BEHAVE-SHELL for the complete primitive reference, computation logic, calibration data, and thresholds.
Layer 4 — SMTP / email identity signals
Every inbound email to an smtp or smtp_relay decky produces a rich set
of identity signals:
Attacker domains and sender identity
| Signal | Description |
|---|---|
mail_from_domain |
Domain in the SMTP envelope MAIL FROM |
from_domain |
Domain in the From: header (may differ from envelope) |
return_path_domain |
Return-Path: domain |
x_mailer |
X-Mailer header — identifies the mail client or framework |
dkim_signed |
DKIM signature present (bool) |
spf_pass |
SPF check result (bool) |
Victim domain targeting
| Signal | Description |
|---|---|
rcpt_domains |
Set of unique domains in the RCPT TO list |
rcpt_count |
Number of recipients (bulk vs. targeted) |
Payload and attachment fingerprints
| Signal | Description |
|---|---|
body_simhash |
16-hex similarity hash of the email body — clusters phishing campaigns |
body_sha256 |
Exact body hash |
attachment_sha256s |
Per-attachment SHA-256 list |
attachment_extensions |
File extension set |
attachment_macros |
Macro-bearing Office documents detected (bool) |
attachment_password_protected |
Encrypted attachment (evasion signal) |
html_smuggling |
HTML obfuscation / JS blob smuggling detected (bool) |
mal_hash_match |
Any attachment hash matched MalwareBazaar bulk feed (bool) |
urls |
Extracted URLs from body |
Layer 5 — TTP and tool detection
The TTP engine (decnet/ttp/) maps collected events onto MITRE ATT&CK
techniques. Detected techniques are stored as ttp_tag rows and surfaced
in the Attacker detail page.
Detected tools are inferred from:
- Command strings matched against known-tool signatures (nmap, Metasploit, BloodHound, Mimikatz, linpeas, pspy, etc.)
- User-Agent strings for HTTP tools
- SSH banner strings from the HASSH probe
- TLS fingerprints matching known C2 frameworks (Cobalt Strike JARM, etc.)
Layer 6 — Inter-event timing and phase sequence
The correlator and attribution engine track how an attacker behaves across an entire engagement, not just individual connections.
Inter-event timing
Time deltas between successive events of the same type reveal automation vs. human operation:
- Sub-second, uniform intervals → scripted scanner or bot.
- Variable intervals with human-range pauses (2–30 s) → interactive operator.
- Long gaps between sessions with consistent inter-session intervals → scheduled beacon or cron-driven implant.
These are captured as attribution primitives (interarrival_*) via the
BEHAVE-SHELL profiler and as raw timestamps on bounty rows.
Phase sequence
The correlator classifies each event into an engagement phase:
reconnaisance, exploitation, post-exploitation, exfiltration,
persistence, lateral movement. The sequence of phases across a
session is a fingerprint in itself — some toolkits always run
reconnaissance before exploitation; human operators often skip phases or
return to earlier ones.
Phase-sequence analysis drives the phase_sequence attribution primitive
and feeds the campaign clusterer.
Where fingerprints are stored
Bounty fingerprint_type |
Source | Key discriminating fields |
|---|---|---|
ja3 / ja3s / ja4 / ja4s |
Sniffer | hash, tls_version, sni, raw_ciphers |
ja4l |
Sniffer | rtt_ms, client_ttl |
ja4_quic |
Sniffer | ja4_quic, sni, alpn |
tls_certificate |
Sniffer + prober | cert_sha256, subject_cn, sans |
tls_resumption |
Sniffer | mechanisms |
tcp_os |
Sniffer | os_guess, mss, window_scale, options_order |
jarm |
Prober | hash, target_port |
hassh_server |
Prober | hash, ssh_banner, kex_algorithms |
tcpfp |
Prober | hash, ttl, window_size, df_bit |
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 |
http_useragent |
Ingester (HTTP events) | category, tool, signals |
http_header_quirks |
Ingester (HTTP events) | headers_ordered |
vnc_client_version |
Ingester (VNC events) | value |
Bounties are deduplicated per (attacker_uuid, fingerprint_type, hash) so
repeated connections produce one row, not thousands.
Non-fingerprint bounty types: ip_leak, spoofed_source, artifact
(captured files and emails), credential (harvested secrets).
Enabling inline HTTP fingerprinting
The Caddy fp module is built into the http and https decky templates
automatically — no configuration is needed. For HTTP/3, ensure http/3
is listed in the service's http_versions setting.
SSH/Telnet keystroke dynamics require the behave_shell feature to be
enabled on the service (see Service-Personas).
Related pages
- Identity-Resolution — how fingerprints are clustered into attacker identities and campaigns
- OS-Fingerprint-Spoofing — how DECNET spoofs its own OS fingerprint to look like the target OS
- Security-and-Stealth — probe stealth measures
- Logging-and-Syslog — how fp socket records flow through syslog_bridge to the collector
- Service-Personas — configuring BEHAVE-SHELL and session recording per service
DECNET
User docs
- Quick-Start
- Installation
- Requirements-and-Python-Versions
- CLI-Reference
- INI-Config-Format
- Custom-Services
- Services-Catalog
- Service-Personas
- Archetypes
- Distro-Profiles
- OS-Fingerprint-Spoofing
- Networking-MACVLAN-IPVLAN
- Deployment-Modes
- SWARM-Mode
- Tailscale-Global-Deployment
- Resource-Footprint
- MazeNET
- Remote-Updates
- Environment-Variables
- Teardown-and-State
- Database-Drivers
- Systemd-Setup
- Logging-and-Syslog
- Fingerprinting
- Service-Bus
- Realism
- Web-Dashboard
- REST-API-Reference
- Mutation-and-Randomization
- Troubleshooting
Developer docs
DECNET — honeypot deception-network framework. Pre-1.0, active development — use with caution. See Sponsors to support the project. Contact: samuel@securejump.cl