Threat detection¶
Beyond inventory drift, Delta reports observations that commonly accompany a compromise: contact with infrastructure the operator has listed as known-bad, rootkit and persistence indicators, and changed DNS resolvers. Every finding carries a severity and, where it maps cleanly, a MITRE ATT&CK technique id; the findings also feed the aggregate risk score.
What this is and is not. These are comparisons — against a policy, a baseline, or a list of indicators the operator supplies — reported as structured findings. Delta ships no signature database, runs no heuristics or classifier, fetches nothing from the network, and never concludes on its own that something is malicious. It reports what it saw and how that differs from what was declared normal; whether that constitutes an incident is the operator’s call, and any action on the host is a separate, explicitly commanded step (see Shipping reports to a server). A build that cannot act on the host at all is one CMake switch away — see Report-only builds.
All sources are path-based, so these checks apply equally to a live host and to
an offline firmware image under --root.
Threat intelligence (IOC feed)¶
--ioc-feed <file> matches what Delta observes against a local
Indicator-of-Compromise feed — the threat-intelligence counterpart to the CVE
(vulnerability) feed. The feed is a file you write and distribute; Delta
neither ships nor updates one. Matching is literal — exact IP equality, IPv4
CIDR containment, case-insensitive hash equality — so this is inverted policy
(“these must not appear”) rather than malware analysis. It is matched against:
remote connection peers — the socket table (
/proc/net/{tcp,udp}{,6}), so an outbound session to a flagged host is caught (C2 —T1071);ARP neighbors;
file fingerprints — a known-bad file hash on disk (
T1204).
Feed schema:
{
"ips": ["203.0.113.5"],
"cidrs": ["198.51.100.0/24"],
"hashes": ["d41d8cd98f00b204e9800998ecf8427e"]
}
ips are exact matches, cidrs are IPv4 network matches, hashes match
file checksums case-insensitively. No network access is made — the feed is a
local file you distribute. A match produces a violation in the report and
nothing else: the file is not touched, the process is not stopped, the peer is
not blocked.
delta --netif --ioc-feed ioc.json --json
Running executable images¶
The fingerprint scan (--fingerprint) only sees files under the directory it
is pointed at. To catch malware that runs from elsewhere — /tmp, a user’s
home, or entirely from memory — Delta resolves and MD5s each running process’s
executable image (/proc/<pid>/exe) when --processes is collected. This
feeds two detections (Linux live host only):
the image hash is matched against the IOC feed, so a running known-bad binary is caught even if it was never on a scanned path (
T1204). The exe symlink opens the real inode even after unlinking, so a self-deleting dropper is still hashed;a process whose image was unlinked while running (the kernel resolves
exeto"… (deleted)") is flagged outright — a self-deleting dropper or a binary replaced under a live service (T1070.004, high).
In Continuous monitoring the image hash also joins the drift keyset
(procexe:<name>=<hash>), so a binary swapped under a running service name
surfaces as drift with no feed at all.
System / runtime integrity¶
Always evaluated (the heuristics need no policy):
Loaded kernel modules — captured in a baseline; an unexpected module on re-check is flagged (rootkit vector —
T1547.006)./etc/ld.so.preload — a non-empty preload file is a library-injection persistence mechanism (
T1574.006, critical).World-writable executable directories — a writable
/usr/bin&c. lets any user trojan a system binary (T1574).
Kernel memory (SLAB/SLUB)¶
/proc/meminfo and the slab allocator are collected on every run. One check
needs no policy:
Slab caches hidden from /proc/slabinfo — the slab memory
meminfoaccounts for is cross-checked against the sum of the cachesslabinfolists. A gross shortfall means caches are not being reported, which is what a rootkit filtering the file looks like (T1014, high). The full comparison is published as aconsistencyblock, so a smaller divergence shows up indata_consistentwithout failing the run.
The rest is enforced only when the memory norm block is configured (see
Configuration), and splits into exhaustion and exploitation surface:
Available memory / swap headroom below the configured floor — a device that has run out of usable memory has stopped doing its job, and memory exhaustion is a denial-of-service primitive an attacker can drive (
T1499);Slab and unreclaimable-slab share of RAM above the configured ceiling.
SUnreclaimis the telling one: reclaimable slab is cache the kernel gives back under pressure, while unreclaimable slab only grows if kernel objects are not being freed (T1499);A single slab cache past
max_cache_mb— a leak (or a deliberate spray) concentrates in one cache long before it registers as total exhaustion (T1499);/proc/slabinfo readable beyond root — the file is a live map of kernel heap layout and object counts, which is exactly the reconnaissance a slab-spray exploit needs to aim (
T1082);Freelist hardening off — a kernel built without
CONFIG_SLAB_FREELIST_HARDENEDleaves slab freelist pointers unobfuscated, and withoutCONFIG_SLAB_FREELIST_RANDOMthe allocation order is predictable; both make heap corruption far easier to turn into control (T1068).
In Continuous monitoring the volume side also feeds the behavioral model
(mem_available_pct, slab_unreclaim_kb), so a slow leak surfaces as a
trend anomaly rather than waiting to cross a static threshold.
Accounts & SSH persistence¶
From /etc/passwd and each user’s ~/.ssh/authorized_keys:
Extra UID-0 account — any account other than
rootholding UID 0 is a root-equivalent backdoor (T1136.001, critical). A baseline records the observed UID-0 set; the allow-list can also be pinned explicitly withuid0_accounts.authorized_keys — key counts per user are tracked, so an added key (a common SSH persistence trick) shows up as drift in Continuous monitoring.
Persistence surface¶
The classic Unix persistence locations are fingerprinted on every collection — no configuration needed:
cron —
/etc/crontab,/etc/anacrontab,/etc/cron.dand thecron.{hourly,daily,weekly,monthly}directories, plus per-user crontabs under/var/spool/cron(T1053.003);systemd units —
/etc/systemd/systemincluding the*.wantsenablement level (symlinks are followed, so enabling a unit is visible) (T1543.002);init scripts —
/etc/init.d,/etc/rc.local(T1037);shell profiles —
/etc/profile,/etc/profile.d,/etc/bash.bashrc, and the.bashrc/.profile/.bash_profile/.zshrcof every account with an interactive shell (T1546.004);/etc/hosts — silent redirection of any name the device resolves (
T1565.001);trusted CA bundle —
/etc/ssl/certs/ca-certificates.crt; an added root certificate enables TLS interception (T1553.004).
The files join the regular fingerprint set, so a baseline
pins them: a later edit, addition or removal fails the file check and shows as
drift in Continuous monitoring. Two hygiene findings need no baseline at all: a
world-writable persistence file (critical) and a system persistence file
not owned by root (high; live host only — extraction ownership makes it
meaningless under --root).
DNS resolvers¶
The nameserver entries of /etc/resolv.conf are recorded. A baseline
pins them; an unexpected resolver on re-check is flagged as a possible hijack
(T1557). A silently-changed resolver redirects every lookup on the device.
Device identity¶
Delta emits a stable device identity — OS id/version (/etc/os-release),
kernel release and machine-id — as a device block in the JSON report. The
collector uses it to group a fleet; a change in OS or kernel between
observations is itself a meaningful event (an unplanned firmware/OS swap), and
is reported as drift in Continuous monitoring.
Risk score & ATT&CK¶
The JSON report includes an aggregate posture:
"risk": { "score": 80, "grade": "E" }
score (0–100) is a severity-weighted sum of the findings; grade is a
single letter A (clean) … F (worst). Each violation may also carry an
"attack" field with its MITRE ATT&CK technique id, so a SIEM can pivot on
the technique directly.
Syscall behavior¶
Each check above reports what the host contains. Syscall behavior tracing reports what its
software does. Delta samples a syscall profile for each cycle and puts each
observation into a behavior class: module, ptrace, bpf, kexec,
memexec, network and others. --trace-args also decodes the
arguments of the selected syscalls: the file that a process opened, the peer
that it contacted, the page that it made executable.
This covers the case that an inventory cannot cover: a legitimate binary that
keeps its name, its path and its checksum, and changes its behavior. The
syscalls norm block makes this a policy. The findings have the same
severities and the same ATT&CK ids as the other findings on this page.
Configuration keys¶
The relevant norm keys (all optional; a generated baseline fills them in):
"norm": {
"kernel_modules": ["ext4", "i915"], "other_modules_present": false,
"resolvers": ["1.1.1.1"], "other_resolvers_present": false,
"uid0_accounts": ["root"],
"memory": { "min_available_pct": 10, "max_unreclaim_pct": 25,
"require_slabinfo_restricted": true },
"syscalls": { "forbidden": ["init_module", "bpf"],
"deny_classes": ["module", "kexec"],
"forbidden_args": ["path=/etc/shadow", "prot=rwx"] }
}
The full memory key set is in Configuration; a baseline does not
fill it in, since the thresholds are a deployment decision rather than an
observed state.