← 02 — Articles

auditd for people who mostly run Windows

Endpoint6 min read

On a network that runs mostly Windows, the Linux servers tend to be the quietest machines in the SIEM. The authentication log arrives — who logged on over SSH, who ran sudo — and very little else. The equivalent of process creation, account changes and persistence events exists on Linux too, in the kernel’s audit subsystem. It is just not configured by default.

This article is a short auditd baseline for readers who know the Windows side better. It maps what you already collect on Windows to Linux, explains each rule in linux-auditd-baseline.rules, and shows how to check what a server records. It follows the audit policy and Sysmon articles in this series.

What you need before you start

  • The audit package: auditd on Debian and Ubuntu, audit on Red Hat family systems. It is installed by default on many server distributions; auditctl -s tells you whether it runs.
  • Root on a test server that runs your typical workload.
  • A collector for /var/log/audit/audit.log. Most SIEM agents have an audit log input. Check whether yours reads the file or talks to the kernel directly: two consumers of the kernel audit stream can conflict, and the documentation of your agent says which mode it uses.

Windows terms, Linux terms

On Windows you collect On Linux the closest source is
4624/4625 logons /var/log/auth.log or /var/log/secure, and auditd USER_LOGIN / USER_AUTH records
4688 process creation auditd rules on the execve system call
4720, 4738, 4728 account and group changes auditd watches on /etc/passwd, /etc/shadow, /etc/group
4698 scheduled task created watches on cron files and systemd unit directories
4697 service installed watches on /etc/systemd/system/
Sysmon event 6, driver loaded auditd rules on init_module / finit_module

The rest of the article explains the rules that produce these.

How auditd rules work, briefly

Rules live in files under /etc/audit/rules.d/. augenrules compiles them into /etc/audit/audit.rules, and augenrules --load loads the result into the kernel. There are two kinds of rule that matter here:

  • File watches (-w path -p wa -k key): record writes (w) and attribute changes (a) to a file, or anything under a directory.
  • System call rules (-a always,exit -F arch=b64 -S syscall -F filter -k key): record a system call when the filters match.

Every rule has a key (-k). Keys are how you search later — ausearch -k identity — and how your SIEM rules will filter, so name them for what they mean, not for the file they watch. The full syntax is in audit.rules(7).

The baseline, rule by rule

Identity and privileges

Watches on /etc/passwd, /etc/shadow, /etc/group, /etc/gshadow, /etc/sudoers and /etc/sudoers.d/, keys identity and sudoers. Any account, password, group or sudo rights change writes one of these files, whether it was done with useradd, usermod, visudo or an editor.

Remote access and persistence

Watches on the SSH server configuration, on /etc/crontab, /etc/cron.d/ and /var/spool/cron/, on /etc/systemd/system/ (where new or overridden services land), and on /etc/ld.so.preload, which makes every program load a library — rarely used legitimately, and a known persistence method. Keys sshd, cron, systemd and preload.

The audit configuration itself

A watch on /etc/audit/, key auditconfig. Changing the rules to stop recording is the obvious first move for someone who notices them.

Kernel modules

init_module, finit_module and delete_module system calls, key modules. Loading a kernel module is rare on a stable server, and a malicious module is one of the hardest things to find afterwards.

Commands run by people

execve for processes with an audit user ID (auid) of 1000 or more, excluding the unset value 4294967295, key exec_user. The audit user ID is set at login and survives sudo and su, so a command run as root through sudo is still attributed to the person who logged in. This is the closest equivalent to 4688 for interactive work, and its volume grows with how much people work on the server — usually modest.

Commands run by a service account

The rule above has a large blind spot: a web shell or an exploited application runs as the service account, outside any login session, with no audit user ID. The baseline adds execve for one effective user ID — 33, which is www-data on Debian and Ubuntu — key exec_service. ADAPT it to the account your internet-facing service runs as (id -u <user>). A web server account that starts sh, curl or python is one of the clearest signals you can collect on Linux.

What is deliberately not in the baseline

  • execve for every process. Automation, monitoring and package managers start thousands of processes per hour. If you need it, add it per server role after measuring.
  • File reads. Watching reads of sensitive files (-p r) is possible and very noisy on files that services read constantly, such as /etc/passwd.
  • Network system calls. connect rules exist; a firewall or network sensor is usually a better source.
  • The immutable flag (-e 2). It makes the rules unchangeable until reboot — good against tampering, painful while you are still tuning. Enable it last.

Checking what a server records

auditctl -s                      # enabled, failure mode, backlog, lost events
auditctl -l                      # rules loaded in the kernel right now
augenrules --check               # do rules.d and audit.rules differ?
ausearch -k identity -i --start today
aureport -k --summary            # events per key: where the volume is

auditctl -s shows a lost counter. If it grows, the kernel buffer (-b) is too small for the load, and events are being dropped. aureport -k --summary after a working day is the quickest way to see which rule produces most of the volume.

Testing that events arrive

  1. Identity: sudo useradd ew-auditd-test, then sudo userdel ew-auditd-test. Expect events with key identity.
  2. Commands: log in over SSH as a regular user and run sudo id. Expect exec_user events for sudo and id, with your user as auid.
  3. Service account: sudo -u www-data /bin/true (or the account you configured). Expect an exec_service event. Note that this one also matches exec_user, because you started it from your login session; a real web shell would only match exec_service.
  4. Find them in the SIEM by key, and check that the key field is parsed. A SIEM rule that filters on the key is far cheaper than one that searches raw audit text.

Limitations

  • auditd records system calls and file changes. It does not inspect what a script does internally; a Python script that edits /etc/shadow shows as a write by python3, which is the correct and sometimes confusing answer.
  • A root user can stop auditd or change the rules unless -e 2 is set, and even then can reboot. Forwarding and a silent-source alert are the backstop.
  • Containers share the host kernel. Rules on the host see system calls from containers, but paths and user IDs are the container’s view, which makes attribution harder.
  • We have not measured the volume on your servers. aureport -k --summary on a test server is how you measure it.

Adapting this to your environment

  • ADAPT auid>=1000 if regular users start at a different UID (UID_MIN in /etc/login.defs).
  • ADAPT the service account rule: one line per internet-facing service account, with its UID.
  • ADAPT the architecture lines: keep arch=b32 on x86_64, remove it where 32-bit system calls do not exist.
  • Check for other files in /etc/audit/rules.d/. Distributions and hardening tools add their own; keep one -D, one -b and one -f overall.

The rules are linux-auditd-baseline.rules (MIT). Record the result for each server role in your log source inventory. If something here is wrong, write to hello@entropywatch.dev and we will correct it in the open.