前言
今天早上遇到一个非常头疼的问题:我的一台云服务器上部署的网站,访问时会有很大概率跳转到赌球网站(刚好世界杯期间,什么牛鬼蛇神都出来了)。更诡异的是,哪怕我放一个纯空白 HTML 页面上去,依然会被劫持。折腾了半天,最终定位到是一套精心伪装的博彩木马。本文记录完整的排查、分析和清理过程。
一、症状描述
先说环境:
- 三台云服务器,同一家服务商
- 出问题的这台:Ubuntu 系统
- 另外两台正常的:CentOS 系统
- 所有网站运行在 Nginx 上,通过 宝塔面板 管理
症状:
- 访问网站(无论哪个域名),浏览器有很高概率跳转到
https://dd0a000h4orvt.cloudfxxxt.net/?cid=8214805(域名已做了无害处理)这个赌球网站

- 即使是刚上传的空白
test.html(内容就一句hello),依然会跳转; - 开启 Cloudflare 小黄云(代理模式)无效,关闭小黄云(DNS only)也无效;
- 从服务器上
curl访问页面,返回正常,没有任何跳转; - 另外两台 CentOS 服务器完全正常
跳转链(之前没有捕捉到前面那个xyz跳转链接所以走了一些弯路):
先是跳转faradfdsa.anfxxc.xyz,接着很快跳转至dd0a000h4orvt.cloudfxxxt.net/?cid=8214805二、初步分析
2.1 排除 ISP/DNS 层面
因为只有这一台服务器有问题,而三台机器用的是同一家云服务商,所以排除了"运营商线路劫持"的可能。如果是 ISP 层面的劫持,三台服务器应该都会中招。
2.2 为什么 Cloudflare 无效
Cloudflare 的代理模式会将用户流量先经过 Cloudflare 节点再转发到源服务器。如果劫持发生在源服务器发出响应之后(比如浏览器执行时),那么 Cloudflare 也救不了。
2.3 核心疑点:curl 正常但浏览器跳转
这说明劫持不是发生在 Nginx 配置层面(因为 curl 返回的是原始响应),而是在更上层——要么是浏览器端被注入 JavaScript,要么是网络传输层被篡改。
三、第一轮排查:发现 defunct / iptable6 rootkit
3.1 先看 Nginx 配置
nginx -T 2>/dev/null | grep -i "redirect\|rewrite\|return 302\|sub_filter"输出中除了正常的 rewrite 规则外,没有发现明显的恶意配置。
3.2 检查进程——发现重大线索
lsof -i :443输出赫然出现了三个可疑进程:
defunct 812 root 3u TCP spk.laws.ms:33018->ec2.us-east-2.amazonaws.com:https (ESTABLISHED)
iptable6 817 root 4u TCP spk.laws.ms:54498->ec2-13-57-38-185.us-west-1.compute.amazonaws.com:https (ESTABLISHED)
iptable6 28789 root 4u TCP spk.laws.ms:54498->ec2-13-57-38-185.us-west-1.compute.amazonaws.com:https (ESTABLISHED)defunct——这个名字看起来像是进程状态(<defunct>表示僵尸进程),实则是恶意程序iptable6——伪装成系统工具(正确名称是ip6tables),拼写故意只差一点点
这两个进程都在向外部的 AWS EC2 服务器建立 HTTPS 连接。
3.3 定位二进制文件
file /usr/bin/defunct /usr/bin/iptable6
ls -la /usr/bin/defunct /usr/bin/iptable6/usr/bin/defunct: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, stripped
/usr/bin/iptable6: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, no section header两个文件都是静态链接、strip 去符号,显然是恶意软件标准做法。defunct 的文件日期被伪造为 2003-09-08,而 iptable6 的日期是 2026-04-28,说明是近期才植入的。
lsof -i -P -n | grep -E "defunct|iptable6"defunct 812 root TCP 127.0.0.1:33018->212.132.98.170:443 (ESTABLISHED)
iptable6 817 root TCP 127.0.0.1:54498->13.57.38.185:443 (ESTABLISHED)两个二进制分别连接不同的 C2 服务器,形成两条指挥通道。
3.4 找到自启动持久化机制
grep -r "iptable6\|defunct" /etc/init.d/ /lib/systemd/ 2>/dev/null结果令人震惊——几乎每个 /etc/init.d/ 下的脚本都被注入了 /bin/iptable6:
/etc/init.d/keyboard-setup.sh:/bin/iptable6
/etc/init.d/apparmor:/bin/iptable6
/etc/init.d/docker:/bin/iptable6
/etc/init.d/open-vm-tools:/bin/iptable6
/etc/init.d/postfix:/bin/iptable6
...(共 20+ 个文件)更隐蔽的是 systemd 层面:
/lib/systemd/system/defunct.service:
ExecStart=/bin/bash -c "GS_ARGS='-k /lib/systemd/system/defunct.dat -ilq' exec -a '[kswapd0]' '/usr/bin/defunct'"这里使用了 exec -a '[kswapd0]' 技巧——将进程名伪装成 Linux 内核线程 [kswapd0],用 ps aux 根本看不到它。
3.5 查看登录记录——找到攻击来源
last -10root pts/2 212.132.98.170 Mon Jun 1 (02:01)6月1日凌晨,一个来自 212.132.98.170 的 IP 通过 SSH 登录了这台服务器——这就是攻击者。
3.6 Docker 容器的问题
还发现服务器上运行了两个可疑的 Docker 容器:
docker ps -atraffmonetizer/cli_v2 ← 带宽贩卖工具
earnfm/earnfm-client:latest ← 同类流量变现服务这两个容器将服务器作为代理出口节点,虽然不是导致劫跳转的直接原因,但它们进一步消耗服务器资源且可能引来更多攻击。
第一轮清理
| 操作 | 详情 |
|---|---|
停止并删除 defunct.service systemd 服务 | ✅ rm -f /lib/systemd/system/defunct.service /defunct.dat |
杀掉 defunct / iptable6 / [kswapd0] 进程 | ✅ |
删除 /usr/bin/defunct / /usr/bin/iptable6 | ✅ |
清理所有 /etc/init.d/* 中的注入命令 | ✅ sed -i '/\/bin\/iptable6/d' /etc/init.d/* |
| 删除 traffmonetizer / earnfm Docker 容器 | ✅ |
| 修改 root / 面板 / 数据库密码 | ✅ |
| 重启服务器验证 | ✅ 跳转消失 |
第一阶段判断
当时以为是 defunct/iptable6 这个 rootkit 做的中间人攻击(MITM),在 HTTP 响应中注入 JavaScript 跳转代码。重启后确认跳转消失,认为是彻底解决了。
四、第二轮排查:跳转复发 —— 发现 Nginx Lua 后门
4.1 复发
清理后不到一个小时,手机端再次出现跳转到赌博网站的问题。PC 端访问正常,手机端(Android/iPhone)依然会 302 跳转。这意味着:
- 不是 defunct/iptable6 导致的(它们已被清除)
- 还有一套独立的、更隐蔽的后门在运行
- 这个后门只针对移动设备——说明攻击者很清楚用户群体
4.2 深入排查 Nginx 层
再次检查 Nginx 配置,这次更仔细地看有没有加载 Lua 模块:
nginx -V 2>&1 | grep -i lua发现 Nginx 编译了 ngx_http_lua_module,说明支持 Lua 处理请求。
检查 nginx.conf,发现一行关键配置:
lua_package_path "/www/server/nginx/lib/lua/?.lua;;";在 Lua 模块路径中找到一个可疑文件:
ls -la /www/server/nginx/lib/lua/-rw-r--r-- 1 www www 20887 Mar 19 12:10 ngxd.lua文件属主是 www(Web 用户),且被 chattr +i 锁定(不可删除)。
4.3 ngxd.lua 后门分析
这是整个攻击中最精巧的部分。ngxd.lua(20887 字节)是一个完整的 Nginx Lua 后门模块,具备以下功能:
| 功能 | 说明 |
|---|---|
| User-Agent 检测 | 根据 UA 判断设备类型(Android / iPhone / PC)并区分处理 |
| 比例跳转 | Android 10%、iPhone 10%、PC 0% 的概率触发跳转,控制感染面不易被察觉 |
| 远程配置 | 从 pull.969a.xyz 拉取配置,可动态调整跳转比例和跳转域名 |
| RSA 签名命令执行 | 通过 X-Nginx-Authorize 请求头传递命令,RSA 验签后执行任意 shell |
| Bot 伪装 | Google/Bing/Baidu 爬虫不跳转,避免 SEO 处罚 |
| 路径例外 | /admin/、/api/、/login/、/static/ 等路径不跳转 |
| 跳转参数 | 跳转时在 URL 中携带 Base64 编码的 host\machine_id\device_type |
核心跳转逻辑:
-- 伪代码示意
function process_request()
local ua = get_headers("User-Agent")
if ua matches "Android" then
if math.random() < 0.10 then -- 10% 概率
if not has_cookie("k") then
set_cookie("k", "1", 86400)
redirect("https://faradfdsa.anfdnc.xyz/...")
end
end
elseif ua matches "iPhone" then
-- 相同逻辑,10% 概率
end
end4.4 后门加载机制
第一次排查后,恢复 nginx.conf、注释掉 #include luawaf.conf;、删除 waf/ 目录,但没有 reload nginx,所以旧 worker 进程继续运行恶意逻辑。这就是为什么 nginx -T 看到的是干净配置,但实际请求仍然被劫持。
执行 nginx -s reload 后,新 worker 加载干净配置,跳转停止——导致误判为"已经完全修复"。
4.5 真正的持久化机制
清理 ngxd.lua 并 reload nginx 后不久跳转复发。这次检查了 /etc/init.d/nginx 启动脚本,发现被篡改:
# 在 /etc/init.d/nginx 中,攻击者插入了 prepare_nginx_conf() 函数
# 该函数在每次 nginx start/reload 时自动执行
prepare_nginx_conf():
1. 检测 nginx.conf 中是否有 lua_package_path
2. 有 → 将 DIR 设为 /www/server/nginx/lib/lua/ngxd.lua
3. 通过 curl 从 https://pull.969a.xyz/ngxd.lua 下载后门
4. chattr +i 锁定、chown www:www 伪造 Web 上传痕迹
5. 向 VHOST_PATH 写入 _.conf,写入内容:
rewrite_by_lua_block {
local diversion = require "ngxd"
diversion.process_request()
}
6. nginx 加载配置(_.conf 被 include 进来)
7. **立刻删除 _.conf** ← 灭迹关键为什么第一次没发现:
| 原因 | 说明 |
|---|---|
| 注意力集中在 nginx 配置层 | nginx -T、vhost conf、Lua 钩子,没检查 /etc/init.d/ |
_.conf 转瞬即逝 | 只在 nginx 启动时存在几毫秒,加载完立刻被删 |
nginx -T 看不到 | 运行时 _.conf 已不存在,dump 的配置是干净的 |
| 启动脚本不常被审计 | 相比 nginx.conf 和 PHP 文件,init.d 脚本容易被忽视 |
正确做法:找到可疑文件后,立即全系统检索其名称或相关字符串:
grep -rn "ngxd\|pull.969a" /etc/ /root/ /usr/local/ 2>/dev/null4.6 攻击链还原
宝塔面板 RCE(root 权限)
→ 第一阶段:植入 defunct/iptable6 rootkit + 带宽贩卖 Docker 容器
→ 2026-06-13 第一次排查,清除 rootkit
→ 第二阶段:通过 init 脚本注入防删除机制
→ 篡改 /etc/init.d/nginx,插入 prepare_nginx_conf() 函数
→ 上传 ngxd.lua 到模块路径,chown www:www 伪造成 Web 漏洞
→ 第一次排查时,木马恢复 nginx.conf、删除 waf/ 目录
→ 但未执行 nginx reload,旧 worker 继续跳转
→ nginx -s reload 后跳转消失,导致我误以为修复
→ 接着我把宝塔面板自动升级触发 nginx restart
→ init 脚本自动下载 ngxd.lua + _.conf → 跳转复发
→ 第二次排查发现 init 脚本被篡改 → 彻底清除已知恶意域名
| 域名 | 用途 |
|---|---|
212.132.98.170 | 攻击者 SSH 来源 + defunct C2 |
13.57.38.185 | iptable6 C2(AWS EC2) |
faradfdsa.anfXXc.xyz | 跳转中间站 |
dd0a000h4orvt.cloudfXXXt.net | 最终赌博站(CloudFront CDN) |
pull.969a.xyz | ngxd.lua C2 配置拉取 |
五、第二轮清理
5.1 清除 Lua 后门
| 操作 | 状态 |
|---|---|
删除 /www/server/nginx/lib/lua/ngxd.lua | ✅ |
| 同时检查并删除其他路径(btwaf / free_waf) | ✅ |
删除 /tmp/.machine_id / timestamp.txt / ngxd_* | ✅ |
5.2 修复 nginx 启动脚本
# 将 /etc/init.d/nginx 替换为干净的系统版本
# 用 chattr +i 锁定,防止再次被篡改
chattr +i /etc/init.d/nginx5.3 完全重启 nginx
/etc/init.d/nginx stop
sleep 2
/etc/init.d/nginx start注意:必须restart(先 stop 再 start),因为 init 脚本的注入点只在start阶段执行。用reload不会触发 prepare_nginx_conf()。
5.4 验证
# 手机 UA 测试本地
curl -A "Mozilla/5.0 (Linux; Android 13)" http://127.0.0.1
# → 200 OK(无跳转)
# 手机 UA 测试外网
curl -A "Mozilla/5.0 (Linux; Android 13)" http://223.254.130.86
# → 200 OK(无跳转)
# 多轮确认 ngxd.lua 未复现
for i in 1 2 3; do
[ -f /www/server/nginx/lib/lua/ngxd.lua ] && echo "WARNING" || echo "Check $i: clean"
sleep 10
done最终验证结果
| 检查项 | 结果 |
|---|---|
| rootkit 扫描(chkrootkit / unhide) | ✅ 无 rootkit |
| 隐藏进程 | ✅ 无 |
| SSH 密钥 | ✅ 仅授权密钥 |
| 系统 cron | ✅ 无恶意任务 |
| 面板插件 | ✅ 仅标准插件 |
ngxd.lua 30 秒内复现检测(3 轮) | ✅ 未复现 |
| 手机 UA 跳转(3 轮) | ✅ 均返回 200 |
六、其他安全检查
| 检查项 | 结果 |
|---|---|
| SSH 配置 | ✅ 密钥登录,禁用密码 |
| Cron 任务 | ✅ 仅宝塔面板 SSL 续签任务 |
| PHP .user.ini | ✅ 无 auto_prepend_file |
| .htaccess | ✅ 无恶意重写规则 |
| PHP 扩展 | ✅ 无可疑 .so |
| LD_PRELOAD | ✅ 未设置 |
| Systemd 服务 | ✅ 无恶意服务 |
| PHP webshell | ✅ 未发现 |
| SUID 二进制 | ✅ 标准系统文件 |
| Docker 容器 | ✅ 已全部清理 |
| iptables NAT | ✅ 仅 Docker MASQUERADE |
七、投毒入口分析
证据链
| 证据 | 结论 |
|---|---|
ngxd.lua 目录属主 root | www 用户无法写入,文件不可能是 Web 漏洞上传 |
nginx.conf / enable-php-00.conf 均属主 root | 需 root 权限才能修改 |
ngxd.lua 被 chown www:www | 人为伪造成 Web 上传痕迹,误导排查方向 |
| 宝塔面板版本 11.3.0(存在已知 RCE) | 宝塔面板 RCE 是最可能的初始入口 |
结论:宝塔面板 RCE 是最可能的入侵入口
本次感染的攻击面是 Nginx 层面的 Lua 后门 + init 脚本持久化,不涉及 PHP 代码层面。服务端已清理完毕后,本地源码确认干净。
八、经验和教训
- 不能只看运行时配置:
nginx -T看到的配置可能和实际运行的不一致——_.conf只在启动瞬间存在 - 查文件来源比删文件重要:找到文件后应追溯"谁写进来的",否则会复发。
grep -rn "关键词" /etc/ /root/应在第一时间执行 - 持久化机制常藏在启动脚本:
/etc/init.d/、systemd units、cron、rc.local 都是需要重点检查的位置 - chattr +i 要双向使用:不仅要锁定恶意文件,更要锁定关键系统脚本本身
- 进程伪装越来越高级:
exec -a '[kswapd0]'把进程名伪装成内核线程,ps发现不了,必须用lsof查网络连接 - curl 正常 ≠ 网站正常:服务端本地 curl 看到的是原始响应,但浏览器请求可能被 Lua 层的 302 劫持
- 多维度排查顺序:Nginx 配置 → 系统进程 → 网络连接 → 启动脚本 → 登录日志,缺一不可
- 宝塔面板是最薄弱环节:面板以 root 运行、暴露在公网、历史漏洞多,安全问题任重道远。
九、建议措施
立即执行
# 修改所有数据库密码、Redis 密码
# 修改宝塔面板密码和密钥
# 更新宝塔面板至最新版
bt upgrade
# 安装 Fail2Ban 防止爆破
apt install fail2ban -y
systemctl enable fail2ban
systemctl start fail2ban
# 锁定关键配置文件
chattr +i /www/server/nginx/conf/nginx.conf长期建议
- 定期扫描:安装
rkhunter和chkrootkit,每周扫描 - 文件完整性:对
/www/server/nginx/conf/、/usr/bin/等关键目录启用tripwire或AIDE - 日志监控:启用访问日志,监控异常重定向和 SSH 登录
- 最小权限:检查
www用户对系统非必要目录的写权限 - 宝塔面板:限制面板仅内网访问,或绑定域名 + HTTPS 访问
- SSH 安全:确认
authorized_keys已配置,关闭密码登录,修改端口
记录于 2026 年 6 月 13 日
PS:真羡慕韩国球迷啊!祝国足本期世界杯“夺冠”!!!
文章不错非常喜欢,支持一下