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
Tools
Compare all tools- Linux Log ParserIn browser
- ausearchCLI · built into Linux
- aureportCLI · built into Linux
- PlasoCLI · open source
- ZircoliteCLI · open source
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
| Item | Path | Notes |
|---|---|---|
| Log | /var/log/audit/audit.log, rotated audit.log.1 ... audit.log.N | Higher number = older |
| Daemon config | /etc/audit/auditd.conf | log_file, log_format, num_logs, max_log_file, max_log_file_action |
| Persistent rules | /etc/audit/rules.d/*.rules | Compiled 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 |
| Package | RHEL/Fedora: audit, installed and enabled by default | Debian/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, withacct=,addr=,terminal=andres=success|failed. - Program execution (when an execve rule exists):
SYSCALL+EXECVE+CWD+PATH+PROCTITLErecords reconstruct the full command line and directory. - Attribution across privilege changes:
auidis the login UID set at login and inherited throughsudoandsu, so a root shell started bydeploystill 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
| Field | Meaning |
|---|---|
type | Record type (SYSCALL, EXECVE, PATH, USER_LOGIN ...) |
msg=audit(sec.msec:serial) | Event time and serial; all records of one event share it |
auid | Login 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 |
ses | Login session ID, links events to one login |
syscall, success, exit | Syscall number, result and return value |
comm, exe | Process name and executable path |
key | Label 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 |
proctitle | Command line, hex encoded with NUL separators |
acct, addr, hostname, terminal, res | In 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-wrules explains missing execution evidence better than tampering does. -itranslates UIDs using the analysis host's/etc/passwdunless the log is enriched. Always check names against the image's own account files.auidsurvivessudo -iandsu -, so filtering onauidrecovers what a user did in a root shell; compare with sudo logs and shell history.- Hunt for
auditctl -e 0,auditctl -D,systemctl stop auditdand edits under/etc/audit/inCONFIG_CHANGEandSYSCALLrecords. The immutable flag (-e 2) prevents rule changes until reboot, so an unexpected reboot just before an incident may be deliberate. sesvalues link every record of one login; pair them with wtmp sessions andAcceptedlines in auth.log.- Fewer rotated generations than
num_logsallows, on a host busy enough to have filled them, suggests deletion. Compare file sizes withmax_log_file.