记一次Web应用账户经sudo配置不当提权到root的入侵溯源与加固

应用只应低权限运行却出现root异常进程,顺着sudo配置不当与GTFOBins提权链还原入侵并加固的实录。

问题背景

我们有一台对外提供图片处理 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 的?

故障现象

初步观察到的异常集中在三处:

  1. 多出一个 UID 0 账户grep ':0:' /etc/passwd 除了 root 外还有 oracle:x:0:0::/home/oracle:/bin/bash,是典型的“影子 root”后门。
  2. root 身份的异常进程ps -ef 里有一个 root 拥有的 .../.cache/.sysupdate 进程在持续外联,而它的父进程链最终追溯到 www-data 启动的应用。
  3. 可疑的 sudo 使用记录/var/log/auth.log 中出现多条 www-data 执行 sudo 成功的记录,而运维从没给应用账户配过“正常要用”的 sudo。

关键矛盾点是:www-data 是低权限账户,正常情况下它连 sudo 都不该能用,为什么日志里它能成功执行 sudo,还最终跑出了 root 进程?这条“低权限 → root”的链路就是本次排查的核心。

排查过程

第一步,锁定 root 进程的来源。 先别急着删账户,先看攻击链。用 ps 打印完整父子关系:

1
ps -eo pid,ppid,user,lstart,cmd | grep -v grep | grep sysupdate

拿到可疑进程 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 身份看它能免密执行什么:

1
sudo -l -U www-data

输出让人后背发凉:

1
2
3
User www-data may run the following commands on this host:
    (ALL : ALL) NOPASSWD: /usr/bin/find
    (ALL : ALL) NOPASSWD: /usr/bin/tar

www-data 被授予了对 findtar免密 sudo。翻 GTFOBins 就知道,这两个命令都能直接“越狱”成 root:

1
2
3
4
# find 的 -exec 可以在 root 上下文里拉起 shell
sudo find . -maxdepth 0 -exec /bin/sh \; -quit
# tar 的 checkpoint-action 也能执行任意命令
sudo tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh

auth.logwww-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 里都留了拉起脚本。

解决方案

确认范围后按“先隔离、后清理、再加固”推进:

  1. 隔离:安全组只保留运维跳板机入站,切断 .sysupdate 的外联 C2,业务临时摘出负载均衡。
  2. 清理落地物kill 掉外联进程,删除 oracle 影子账户(userdel -r oracle),清掉 /etc/rc.localwww-data crontab 里的持久化项,删掉 .sysupdate 及其 .cache 目录。
  3. 收敛 sudo 权限——治本第一刀:直接删掉 /etc/sudoers.d/ 下那条给 www-data 授权的文件,www-data 恢复到“完全不能 sudo”的状态。用 visudo -c 校验语法无误。
  4. 凭据与密钥轮换:机器上涉及的数据库口令、API Key、SSH 私钥全部轮换,因为 root 沦陷期间它们都可能已泄露。
  5. 重装保底:由于取证不完整、无法 100% 断定没有更深的内核态后门,最终选择备份数据后重装系统,用干净基线重新上线,而不是原地“清干净”后继续用。

根因分析

技术根因只有一句话:给低权限应用账户配了可交互提权命令的免密 sudofindtarvimawksystemctlless 这类命令都能在 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 日志与 auditdsudoers 里配 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 查一遍能不能提权;最小权限不是口号,它就是拦在“局部失陷”和“整机沦陷”之间的那道墙。

使用 Hugo 构建
主题 StackJimmy 设计