WhisperDocs
Endpoint

Detection engines

Telemetry is what the sensor sees. The engines on this page are what it decides. Twelve behavioral engines and two real, industry-standard rule engines, a SigmaHQ-format evaluator and VirusTotal's YARA-X, run on the host, observe-only, and turn a raw event stream into named technique detections and per-process verdicts.

The collectors (see Telemetry collectors) give us visibility. On top of that stream sits the analytic layer: hash reputation, a per-process trust tier, the Sigma evaluator, the YARA-X scanner, a process-injection scanner, an event-chain correlator, a critical-config watch, a ransomware behavior detector, and the internet-scale graph reputation that scores every destination and hash the fleet touches. Together they name 91 on-host ATT&CK techniques through 12 behavioral engines, 79 bundled Sigma rules and a curated critical-config watch. Turn the sensor on and every one of them ships on: the engines below are opt-out, not opt-in, and the next section shows the exact key for each. YARA-X sits on a different axis entirely, a build-time switch rather than a runtime one, so the default release build still ships zero YARA rules regardless of any key. None of this is tuned by vibes: the package carries 2,112 test functions and 93 fuzz targets, the pure-logic deciders are mutation-tested, and the ransomware engine is false-positive-safe by construction. The full per-technique ledger, with the four graph-side infrastructure columns beside it, is on ATT&CK coverage.

Telemetry is not a verdict

This distinction is load-bearing, so we state it before anything else. Telemetry is visibility: the exec, file, socket and DNS events the collectors capture. We measured that at 100% across a driven Atomic Red Team run (37 of 37 techniques). A verdict is an engine deciding that a specific pattern is worth an alert. That analytic layer is narrower than the telemetry: on the same run, the signature lane fired on 32 of 37 techniques (86%), and we publish both numbers rather than let the 100% stand in for detection. We never claim an alert we cannot demonstrate.

There is a second line that matters just as much. Every engine on this page is observe-only: it emits a detection event onto the telemetry stream, and it never blocks, kills or quarantines on its own. A verdict, by default, is held: surfaced, ranked and recorded, but not acted on. On-host blocking is opt-in, and the adversary-proof lever, the off-host kill-switch, lives on the network, not in the sensor. So on this page you will read detected, held and observe-first, never prevented or contained. The response levers are documented in Containment.

Opt-out, not opt-in

The default posture flipped: every engine below ships on the moment the sensor itself is on. Turning the sensor on is the one opt-in; each engine underneath it is its own key, and the key is read the way everything here is, liberally: absent or true means on, and only an explicit false turns that one engine off. Drop this into the sensor's config (/etc/whisper/sensor.json for a system service, .whisper/config for a project) and reinstall or restart:

# /etc/whisper/sensor.json - every key below is ON unless you say otherwise.
{
  "sensor": {
    "enabled": true,
    "sigma": false,            // the Sigma rule evaluator
    "injection": false,        // the memory-map sweep (Linux, Windows, macOS ES)
    "eventchain": false,       // the write -> exec -> connect correlator
    "lateralmovement": false,  // T1021 remote-service correlator
    "defenseevasion": false,   // T1070 / T1562 correlator
    "collection": false,       // T1056 / T1113 / T1115 correlator
    "credaccess": false,       // T1003.001 / T1552.001 / T1555.003 correlator
    "dga": false,              // DNS qname burst classifier
    "jatap": false,            // the JA3/JA4 capture tap
    "javerdict": false,        // the JA3/JA4 graph verdict
    "yara": false              // YARA-X, where a -tags yara build carries it
  }
}

An existing config is honored verbatim, so none of this is retroactive: add only the lines for what you want off, and everything else keeps running.

Two engines sit outside this switch entirely, for opposite reasons. The driver blocklist (T1068) is wired unconditionally onto the image-load feed, so there is no key to flip. YARA-X is gated a level up, at build time: the default release binary never compiles the engine in, so the yara key above only means anything once you build with -tags yara yourself, at which point it, too, defaults on. Both are covered in the engine table below.

The engines

Every engine below ships and runs on the shipped Linux sensor today, with the build-tag and platform caveats noted in the status column and the exact opt-out key from the block above.

