Skip to content

PersistenceAnti-forensicsLogs

PAM Configuration and Modules: Linux Auth Backdoors

Linux PAM stacks in /etc/pam.d and the pam_*.so modules they load: where attackers plant password loggers and master passwords, and how to verify them.

Location
/etc/pam.d/, /usr/lib64/security/ (RHEL, SUSE), /usr/lib/x86_64-linux-gnu/security/ (Debian/Ubuntu), /usr/lib/security/ (Arch)
Proves
How the host authenticates logins, sudo and su, and whether that chain was altered to accept a backdoor password or capture credentials
Timestamps
File system times of stack files and modules; PAM messages in auth logs and the journal
Access
root to modify; configuration world-readable, modules readable by all
Retention
Persistent until changed; package updates may overwrite a patched module
Collection
UAC, Velociraptor, cp -a, tar
  • rpmCLI · built into Linux
  • dpkgCLI · built into Linux
  • debsumsCLI · open source
  • authselectCLI · built into Linux
  • findCLI · built into Linux
  • grep / zgrepCLI · built into Linux
  • stringsCLI · built into Linux

What it is

Pluggable Authentication Modules (Linux-PAM) sit between programs that need to authenticate a user (sshd, login, sudo, su, gdm, cron, passwd) and the actual checks. Each program has a stack file in /etc/pam.d/ listing modules by type (auth, account, password, session) and control flag (required, requisite, sufficient, optional, or the bracket syntax). The modules are shared libraries, typically pam_unix.so for /etc/shadow passwords.

Because every password passes through PAM in clear text, it is a prime target. MITRE ATT&CK T1556.003 covers three patterns seen in real intrusions: a patched pam_unix.so that accepts a hard-coded master password or writes credentials to a file, an extra malicious module inserted into a stack, and a legitimate module misused, such as pam_exec.so with expose_authtok feeding every password to a script.

Where it lives

ItemDebian / UbuntuRHEL / FedoraSUSEArch
Stack files/etc/pam.d/samesame (also /usr/lib/pam.d/ vendor defaults on recent releases)same
Shared includescommon-auth, common-account, common-password, common-sessionsystem-auth, password-auth (symlinks into /etc/authselect/)common-* symlinks to common-*-pcsystem-auth, system-login
Include managerpam-auth-update from /usr/share/pam-configs/authselect (RHEL 8+, Fedora 28+)pam-confignone, edited by hand
Modules/usr/lib/x86_64-linux-gnu/security/ (also reachable as /lib/... on merged-usr systems)/usr/lib64/security//usr/lib64/security/ or /lib64/security/ on older releases/usr/lib/security/
Module settings/etc/security/ (access.conf, limits.conf, pam_env.conf, faillock.conf)samesamesame

A legacy single file, /etc/pam.conf, is still honoured when /etc/pam.d/ is missing. It should not exist on a modern system.

What it proves

  • Which modules run for each service and in which order, so you can tell whether a password check can be skipped (auth sufficient pam_permit.so at the top of sshd or common-auth accepts any password).
  • Whether a module file differs from the vendor package (patched pam_unix.so) or is not owned by any package at all.
  • Whether passwords were exposed to an external program (pam_exec.so expose_authtok, pam_script, or an unknown .so).
  • When the configuration changed, from file times and from package manager or authselect state.
  • It does not directly show which passwords were captured; look for the output file the backdoor writes, often in /tmp, /dev/shm, /var/tmp or a hidden path found with strings.

Key fields

A normal Debian common-auth next to a tampered one:

# normal
auth    [success=1 default=ignore]  pam_unix.so nullok
auth    requisite                   pam_deny.so
auth    required                    pam_permit.so

# tampered
auth    optional                    pam_exec.so quiet expose_authtok /usr/lib/.cache/pw.sh
auth    sufficient                  pam_sshd_helper.so
auth    [success=1 default=ignore]  pam_unix.so nullok
ElementWhat to check
Type (auth, account, password, session)auth and password lines see the clear-text password
Controlsufficient before pam_unix.so means success there ends the check
Module pathBare names resolve to the module directory; absolute paths to other directories are unusual
Argumentsexpose_authtok, seteuid, script paths, nullok (allows empty passwords)
@include / include / substackFollow them; a malicious line can hide in a shared include

Timestamps

Stack files and modules only carry file system times. Package-installed modules keep their build-time mtime, so a module whose ctime is much newer than the last pam or libpam-modules update in the package logs was touched afterwards. Attackers often copy the original mtime back with touch -r, which does not reset ctime or ext4 birth time.

PAM itself logs through syslog (pam_unix(sshd:auth): authentication failure, session opened for user), so authentication events land in auth.log or secure and the journal. With auditd running, PAM also emits USER_AUTH and USER_ACCT records whose grantors= field lists the modules that granted access (an unknown module name there is a strong lead), see audit.log.

Retention

Configuration persists until changed. A package update of pam or libpam-modules replaces a patched pam_unix.so with the vendor file, silently removing the backdoor, while leaving stack edits in place (dpkg treats /etc/pam.d/ files as conffiles and keeps local changes).

Collection

# Dead box
tar -C /mnt/evidence -cpf /cases/2026-017/pam.tar etc/pam.d etc/pam.conf etc/security etc/authselect \
  usr/lib/x86_64-linux-gnu/security usr/lib64/security lib64/security usr/lib/security 2>/dev/null

UAC collects all of /etc and hashes binaries in its default profiles; add the module directory to a file collector if you need the libraries themselves. Velociraptor's Linux.Search.FileFinder with a hash option covers the module paths fleet-wide.

Parsing

R=/mnt/evidence
# Package verification of modules and configs
rpm --root "$R" -V pam                      # RHEL/SUSE: 5 = digest changed, M = mode, T = mtime
dpkg --root="$R" -V libpam-modules libpam-modules-bin libpam-runtime   # Debian/Ubuntu
debsums -r "$R" -s libpam-modules           # alternative on Debian
authselect check                            # live RHEL: reports edits made outside authselect

# Modules not owned by any package (Debian)
for m in "$R"/usr/lib/x86_64-linux-gnu/security/*.so; do dpkg --root="$R" -S "${m#$R}" >/dev/null 2>&1 || echo "unowned: $m"; done

# Suspicious arguments and newest stack files
grep -rnE 'expose_authtok|pam_exec|pam_script|pam_permit|/tmp/|/dev/shm/' "$R"/etc/pam.d/
find "$R"/etc/pam.d "$R"/usr/lib64/security "$R"/usr/lib/x86_64-linux-gnu/security -type f -printf '%C+ %p\n' 2>/dev/null | sort -r | head -20

# Strings in a suspect module: hard-coded passwords, log paths
strings -a "$R"/usr/lib64/security/pam_unix.so | grep -E '^/|%s|pass' | head -50

Investigator tips

  • Compare pam_unix.so against a clean copy from the same package version, or the vendor's hash; size alone can match on a carefully patched binary.
  • Check every service stack, not just sshd: sudo, su, login, gdm-password, sshd and common-auth/system-auth are all used for backdoors.
  • Credential log files written by a PAM backdoor often sit next to legitimate files with plausible names. Search for files modified at the same second as recent successful logins.
  • A module that exists only in memory will not be on disk. On a live host, check /proc/<sshd pid>/maps for .so paths outside the module directory and compare with LD preload hijacks.
  • After containment, reinstall the PAM packages and review stack files by hand; package reinstall does not revert local edits to conffiles.

See also