问题背景
我们有一台对外提供图片处理 API 的 Linux 服务器(Ubuntu 20.04),业务进程由 systemd 拉起,运行账户是低权限的 www-data。按设计,这台机器上不应该有任何以 root 身份运行的业务相关进程——应用连读写自己数据目录都是 www-data 权限。
某天凌晨,主机安全(HIDS)弹出一条中危告警:/etc/passwd 文件被修改。值班同事第一反应是误报,但登上去一看,/etc/passwd 末尾赫然多了一行 UID 为 0 的账户 oracle(这台机器根本没装 Oracle)。一个只该跑 www-data 的应用服务器,凭空冒出一个 root 等价账户,这不是误报,是实打实被人拿到 root 了。问题是:入口在哪?攻击者是怎么从低权限爬到 root 的?
故障现象
初步观察到的异常集中在三处:
- 多出一个 UID 0 账户:
grep ':0:' /etc/passwd除了root外还有oracle:x:0:0::/home/oracle:/bin/bash,是典型的“影子 root”后门。 - root 身份的异常进程:
ps -ef里有一个 root 拥有的.../.cache/.sysupdate进程在持续外联,而它的父进程链最终追溯到www-data启动的应用。 - 可疑的 sudo 使用记录:
/var/log/auth.log中出现多条www-data执行sudo成功的记录,而运维从没给应用账户配过“正常要用”的 sudo。
关键矛盾点是:www-data 是低权限账户,正常情况下它连 sudo 都不该能用,为什么日志里它能成功执行 sudo,还最终跑出了 root 进程?这条“低权限 → root”的链路就是本次排查的核心。
排查过程
第一步,锁定 root 进程的来源。 先别急着删账户,先看攻击链。用 ps 打印完整父子关系:
|
|
拿到可疑进程 PID 后,逐级回溯 PPID,发现链路是 systemd → 应用主进程(www-data) → sh → sudo → .sysupdate(root)。也就是说,是应用进程内部触发了一次 sudo,把自己提权成了 root。应用为什么会去调 sudo?八成是应用先被打穿,攻击者拿到了 www-data 的命令执行能力。
第二步,找应用层入口。 查 Nginx access log,定位到图片上传接口有大量异常 POST,其中夹着对一个临时诊断脚本的调用,攻击者借它拿到了 www-data 的 webshell(这部分属于应用漏洞,是另一条线,本文聚焦提权)。确认落脚点确实是 www-data 后,重点转向:www-data 手里能有什么提权的“梯子”。
第三步,检查 sudo 权限——问题的核心。 以 www-data 身份看它能免密执行什么:
|
|
输出让人后背发凉:
|
|
www-data 被授予了对 find 和 tar 的免密 sudo。翻 GTFOBins 就知道,这两个命令都能直接“越狱”成 root:
|
|
auth.log 里 www-data 成功执行的正是 sudo find ... -exec。攻击者拿到 www-data 后,只用一条命令就借 find 拿到了 root shell,接着写影子账户、装外联程序做持久化。至此,“低权限 → root”的整条链路清晰了:应用漏洞拿 www-data → sudoers 里可提权命令的 NOPASSWD → GTFOBins 一步登天。
第四步,摸清攻击者干了什么。 用 find / -newermt "$(date -d '2 days ago' +%F)" -type f 2>/dev/null 找近两天被改动的文件,结合 auth.log、~/.bash_history、/var/log/audit(可惜 auditd 没开,取证信息有限),确认攻击者做了:加影子 root 账户、下 .sysupdate 外联程序、在 /etc/rc.local 和一个 www-data 的 crontab 里都留了拉起脚本。
解决方案
确认范围后按“先隔离、后清理、再加固”推进:
- 隔离:安全组只保留运维跳板机入站,切断
.sysupdate的外联 C2,业务临时摘出负载均衡。 - 清理落地物:
kill掉外联进程,删除oracle影子账户(userdel -r oracle),清掉/etc/rc.local、www-datacrontab 里的持久化项,删掉.sysupdate及其.cache目录。 - 收敛 sudo 权限——治本第一刀:直接删掉
/etc/sudoers.d/下那条给www-data授权的文件,www-data恢复到“完全不能 sudo”的状态。用visudo -c校验语法无误。 - 凭据与密钥轮换:机器上涉及的数据库口令、API Key、SSH 私钥全部轮换,因为 root 沦陷期间它们都可能已泄露。
- 重装保底:由于取证不完整、无法 100% 断定没有更深的内核态后门,最终选择备份数据后重装系统,用干净基线重新上线,而不是原地“清干净”后继续用。
根因分析
技术根因只有一句话:给低权限应用账户配了可交互提权命令的免密 sudo。find、tar、vim、awk、systemctl、less 这类命令都能在 root 上下文里派生 shell 或执行任意命令,一旦以 NOPASSWD 授权给应用账户,等于把 root 直接送出去——它把“应用被打穿”这种局部失陷,一步放大成了整机 root 沦陷。
追溯 sudoers 的来历:几个月前有个批量打包/清理的运维脚本要以 www-data 跑,为图省事,运维直接给它开了 find/tar 的 NOPASSWD,用完既没回收、也没做最小化。这条配置就成了埋在系统里的提权后门,静静等着应用层任何一个漏洞来引爆。
预防措施
- sudoers 最小权限:绝不给应用/服务账户授予可交互提权的命令。确需授权,只给具体子命令和固定参数,杜绝
ALL和裸命令;能用Cmnd_Alias精确到路径与参数就精确到底。 - 定期审计提权面:把
sudo -l -U <每个服务账户>纳入基线巡检,对照 GTFOBins 名单排查危险命令;同时扫描 SUID/SGID 文件(find / -perm -4000 -type f)防止另一条提权路径。 - 开启 sudo I/O 日志与 auditd:
sudoers里配Defaults log_output,并启用 auditd 记录 execve,出事时才有完整取证链,别再像这次一样两眼一抹黑。 - 账户与文件完整性监控:对
/etc/passwd、/etc/sudoers、/etc/sudoers.d/、authorized_keys做 FIM 监控(本次正是/etc/passwd告警救了场),任何 UID 0 新增即高危报警。 - 纵深防御:应用漏洞是第一道被突破的门,提权是第二道;两道都得守。低权限运行 + 最小 sudo + 目录不可执行 + EDR,缺一不可。
总结
这次事件里,应用被打穿只是“擦伤”,真正致命的是那条给 www-data 开的 find/tar 免密 sudo——它让一次普通的低权限失陷直接升级成整机 root 沦陷。排查的关键动作其实很朴素:顺着 root 进程的父进程链往回追,一路追到 sudo,再用 sudo -l -U 一照,问题原形毕露。给运维同行的提醒是:任何以 NOPASSWD 授权给服务账户的命令,都要先去 GTFOBins 查一遍能不能提权;最小权限不是口号,它就是拦在“局部失陷”和“整机沦陷”之间的那道墙。