EngineWhat it decidesWhere it runsStatus
Hash verdictsEvery observed binary gets a hash verdict: known-bad from the threat graph (MalwareBazaar / ThreatFox) becomes a confirmed-bad signal, and known-good OS binaries are affirmatively cleared so the analyst sees the calm majority.On-host clamp (a bundled allowlist) plus the graphon
Process trust tier (T0-T4)Grades every process from T4 (verified-signed or known-good) down to T0 (known-bad), composing hash reputation with the on-host and cross-fleet signals. Only a confirmed graph verdict reaches T0, never a fail-open guess.On-host, folded with graph reputationon
Sigma evaluatorMatches all four event vocabularies against Sigma rules: 79 bundled rules (34 SigmaHQ under DRL 1.1, 45 first-party under MIT), operator rules alongside, capped at 512, no cloud round-trip.On-host, inline (pure in-memory matching)on · sigma:false
YARA-X scannerScans settled unknown executables and injection-flagged memory regions against YARA rules: four rule files in the tree, zero in the default build, operator rules alongside.On-host, build-gated (-tags yara), calm-scheduledon in a -tags yara build · yara:false; absent from the default build entirely
Process-injection scanner (T1055 / T1620)Classifies four injected-memory shapes from a process's own memory map, no disassembler, JIT-screened.On-host: /proc/<pid>/maps sweep (Linux), the same sweep (Windows), a real-time correlator off the macOS Endpoint Security feedon · injection:false; the macOS arm is also feed-gated on Apple's ES entitlement
Event-chain correlator (T1105)Stitches a write, then an exec, then an outbound connect into one narrative, the classic dropper story, instead of three isolated events.On-host, crossed with the graphon · eventchain:false
Critical-config watchFlags writes to 16 hard-coded persistence and priv-esc paths (linker preload, cron, systemd, sudoers, sshd), each pre-tagged with its technique.On-hoston · configWatch:false
Ransomware behavior (T1486 / T1490)An 8-heuristic sliding-window correlator: canaries, gated entropy, mass write / rename / delete, tree rollup, low-and-slow, recovery destruction. A reversible freeze responder is built alongside it.On-hostdetection on · ransomware:false; the freeze responder stays a separate observe-first opt-in
Driver blocklist (T1068)Matches the vulnerable-driver blocklist on the live image-load feed.On-host (Windows)on, unconditionally; no config key exists to turn it off
Lateral-movement correlator (T1021.001-006)Remote-service session shapes folded over the socket and exec streams.On-hoston · lateralmovement:false
Defense-evasion correlator (T1070 / T1562)Two or more distinct evasion classes inside one process tree within a window.On-hoston · defenseevasion:false
Collection correlator (T1056 / T1113 / T1115)Screen-capture and clipboard shapes correlated with the file they staged.On-hoston · collection:false
Credential-access correlator (T1003.001 / T1552.001 / T1555.003)LSASS dump shape and browser credential-store reads, file-corroborated.On-host (Windows)on · credaccess:false
DGA classifier (T1568.002)Burst classification over the DNS qname stream.On-hoston · dga:false
JA3/JA4 verdict (T1573)The TLS ClientHello fingerprint judged against the graph.On-host (Linux)on · javerdict:false; the capture tap is its own switch, jatap:false
Graph reputationScores every destination, hostname, IP, ASN, certificate and file hash the fleet touches against the Whisper graph, off the host, at resolve time.Off-host (the graph)live
i

On the trust tier and egress. The T0-T4 tier is computed live for every process, and mapping each tier to an egress freedom (none to unrestricted) is live-enforced in the egress plane: the endpoint's worst observed process tier drives the decision. The wider auto-contain ladder beyond that mapping stays gated and observe-first, and the policy read-back labels every namespace as enforcement=live or enforcement=stored.

Two real rule engines, running natively

Most lightweight agents bundle neither YARA nor Sigma; heavyweight ones bolt on a separate YARA integration and convert Sigma somewhere in a SIEM. Whisper runs both, in-process, on the host: the Sigma evaluator matches single events across all four stream vocabularies (exec as process_creation, file as file_event, conn as network_connection, dns as dns_query), and the YARA-X engine scans files and memory. Both carry the rule's own name, author, license and ATT&CK technique onto every finding: the technique is read from the rule's own tag, never invented, and a rule without one is reported as unmapped.

