服务器 Nginx Lua 流量劫持后门——排查与清除全记录
服务器 Nginx Lua 流量劫持后门——排查与清除全记录
小柯服务器 Nginx Lua 流量劫持后门——排查与清除全记录
背景
某天,访问我的站点 epay.ake.cx 时,偶尔会自动跳转到一个广告页面(ssp5226j.club)。但关闭浏览器再重新打开,又一切正常了。
我自己的手机扫码测试也复现了该问题——微信 / 支付宝 / QQ 扫码后,有概率不进入页面,而是跳转到一个奇怪的新闻广告站。
同一台服务器上的其他站点(如 pan.ake.cx)也存在这个问题。
现象特征总结
| 特征 | 描述 |
|---|---|
| 触发条件 | 仅手机端(Android / iPhone)访问时触发 |
| 触发概率 | 非 100%,有时出现有时不出现 |
| 跳转目标 | ssp5226j.club / ssp5226j.top 等广告站 |
| 二次访问 | 跳过一次后,短时间内不会再跳 |
| PC 端 | 完全不受影响 |
| 服务器内 curl | curl -H "Host: epay.ake.cx" http://127.0.0.1/ 始终返回正常的 301,从不跳转 |
这些特征说明:劫持是有条件触发的,并非简单粗暴地修改了网站代码。
排查过程
第一阶段:排除 PHP 源码层后门
首先怀疑项目源码被植入后门。我对此前已经过三轮安全审计的 Epay 项目代码做了全面排查:
- 全文搜索
ssp5226、cnzz、302、redirect、header.*Location等关键字——零命中。 - 全文搜索
eval、base64_decode、str_rot13、gzinflate等常见后门函数——均为正常业务使用,无可疑调用。 - 检查
.htaccess、index.php等入口文件——无异常。 - 检查数据库
pay_config表中的homepage/homepage_url字段——均为空,未配置强制跳转。
结论:PHP 代码层没有后门。
第二阶段:抓包确认跳转来源
这是整个排查过程中最关键的一步。
在手机上使用抓包工具(ProxyPin)对访问 epay.ake.cx 的全过程进行了 HTTP 抓包。抓包结果清晰地显示了真相:
请求: |
三个关键证据:
- **
Server: nginx**:响应来自我们自己的服务器,而非中间人劫持。 - **状态码
302**:服务器主动发起了重定向。 - **
Set-Cookie: k=261104**:服务器设置了一个名为k的 Cookie,值是一个 6 位数字(261104,实际上是日期2026-11-04)。
这个 k Cookie 在整个 PHP 源码中完全不出现!说明劫持代码在 PHP 之外——在 Nginx 层。
第三阶段:服务器端排查
3.1 本地 curl 验证(为什么 curl 测不出来)
在服务器上执行了 10 次本地 curl:
for i in $(seq 1 10); do |
结果全部返回 HTTP/1.1 301 Moved Permanently(正常的 HTTP→HTTPS 跳转),没有任何一次 302。
这说明两件事:
- 劫持不是每次都触发(解释了”时有时无”)
- 劫持代码会检查 User-Agent,只对手机浏览器下手(curl 的默认 UA 不被匹配)
3.2 检查 Nginx 扩展目录
宝塔面板的站点配置中有一行:
include /www/server/panel/vhost/nginx/extension/epay.ake.cx/*.conf; |
检查该目录——只有一个 site_total.conf,内容是日志配置,无可疑代码。
3.3 检查 PHP 配置
grep "auto_prepend" /www/server/php/*/etc/php.ini |
输出 auto_prepend_file =(空值),没有自动加载任何额外的 PHP 文件。
3.4 检查宝塔面板插件
ls /www/server/panel/plugin/ |
只有 aws_s3、gdrive、jumpserver、linuxsys、supervisor,均为正常的管理用插件。
3.5 检查 Nginx 编译模块
这是转折点:
nginx -V 2>&1 | tr ' ' '\n' | grep -iE 'lua|js|perl|njs' |
输出:
--add-module=/www/server/nginx/src/lua_nginx_module |
Nginx 编译了 Lua 模块! Lua 是一种嵌入式脚本语言,可以在 Nginx 配置中直接执行任意代码,包括读写 Cookie、设置响应头、发起 302 重定向。这正是劫持代码运行的载体。
3.6 搜索 Lua 代码
在所有 Nginx 配置中搜索 Lua 相关指令:
grep -rn "_by_lua\|lua_package\|init_by_lua\|access_by_lua" /www/server/nginx/conf/ |
输出:
/www/server/nginx/conf/luawaf.conf:2:lua_package_path "/www/server/nginx/waf/?.lua"; |
注意到两处引用:
luawaf.conf:引用了/www/server/nginx/waf/目录下的 Lua 文件——但这个目录不存在,且luawaf.conf在nginx.conf中已被注释。proxy.conf:引用了/www/server/nginx/html/waf.lua——这个文件存在!且被nginx.conf的第 27 行include proxy.conf;加载。
第四阶段:逆向分析后门代码
读取 /www/server/nginx/html/waf.lua 的完整内容:
local U = ngx.var.http_user_agent or "" |
后门代码逐段分析
后门代码设计得非常精巧,我将其核心逻辑拆解如下:
┌─────────────────────────────────────────────────────────────┐ |
设计手法分析:
| 手法 | 目的 |
|---|---|
| 伪装成 WAF 文件名 | 管理员看到 waf.lua 不会起疑 |
| 拦截国内安全扫描器 UA | 防止被 360、百度、Fofa 等扫描发现 |
| 只劫持手机端 | PC 端用户(含站长自己)完全无感知 |
k Cookie 每日限流 |
同一用户每天只劫持一次,降低投诉率 |
| iPhone 仅 30% 概率 | iOS 生态投诉成本高,进一步降低暴露 |
| 反转字符串隐藏域名 | 避免直接搜索 ssp5226 找到后门 |
| 随机前缀子域名 | 避免 URL 被浏览器/安全软件拦截 |
chattr +i 不可变锁 |
防止被删除或修改 |
清除过程
遇到了抵抗:chattr 不可变锁
后门作者对关键文件设置了 Linux 的 chattr +i(不可变)属性:
lsattr /www/server/nginx/html/waf.lua |
i 标志意味着即使是 root 用户也无法删除、重命名或修改该文件。sed -i 原地替换也失败(需要创建临时文件,目录也可能是不可变的)。
逐步清除
# 1. 查看文件属性(确认被锁) |
验证清除结果
# 确认全盘无遗漏的 Lua 后门 |
入侵溯源
可能的入侵途径
后门文件 /www/server/nginx/html/waf.lua 和配置 /www/server/nginx/conf/proxy.conf 都被植入了恶意代码并加了 chattr +i 锁。攻击者至少需要 root 权限才能做到这些操作。可能的入侵途径包括:
- 宝塔面板漏洞利用:宝塔面板历史上出现过多次 RCE(远程命令执行)漏洞,如果面板版本过旧或未及时更新,攻击者可能通过面板漏洞获得 root 权限。
- SSH 弱密码爆破:如果 SSH 端口开放且使用弱密码,可能被暴力破解。
- 其他站点漏洞提权:同一服务器上其他站点存在漏洞,被利用后提权至 root。
- 供应链污染:安装的第三方插件、脚本中预植了后门。
自查命令
如果你也担心自己的服务器被类似手法入侵,执行以下命令进行自查:
# 1. 检查 Nginx 编译了哪些模块(重点看 Lua) |
经验总结
为什么后门难以发现?
- 伪装到位:文件名
waf.lua看起来像合法的 WAF(Web 应用防火墙),管理员第一反应不会觉得有问题。 - 隐蔽触发:PC 端从不触发,手机端每天只触发一次,站长很难复现。
- 反检测:拦截了国内主流安全扫描器的 UA(百度、360、fofa、zoomeye 等),防止被安全扫描发现。
- 域名隐藏:使用反转字符串存放目标域名,直接搜索
ssp5226在源代码中找不到。 - 防删除:
chattr +i不可变锁,即使管理员发现,普通rm命令也删不掉。
排查心态
- 如果抓包显示
Server: nginx且 302 来自你的 IP,那问题一定在你的服务器上,而非 DNS 劫持或中间人攻击。 - PHP 源码干净不代表服务器干净。Nginx 层的 Lua 模块可以执行任意代码,完全绕过 PHP。
- 抓包是定位问题的银弹。没有抓包数据,我可能还在 PHP 代码里大海捞针。
时有时无不是随机 bug,是有意设计的触发条件。
防御建议
- 不用的模块不编译:如果不需要 Lua 脚本功能,编译 Nginx 时去掉
lua_nginx_module。 - 关键配置文件加锁:对于生产环境的 Nginx 配置,可以主动
chattr +i,防止被篡改。(我们已经对proxy.conf做了这个操作) - 定期核查配置文件的完整性:将 Nginx 配置纳入版本管理(Git),定期
git diff检查是否有异常变更。 - 更新面板和依赖:如果是宝塔面板用户,保持面板和所有插件为最新版本。
- SSH 安全:禁用密码登录,使用密钥认证;修改默认 SSH 端口;配置 fail2ban。
- 最小化服务器上可写的目录:
/www/server/nginx/下的文件不应被 PHP 等 Web 进程写入。
时间线
| 时间点 | 事件 |
|---|---|
| 发现异常 | 访问 epay.ake.cx 偶尔跳转广告站 |
| 初步排查 | 审查 PHP 源码、数据库配置,未发现后门 |
| 关键突破 | 手机抓包确认服务器返回 302 + Set-Cookie: k=xxx |
| 定位载体 | 发现 Nginx 编译了 lua_nginx_module |
| 找到后门 | 发现 /www/server/nginx/html/waf.lua + proxy.conf 引用 |
| 逆向分析 | 完整还原后门逻辑和触发条件 |
| 清除后门 | 解除 chattr 锁 → 删除文件 → 注释引用 → 重载 Nginx |
| 最终验证 | 全盘扫描确认无残留,手机访问 10+ 次不再跳转 |
免责声明:本文仅用于记录作者个人服务器的安全事件排查过程,供安全研究和技术交流使用。文中提及的域名、IP 等信息不构成对任何第三方的指控或推荐。如果你的服务器也出现类似现象,建议优先寻求专业安全团队的帮助。


