sudo Logs: Linux Privilege Use Records
How sudo records who ran what as root on Linux: syslog and journal lines, optional log files and I/O session recordings, plus sudoers policy files.
- Location
- /var/log/auth.log, /var/log/secure
- Proves
- Which account ran which command as which target user, from which terminal and directory, and failed attempts
- Timestamps
- Syslog/journal time of the host (see syslog formats); I/O logs keep relative timing per session
- Access
- root (or adm group for auth.log on Debian/Ubuntu)
- Retention
- Follows auth.log/secure rotation and journal limits; I/O logs until deleted
- Collection
- UAC, Velociraptor, cp -a, journalctl
Tools
Compare all tools- Linux Log ParserIn browser
- grep / zgrepCLI · built into Linux
- journalctlCLI · built into Linux
- sudoreplayCLI · built into Linux
- ausearchCLI · built into Linux
- PlasoCLI · open source
What it is
sudo's sudoers policy plugin logs every accepted and rejected request. By default it sends one line per event to syslog, using the authpriv facility where available (priority notice for allowed, alert for denied). Those lines land in the authentication text log and in the journal. Optional settings add a dedicated log file, JSON output, exit status, logging of sub-commands, and full keystroke and screen recordings of sessions.
The policy itself (/etc/sudoers and its include directories) is also evidence: an attacker who adds a NOPASSWD rule has created persistence and removed the password prompt that would otherwise show up as an authentication event.
Where it lives
| Item | Debian / Ubuntu | RHEL / Fedora | Notes |
|---|---|---|---|
| Syslog lines | /var/log/auth.log | /var/log/secure | Plus the journal (SYSLOG_IDENTIFIER=sudo) |
| Policy | /etc/sudoers, /etc/sudoers.d/* | same | @includedir/#includedir; files containing . or ending in ~ are skipped |
| Optional log file | Defaults logfile=... (off by default) | same | Any path, for example /var/log/sudo.log |
| I/O session logs | /var/log/sudo-io/ | same | Only with log_input/log_output |
| Credential cache | /run/sudo/ts/<user> | /run/sudo/ts/ or /var/run/sudo/ts/ | Cleared at boot |
| Lecture status | /var/lib/sudo/lectured/<user> | /var/db/sudo/lectured/ or /var/lib/sudo/lectured/ | Zero-length file per user, depends on build |
| Audit records | USER_CMD in audit.log | same | When sudo is built with Linux audit support and auditd runs |
| First-use marker | ~/.sudo_as_admin_successful (Ubuntu) | none | Created on first successful sudo |
What it proves
- The invoking account, target user (
USER=), terminal, working directory and full command line of each allowed command. - Denials:
user NOT in sudoers,user NOT authorized on host,command not allowed,N incorrect password attempts,a password is required. - PAM context around it:
pam_unix(sudo:session): session opened for user root(uid=0) by deploy(uid=1001)andpam_unix(sudo:auth): authentication failure. - With I/O logging: exactly what was typed and displayed, replayable.
- It does not show what happened inside
sudo -i,sudo -sorsudo bash: the log records only the shell. Use auditd (auid), shell history,log_subcmdsor I/O logs for that. - A user with root can edit these logs, and anyone can inject look-alike lines with
logger.
Key fields
Accepted command, sudo log format:
Sep 20 03:15:30 web-prod-03 sudo: deploy : TTY=pts/0 ; PWD=/home/deploy ; USER=root ; COMMAND=/usr/bin/bash
Sep 20 03:16:02 web-prod-03 sudo: deploy : TTY=pts/0 ; PWD=/tmp ; USER=root ; TSID=000001 ; COMMAND=/usr/bin/tar -xf /tmp/x.tar
Sep 20 03:17:44 web-prod-03 sudo: intern : user NOT in sudoers ; TTY=pts/1 ; PWD=/home/intern ; USER=root ; COMMAND=/usr/bin/id
| Field | Meaning |
|---|---|
user before : | Login name of the user who ran sudo |
| denial reason | Present only for rejected requests |
TTY= | Terminal, or unknown without a tty (scripts, some remote commands) |
PWD= | Working directory when sudo ran |
USER= / GROUP= | Target user and optional group |
CHROOT= | Root directory if one was requested |
TSID= | I/O log ID, present only when I/O logging is on |
ENV= | Environment variables set on the command line |
COMMAND= | Resolved command path and arguments |
Control characters are logged in octal with a leading # (#011 for tab), and spaces in the command path appear as #040. With log_format=json the file log holds JSON objects with the full user details and environment.
Audit record for the same action (RHEL family):
type=USER_CMD msg=audit(1789874130.201:8830): pid=4302 uid=1001 auid=1001 ses=12 msg='cwd="/home/deploy" cmd="/usr/bin/bash" terminal=pts/0 res=success'
cmd and cwd are hex encoded when they contain spaces; ausearch -i decodes them.
Timestamps
Syslog lines inherit the syslog daemon's format: traditional local time without year on RHEL defaults, RFC 3339 with offset on Debian 12+ and Ubuntu 24.04 (see auth.log). The journal copy has a UTC microsecond timestamp and is the easier anchor. sudo's own log file uses a Mmm dd HH:MM:SS style date and adds the year only with log_year. I/O logs record the session start in the log/log.json file and relative delays in timing.
Retention
The syslog copy lives as long as auth.log/secure (about four weekly generations by default) and the journal copy as long as the journal's size limits allow. I/O logs are never rotated by sudo; the six-character base-36 session ID (00/00/01) wraps at maxseq, after which old sessions are overwritten. The lecture files and ~/.sudo_as_admin_successful persist until deleted.
Collection
E=/mnt/evidence
tar -C "$E" -cpf /cases/2026-017/sudo.tar \
etc/sudoers etc/sudoers.d var/log/sudo-io var/lib/sudo var/db/sudo 2>/dev/null
journalctl -D "$E/var/log/journal" SYSLOG_IDENTIFIER=sudo -o json --utc > sudo-journal.json
zgrep -h 'sudo:' "$E"/var/log/auth.log* "$E"/var/log/secure* 2>/dev/null > sudo-lines.txt
UAC collects /var/log, /etc and the sudo lecture timestamps (sudo_lectured artifact). On a live system, sudo -l -U <user> shows the effective rights of an account without editing anything.
Parsing
# Allowed commands per invoking user
zgrep -h 'COMMAND=' auth.log* | sed -E 's/.*sudo: +([^ ]+) : .*USER=([^ ]+) ; .*COMMAND=(.*)/\1 -> \2 : \3/' | sort | uniq -c
# Failures
zgrep -hE 'sudo:.*(NOT in sudoers|not allowed|incorrect password)' auth.log*
# Replay a recorded session (read-only)
sudoreplay -d /cases/2026-017/var/log/sudo-io -l
sudoreplay -d /cases/2026-017/var/log/sudo-io 000001
# Audit copy
ausearch -if audit.log -m USER_CMD -i
Each I/O session directory holds log, log.json, timing, ttyin, ttyout, stdin, stdout and stderr. Plaso picks up sudo lines through its syslog parsers. 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
- Review every file in
/etc/sudoers.d/and its mtime/ctime. NewNOPASSWD: ALLentries,Defaults !syslog,!log_allowedorsyslog_goodpri=nonesilence logging and are strong indicators. COMMAND=/usr/bin/bash,/bin/su, editors,find,less,vior interpreters mean the real activity happened in a child process; followauidin audit.log and the account's shell history.- sudo success without a preceding
pam_unix(sudo:auth)prompt is normal withintimestamp_timeout(5 minutes by default) or withNOPASSWD; the files in/run/sudo/ts/exist only on a live system. - The lecture file is created the first time sudo shows its lecture with a password prompt (default
lecture=once). Its timestamps and Ubuntu's~/.sudo_as_admin_successfulgive a first-use date for sudo per account, useful for newly created attacker accounts (see accounts). - Lines with
TTY=unknowncome from scripts, cron or non-interactive SSH; correlate with cron and sshd lines. - Compare text log, journal and audit copies: a sudo event in the journal but not in auth.log points to editing of the text file.