YARA-X · byte signatures
APT_MAL_LNX_Kobalos
Author ESET research · BSD-2 license, attribution preserved · targets the Kobalos Linux backdoor and its SSH credential stealer
→ finding: rule + author + technique T1204.002 on the wire
Sigma · event patterns
Netcat Reverse Shell Execution
SigmaHQ community rule, DRL 1.1, attribution preserved · matches the exec stream · level high
→ finding: rule + author + technique T1059 on the wire

The bundled Sigma set is 79 rules, attribution preserved on every one: 34 upstream SigmaHQ rules bundled verbatim under the Detection Rule License 1.1, each keeping its title, id, author, date, references and tags, and 45 first-party Whisper rules under the MIT License, written where SigmaHQ carries nothing for a lane we ship. One bundle carries every platform's layer side by side and load-time admission selects the subset the running binary can observe, so the platform columns overlap. No GPL rule packs, ever.

The 79, bySplit
Stream61 process creation · 10 file event · 7 network connection · 1 DNS query
Platform admission21 Windows · 17 Linux · 59 macOS
Severity22 high · 38 medium · 18 low · 1 informational

Named examples, so the shape is concrete: Linux Shell Pipe to Shell (T1140, medium), Process Execution From Shared Memory Directory (T1027.011, high), Netcat Reverse Shell Execution (T1059, high), Communication To Ngrok Tunneling Service (T1572, high), Linux Crypto Mining Pool Connections (T1496, high), New Cron File Created (T1053.003, low) and DNS Query to External Service Interaction Domains (T1190 / T1595.002, high).

