那天傍晚,SOC平台弹出一条高优先级告警:某台对外业务的Web服务器在过去的3小时里,每隔30分钟向一个海外IP发起一次HTTP外联,每次流量1到3KB。说实话,第一反应是挖矿木马——这种定时、小流量、到固定IP的外联模式,太像矿机在向矿池汇报挖矿结果了。但登上服务器翻了一圈,进程列表干干净净,CPU使用率也平稳得不像话,crontab、systemd定时器、网络连接、登录日志全部正常。于是把流量导出来准备抓包细看,结果发现根本不是矿池通信,而是一个Webshell在周期性接收外部指令,并回显命令执行结果。
这篇文章就记录从怀疑到确认,再到处置加固的完整过程。重点会放在流量分析的方法论上:哪些字段最可疑、如何从pcap里把一条命令执行的完整链路捞出来、安全设备为什么没拦住、处置时又应该按什么顺序做才不遗漏证据。如果你也需要面对类似告警,或者想提前知道Webshell流量长什么样,这篇文章应该能帮上忙。
1. 不是挖矿木马,是Webshell在做外联
1.1 从告警信息里能读出哪些有用情报
告警是安全设备给的,但设备只会告诉你“有异常”,不会告诉你“发生了什么”。所以拿到告警的第一件事,是把原始信息拆开看。我当时从SOC平台拉到了下面这组字段:
| 字段 | 值 | 分析价值 |
|---|---|---|
| 源IP | 10.x.x.35(业务服务器内网IP) | 确认是我们要保护的资产 |
| 目的IP | 45.x.x.x(海外) | 跨地域外联,明显不符合业务需要 |
| 目的端口 | 80 | 不是常见矿池端口,下面细说 |
| 应用协议 | HTTP | 与业务对外提供的Web服务端口一致 |
| 发生次数 | 约6次/3小时 | 频率固定,说明是自动化任务 |
| 单次流量 | 上传约1KB,下载约2~4KB | 体量很小但双向都有数据 |
很多人看到“海外IP+定时外联”就自动归为矿池通信,但这里有两个细节不对劲。第一,常见矿池端口是3333、4444、14444、5555这类非标准端口,走80端口且协议是HTTP的矿池不是没有,但很少;第二,矿机上行数据虽然小,但下行数据通常更小甚至几乎为0,因为矿池只需要向矿机下发任务和难度调整,没必要每次回传2~4KB。而一个Webshell在等待指令时,请求体和响应体往往都有实际内容——请求体里是命令参数,响应体里是命令输出。这个双向流量特征,是我后续决定抓包分析的最重要原因。
1.2 先排查主机侧异常:为什么一开始查不到
拿到告警后,我按常规顺序检查了服务器主机本身。不看不知道,看了之后更疑惑。
top、ps aux:CPU正常,进程列表只有php-fpm、nginx、mysql等常规服务,没有看到任何可疑进程名。netstat -antlp:正在建立的连接里确实有到45.x.x.x:80的,但对应的进程PID是php-fpm,这在Web服务器上太正常了——php-fpm本来就要处理HTTP请求,攻击者只要让Webshell以Web请求的方式发外联,进程层面就永远不会亮红灯。crontab -l、systemctl list-timers:没有发现新增的定时任务。last、/var/log/auth.log:没有异常登录记录,也没有SSH爆破成功的迹象。lsmod、/proc/:没有加载可疑内核模块,隐藏进程也没扫出来。
也就是说,主机侧检查看起来“一切正常”,但网络侧明明有可疑外联,这个矛盾本身就说明问题不在系统层,而在Web应用层。Webshell是寄生在PHP-FPM进程里的,它不注册服务、不写定时任务、不弹shell,全靠HTTP请求来驱动,所以传统的进程排查、登录日志排查、rootkit扫描对它基本无效。这也是为什么流量分析在Webshell排查里是不可替代的手段——你查不到一个不存在的进程,但所有行为都会在网络上留下痕迹。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从pcap里捞出完整请求链:三个关键特征
2.1 第一步:把可疑外联流量完整抓下来
主机侧没结果,我决定回到网络侧,直接抓包。这里有个前提:最好在安全设备或交换机镜像口上抓,而不是直接在业务服务器上抓。如果Webshell已经带有检测Sandbox或反调试逻辑,你在一台已经失陷的机器
