← BARTOSZ_OSIEJ // ROOT

[06] // BLOG // WRITEUP

Catching ransomware with eBPF: what execve/openat tracing taught me about false positives

2026-09-13 · #ebpf #rust #linux #security #ransomware

TL;DR — SYNOPSISBehavioural eBPF tracing (execve/openat + per-CPU perf buffers) catches ransomware by the shape of its damage — the hard part is scoring patterns without crying wolf on every cargo build.
SYNCED_WITH ▸ /content/ebpf-ransomware-monitor-lessons.md — this page renders from that file; the repo is the source of truth. PLAINTEXT ▸ /blog/ebpf-ransomware-monitor-lessons.txt — read it like software: curl -s https://bartoszosiej.github.io/content/ebpf-ransomware-monitor-lessons/blog/ebpf-ransomware-monitor-lessons.txt | less LAST_COMMIT ▸ querying git…

TL;DR

talus-process-monitor is an eBPF-based ransomware detector for Linux: it traces execve/openat from the kernel, streams events over per-CPU perf buffers, and flags behavioural patterns — mass file rewrites plus extension churn — in real time. The hard part is not the tracing; it’s scoring patterns without crying wolf on every cargo build.

Why behaviour, not signatures

Signature detection on Linux ransomware is nearly useless — most samples are short-lived, often scripts wrapping legitimate tools (find, mv, gzip in a loop). What you can catch is the shape of the damage: hundreds of files opened, rewritten, renamed or re-extensioned within seconds across directories the process has no business touching.

So talus watches:

All of it from kernel probes, before the data hits disk crypto.

The plumbing: per-CPU perf buffers or nothing

First mistake I made: a single global ring buffer. Under parallel load (which is exactly when ransomware runs — it wants throughput), events collide and drop. The fix is standard but worth repeating: per-CPU perf buffers with a userspace loader pinning CPUs and reassembling event order per process.

The userspace side is Rust; the eBPF side is libbpf/C with CO-RE. Compile once, run across kernel 6.x — in theory. In practice, verify your field offsets: struct file layout differences bit me twice.

The scoring problem: build systems are innocent ransomware

A naive “N files written in T seconds” rule fires constantly on:

Three heuristics that made the detector usable:

  1. Extension churn, not just writes. Ransomware renames (doc.docxdoc.docx.locked) or re-extensions in bulk. Builders write new files but rarely rename existing ones at volume.
  2. Write-after-read density. Encryptors must read the plaintext before rewriting it. A builder writes fresh output without having read those exact files.
  3. Directory breadth vs. depth. target/ is one deep subtree; ransomware sweeps breadth-first across $HOME.

None of these is a silver bullet; the score is a weighted blend, and the weights are the actual product.

What I’d do differently

Repo

Code, architecture notes and verification checklist: talus-process-monitor (Rust + libbpf/C, Linux 6.x).