The YARA rules in the tree (4 files, 5 rules, every one under a permissive license with the author's attribution kept; the default release build ships none of them):

RuleAuthorLicenseTargets
MAL_LNX_PLAGUE_BACKDOOR_Jul25Pezier Pierre-HenriDRL 1.1the Plague Linux backdoor
SUSP_LNX_Linux_Malware_Indicators_Aug20_1Florian Roth / NextronDRL 1.1generic Linux malware indicators
APT_MAL_LNX_KobalosESET researchBSD-2the Kobalos backdoor
APT_MAL_LNX_Kobalos_SSH_Credential_StealerESET researchBSD-2its SSH credential stealer (T1204.002)
Whisper_EICAR_Test_FileWhisper SecurityMITthe EICAR self-test, so you can prove the engine end to end

Bring your own rules. Both engines are two-layer: the embedded default set plus an operator override directory (drop .yml Sigma rules or .yar files into the sensor's .whisper/ rule directories; an operator rule with the same id replaces the bundled one). Sigma is capped at 512 rules and matching stays pure in-memory string work, so it runs inline with no scheduler involvement. YARA full-file scans are the expensive half, so they are calm-scheduled: each scan is a deferrable task with a 60-second starvation floor (a fresh unknown executable must not wait long, even on a busy host), file reads are capped at 32 MiB, and binaries already cleared as known-good OS packages skip the deep scan entirely. The boundaries: the default public build ships the YARA engine behind the -tags yara build gate, so nothing above about the yara key applies until you build that way yourself; once you do, YARA-X defaults on like every other engine on this page, and {"sensor":{"yara":false}} opts back out. Sigma aggregation (multi-event) rules are skipped at load rather than half-evaluated.

Ransomware: eight heuristics, false-positive-safe by construction

The complaint every operator has about behavioral detection is the false alarm. The ransomware engine (the most-tested engine in the sensor, 156 test functions) is an 8-heuristic per-process sliding-window correlator over the file and exec streams, and it is built so that no single ambiguous signal convicts. It fires only when several heuristics corroborate and a destructive action is confirmed:

HeuristicWhat it takesNote
Mass writea high volume of distinct paths written by one process inside a short sliding windowvolume, not content
Rename churna sustained run of moves converging on one uniform new extensionbenign suffixes (.tmp, .partial, package-manager renames) screened out
Canary tripwiresany touch of a planted decoy filenear-proof; the canary names are derived per host with an HMAC, so an author cannot ship a grep list
Mass delete + backup tampera burst of deletes alongside execs of shred / snapshot / backup toolingthe destruction pattern, not one delete
Gated entropyrepeated high-entropy reads AND a destructive corroboratorthe engine's only disk I/O, opened only once mass-write is already elevated; plain content exonerates
Tree rollupper-file child processes folded into their tree ownerspawning one child per file no longer evades the per-process bars
Low-and-slowa decaying per-tree accumulatorcatches a paced encryptor that stays under every per-window bar; fires only next to live confirmed encryption
Recovery destruction (T1490)multiple distinct argv-classified destroy operations (snapshot delete, backup forget)fires "Inhibit System Recovery" on its own

The cost is bounded like everything else: entropy sampling reads only a small prefix of a few files, tightly capped host-wide, through a symlink-refusing open. And the exoneration path is the point: a compiler, an rsync or a package upgrade produces a mass-write burst of plain content, which actively pulls the score down instead of merely not raising it. We proved the posture with a live-fire run that drove four workloads past the detector at once:

WorkloadSignalsFused verdictOutcome
Paced mass-encrypt bursthigh_entropy, mass_write, mass_deletecorroboratedDetected (T1486)
Canary-file renamecanarynear-proofDetected
Encrypted-vault createno corroborating signalstays lowSilent
Compile and delete buildno corroborating signalexoneratedSilent

Zero false positives: the legitimate encryptor and the noisy compiler stayed quiet while the paced encryptor and the canary rename fired. The five pure-logic deciders behind this are mutation-tested at 84.21%, so the behavior is proven, not tuned by feel. The reversible freeze that pairs with the detection (a SIGSTOP on the offending process tree, undone by SIGCONT) is built and runs observe-first, off by default and behind an opt-in; copy-on-write file rollback is designed and on the roadmap, not in the tree yet.

Process injection: the memory map is the tell

The injection scanner (124 test functions) classifies four in-memory shapes from /proc/<pid>/maps, deliberately with no disassembler: the memory map itself is the tell, and it keeps the engine small and dependency-clean. The four shapes: a writable-and-executable anonymous region (rwx_anon, shellcode staging), a private executable region with no backing file (missing_backing, the mprotect-flip and memfd injectors), an executable region whose backing file has been deleted (deleted_backing, the unlink-loader dropper), and a process whose main executable is a memfd or matches no executable mapping (hollow, process hollowing and reflective loading, T1055.012 / T1620). Each shape is strong enough to fire on its own, and only after the structural false-positive screens below have cleared it.

False-positive screens are structural, not statistical: a curated screen for the 28 runtimes that map RWX by design (JVM, V8, .NET, LuaJIT and kin), a replaced-on-disk screen so a package upgrade does not read as a deleted backing file, and an exemption for kernel pseudo-regions. The sweep itself is calm-scheduled: it runs as a deferrable task on an idle-priority worker, default cadence 5 minutes, with a 30-minute starvation floor so a busy week never becomes a blind month. Confirmed-anomalous regions are handed to YARA-X for a byte-level second opinion.

The event chain: write, exec, connect

The classic dropper story is three events that mean little alone and everything together. The event-chain correlator (47 test functions) side-consumes copies of the stream, owns no spot in it, and stitches the ordered sequence write(F), then exec(F), then an outbound connect across process trees: the write and the exec are linked by file identity, the exec and the connect by process identity walked up a bounded number of spawn hops, with PID reuse fenced by process start-ticks.

The dropper chain: a file is written, executed soon after within a bounded window, and dials out shortly after; confidence builds from low to high and never reaches certainty. write(F) a payload lands on disk exec(F) the same file runs connect(out) …and dials out bounded window bounded window confidence: low high certainty · never claimed tighter timing raises confidence; a relative-path link degrades it the last step to certainty belongs to the graph: who owns the destination?
The chain fires below certainty, deliberately never claiming it: the write leg could be a local build. Certainty is the graph second pass's to add, from the destination's owner, ASN and prevalence.

The two legs are each bounded by a short correlation window, with bonuses for tight timing; an absolute-path link scores higher than a basename fallback, and the whole engine does zero I/O of its own: every fact arrives in the events. It maps to T1105, Ingress Tool Transfer.

The graph second pass

Every engine above deliberately tops out below certainty, because the sensor's picture ends at the host's edge. The second pass runs where the picture is widest: the destination a chain dialed, the domain a process resolved, is scored on the Whisper graph (roughly 7.53B nodes / 40.3B relationships) for ownership, ASN, prevalence and threat band. In the exfiltration fusion, a connect is joined to the nearest preceding resolve across different processes (by name, or by the shared source-port flow key even when a shared system resolver did the lookup) and the fused chain is scored 0 to 200: temporal proximity plus graph-verdict severity, so a 3-second chain to a rare, newly-registered destination outranks a 3-second chain to a benign popular host. No graph signal means a pure temporal score, never a claim of malice, and every graph call is fail-open: the local verdicts always land, the overlay enriches when it can. The full three-graph story is in How the sensor works.

Live: per-process verdicts

The verdicts are not a slide. Here is the real per-process census for one fleet host, read over the control plane. The API key is redacted and it never leaves the server: you send a name, the graph does the scoring, and the answer comes back with the reputation attached.

# host web-edge-01  ·  /128 2a04:2a01:8dae:4521:45b8:b241:4f91:56e2
$ curl -s https://graph.whisper.online/api/query \
    -H 'content-type: application/json' \
    -H 'X-API-Key: whisper_live_...redacted' \
    --data '{"query":"CALL whisper.agents({op:\'list\', kind:\'proc\',
             address:\'2a04:2a01:8dae:4521:45b8:b241:4f91:56e2\', limit:500})"}'

envelope: op=list  ok=True  status=200  elapsed_ms=66
processes returned                        : 500
hash-verdict known-good (fleet allowlist) : 52
known-good on THIS host                   : 54
carrying a signal                         : 62

--- known-good rows (hash-verdict:known-good rides the signal list) ---
proc=1695119:87841819  trust=T3/unknown  knownGood=True  exe=/usr/bin/wg    signals=[hash-verdict:known-good,hash-verdict:known-good-onhost]
proc=1695161:87842331  trust=T3/unknown  knownGood=True  exe=/usr/bin/ip    signals=[hash-verdict:known-good,hash-verdict:known-good-onhost]
proc=1695016:87838197  trust=T2/unknown  knownGood=True  exe=/usr/bin/bash  signals=[fan-out:8,sensor-rep:32,hash-verdict:known-good,hash-verdict:known-good-onhost]

--- signal-carrying rows ---
proc=705:1107          score=48  trust=T2/unknown  signals=[fan-out:279,sensor-rep:48]
proc=1682122:0         score=48  trust=T2/unknown  signals=[fan-out:26,sensor-rep:48]
proc=1668549:0         score=48  trust=T2/unknown  signals=[fan-out:6,sensor-rep:48]

Known-good is not a hardcoded list. The on-host clamp is seeded from 3075 distro-verified OS-binary hashes, and the graph adds internet-scale hash reputation the fleet's own binaries feed back into. The point is the affirmative signal: the analyst sees 54 processes cleared as known-good on this host, not just a wall of red.

The Hunt surface of the operator console running op:list with kind agents: 320 rows in 324 milliseconds, a table of agent labels, forward names and /128 addresses, with a left panel of saved queries and a Run query button. The API key is not present in the page because the query runs server-side.
Hunt, live: the same surface, here listing the fleet-identity roster (op:list, kind:agents, 320 rows). Switch the kind to proc and it returns the per-process census shown above. The query runs on the control plane, so the key stays server-side and never reaches the browser.

Held, not contained

When an engine fires, the detection lands in the incidents queue and in the exposure-weighted task backlog, ranked worst-exposed-first: a running, network-reachable finding outranks a dormant one. Its recorded state is held. That word is deliberate. While enforcement is observe-only, we do not write "contained" or "blocked" against a detection we have not actually actuated.

The Incidents surface of the operator console listing detections with technique, confidence and signals, and a status column that reads held rather than contained or blocked.
Incidents, live: detections ranked worst-first, each with its technique, confidence and signals. The status reads held, not contained: the true state while enforcement is observe-only.

Two response levers turn a held detection into an action, and both are documented in Containment: an opt-in on-host respond tier (quarantine a file, restart a service, the reversible freeze) and the off-host kill-switch that revokes an identity's resolution and egress network-wide and survives a fully compromised host.

On the roadmap

i

Future, and labelled as such: ransomware copy-on-write rollback (designed, enforcement-gated), multi-event Sigma aggregation rules (single-event rules run today; aggregations are skipped at load, not half-evaluated), the Windows ETW-TI tier (Windows deep detection itself is live, proven on Windows Server 2022; the driver blocklist above is already wired and running; ETW-TI itself is built but dark, gated on Microsoft's anti-malware attestation), deeper in-memory evasion analytics via the macOS Endpoint Security tier (entitlement-gated), and a public MITRE ATT&CK Evaluation in 2027. The detectors above are live today; these extend them.