Skip to content

LogsExecutionFile access

auditd audit.log: Linux Kernel Audit Trail

The Linux audit log written by auditd: PAM logins, syscalls, execve arguments and watched file access, each tied to the original login user (auid).

Location
/var/log/audit/audit.log
Proves
Which login user ran which program or touched which watched file, and every PAM authentication and session
Timestamps
Unix epoch seconds with milliseconds in msg=audit(sec.msec:serial), UTC
Access
root (log_group defaults to root)
Retention
Size based: upstream default 8 MiB x 5 files, rotated by auditd
Collection
UAC, Velociraptor, cp -a, ausearch --raw

What it is

The Linux kernel has an audit subsystem that emits records for security relevant events: syscalls that match loaded rules, file watches, configuration changes, and messages sent from user space by PAM, sudo, sshd, useradd and similar programs. auditd receives these records over netlink and writes them to /var/log/audit/audit.log. See the auditd glossary entry.

What ends up in the log depends entirely on the rules. With no custom rules you still get the user-space events (logins, authentication, session start/end, account changes, service start/stop) plus a few kernel events. With execve or file-watch rules, audit.log becomes the most detailed execution record on a Linux host.

Where it lives

ItemPathNotes
Log/var/log/audit/audit.log, rotated audit.log.1 ... audit.log.NHigher number = older
Daemon config/etc/audit/auditd.conflog_file, log_format, num_logs, max_log_file, max_log_file_action
Persistent rules/etc/audit/rules.d/*.rulesCompiled by augenrules into /etc/audit/audit.rules
Sample rules/usr/share/audit/sample-rules/ (audit 3.x, e.g. RHEL 8/9) or /usr/share/audit-rules/ (audit 4.x)STIG, PCI-DSS, OSPP rule sets
PackageRHEL/Fedora: audit, installed and enabled by defaultDebian/Ubuntu: auditd, not installed by default

On hosts without auditd, some kernel audit messages still reach the kernel log and the journal (_TRANSPORT=audit), so check the journal too.

What it proves

  • Authentication and session lifecycle through PAM: USER_AUTH, USER_ACCT, CRED_ACQ, USER_LOGIN, USER_START, USER_END, with acct=, addr=, terminal= and res=success|failed.
  • Program execution (when an execve rule exists): SYSCALL + EXECVE + CWD + PATH + PROCTITLE records reconstruct the full command line and directory.
  • Attribution across privilege changes: auid is the login UID set at login and inherited through sudo and su, so a root shell started by deploy still carries deploy's auid.
  • Account management (ADD_USER, DEL_USER, ADD_GROUP, USER_MGMT, USER_CHAUTHTOK) and sudo commands (USER_CMD).
  • Tampering with audit itself (CONFIG_CHANGE, DAEMON_START, DAEMON_END) and firewall rule changes (NETFILTER_CFG).
  • It does not prove anything the rules did not cover, and shell builtins never produce execve records.

Key fields

A typical execve event (records share the same msg=audit(...) stamp):

type=SYSCALL msg=audit(1789874047.512:8812): arch=c000003e syscall=59 success=yes exit=0 ppid=4190 pid=4302 auid=1001 uid=0 gid=0 euid=0 tty=pts0 ses=12 comm="curl" exe="/usr/bin/curl" key="exec"
type=EXECVE msg=audit(1789874047.512:8812): argc=3 a0="curl" a1="-o" a2="/tmp/.x"
type=CWD msg=audit(1789874047.512:8812): cwd="/root"
type=PROCTITLE msg=audit(1789874047.512:8812): proctitle=6375726C002D6F002F746D702F2E78
FieldMeaning
typeRecord type (SYSCALL, EXECVE, PATH, USER_LOGIN ...)
msg=audit(sec.msec:serial)Event time and serial; all records of one event share it
auidLogin UID; 4294967295 (unset, shown as unset) for processes not started from a login
uid, euid, gid ...Real and effective IDs at the time of the event
sesLogin session ID, links events to one login
syscall, success, exitSyscall number, result and return value
comm, exeProcess name and executable path
keyLabel from the rule (-k), the fastest filter
a0..aN (EXECVE)Arguments; hex encoded when they contain spaces or special characters
name, inode, mode, ouid, nametype (PATH)Files the syscall touched
proctitleCommand line, hex encoded with NUL separators
acct, addr, hostname, terminal, resIn PAM user records: account, remote address, tty, result

With the upstream default log_format = ENRICHED, auditd appends translated fields after a 0x1D (group separator) byte, in upper case: AUID="deploy" UID="root" SYSCALL=execve. These names were resolved on the original host, which makes them more reliable than ausearch -i on an analysis workstation. Audit 2.x defaulted to RAW and distributions can override the value, so check the config on the image.

Timestamps

msg=audit(1789874047.512:8812) holds Unix epoch seconds with a millisecond fraction, then the event serial. It is UTC by nature, so no zone conversion is needed.

date -u -d @1789874047.512
ausearch -if audit.log -ts 09/20/2026 03:00:00 -te 09/20/2026 04:00:00 -i   # date format follows the analysis host's locale

The serial is generated by the kernel and restarts after a reboot, so group records by the full sec.msec:serial stamp, not by serial alone.

Retention

auditd rotates its own log, not logrotate. The shipped upstream auditd.conf uses max_log_file = 8 (MiB), num_logs = 5 and max_log_file_action = ROTATE, so roughly 40 MiB of history. With execve auditing on a busy server that can be hours. num_logs = 0 or a keep_logs action change the behaviour; space_left_action and disk_full_action decide what happens when the disk fills (the shipped config uses SYSLOG for space_left_action and SUSPEND, which stops writing to disk, for admin_space_left_action and disk_full_action).

Collection

# Dead box
cp -a /mnt/evidence/var/log/audit /mnt/evidence/etc/audit /cases/2026-017/

# Live, as root: also capture the loaded rules and status
auditctl -l > auditctl_rules.txt
auditctl -s > auditctl_status.txt
tar -C / -cpf /media/ir/audit.tar var/log/audit etc/audit

UAC collects /var/log and runs auditctl -l and auditctl -s in live response. Loaded rules can differ from the files on disk if someone ran auditctl -D or added rules by hand.

Parsing

ausearch and aureport from the audit package are the reference tools and work on copied logs with -if.

A=/cases/2026-017/audit
ausearch -if "$A" -m USER_LOGIN,USER_AUTH -i                  # logins and auth, interpreted
ausearch -if "$A" -m EXECVE -ul 1001 -i                        # everything run by login UID 1001
ausearch -if "$A" -k exec --format csv > exec.csv              # by rule key, for spreadsheets
ausearch -if "$A" -m CONFIG_CHANGE,DAEMON_END,DAEMON_START -i  # audit tampering
aureport -if "$A" --login --summary -i
aureport -if "$A" -x --summary                                 # executables

Plaso's text/selinux parser plugin reads audit.log into a super timeline. Zircolite (--auditd) runs Sigma rules for Linux auditd over the log. Linux Log Parser reads auth.log / secure, syslog, journal files, audit.log and wtmp / btmp / lastlog in the browser and merges them into one timeline with rebuilt login sessions; nothing is uploaded.

Investigator tips

  • Before searching, read /etc/audit/rules.d/ on the image: absence of execve or -w rules explains missing execution evidence better than tampering does.
  • -i translates UIDs using the analysis host's /etc/passwd unless the log is enriched. Always check names against the image's own account files.
  • auid survives sudo -i and su -, so filtering on auid recovers what a user did in a root shell; compare with sudo logs and shell history.
  • Hunt for auditctl -e 0, auditctl -D, systemctl stop auditd and edits under /etc/audit/ in CONFIG_CHANGE and SYSCALL records. The immutable flag (-e 2) prevents rule changes until reboot, so an unexpected reboot just before an incident may be deliberate.
  • ses values link every record of one login; pair them with wtmp sessions and Accepted lines in auth.log.
  • Fewer rotated generations than num_logs allows, on a host busy enough to have filled them, suggests deletion. Compare file sizes with max_log_file.

See also