Tomcat AJP协议漏洞CVE-2020-1938:原理、复现与安全加固

发布时间:2026/7/5 0:59:52
Tomcat AJP协议漏洞CVE-2020-1938:原理、复现与安全加固 1. 项目概述从一次内部渗透测试说起前段时间公司内部做了一次红蓝对抗演练目标是测试我们自研的几个Web应用的安全性。在信息搜集阶段我习惯性地用Nmap扫了一下目标服务器的端口发现除了常见的80、443还有一个8009端口处于开放状态后面跟着一个ajp的服务标识。这个发现让我心里“咯噔”了一下因为ajpApache JServ Protocol协议通常与Tomcat的AJP连接器相关而它历史上出过一些严重的问题。我立刻想到了那个著名的CVE-2020-1938也就是CNVD-2020-10487一个影响范围极广的Tomcat AJP协议文件包含/读取漏洞。为了更深入地理解这个漏洞的成因、利用条件以及修复方案我决定在本地环境完整地复现一遍这个漏洞。这不仅是为了验证其危害性更是为了在未来的安全评估和代码审计中能更精准地识别和防御此类风险。如果你负责的Java Web应用部署在Tomcat上并且配置了AJP连接器那么这篇文章将带你从攻击者的视角一步步拆解这个漏洞理解其背后的协议逻辑和配置缺陷最终掌握加固的方法。2. 漏洞核心原理与AJP协议浅析2.1 AJP协议Tomcat的“后门”通道在深入漏洞之前我们必须先理解AJP是什么。很多人把Tomcat和HTTP直接划等号但实际上Tomcat作为一个Servlet容器支持多种协议与外部通信。HTTP/1.1 Connector是面向最终用户的而AJPApache JServ ProtocolConnector更像是一个“内部通道”。它通常用于Tomcat与前端的Web服务器如Apache HTTPD、Nginx通过模块进行集成将动态请求通过AJP协议转发给后端的Tomcat处理静态资源则由前端服务器直接响应。这种架构能提升整体性能。AJP是一个二进制协议相比文本协议的HTTP它更紧凑、高效。通信默认在8009端口。关键点在于AJP协议设计之初就假设连接的两端前端服务器和Tomcat是可信的通常处于同一个受保护的内部网络。因此AJP协议本身没有像HTTP那样内置丰富的安全控制机制它信任前端服务器发送过来的请求信息。2.2 CVE-2020-1938漏洞根源信任的滥用漏洞的根源就来自于这种“信任”。在AJP协议的数据包中有一系列属性request_attributes用于传递额外的请求信息。其中有两个至关重要的属性javax.servlet.include.request_urijavax.servlet.include.path_infojavax.servlet.include.servlet_path这些属性的本意是用于请求派发Request Dispatch比如在Servlet中使用request.getRequestDispatcher(“/some/path”).include(request, response)时Tomcat内部会设置这些属性以便包含include另一个资源的内容。这个过程是服务器内部行为路径应该是受控的。然而通过构造恶意的AJP请求攻击者可以直接从客户端伪装成可信的前端服务器向Tomcat的AJP端口发送数据包并手动设置这些属性。当Tomcat接收到这些属性时由于缺乏严格的校验它会错误地认为这是一个合法的内部请求派发从而根据攻击者指定的路径去读取文件。更致命的是通过精心构造javax.servlet.include.path_info属性攻击者可以利用目录遍历../跳出Web应用根目录WEB-INF或应用目录读取服务器上的任意文件例如WEB-INF/web.xml泄露数据库密码等配置、/etc/passwd等系统敏感文件。在某些特定配置下甚至能配合文件上传等功能实现远程代码执行RCE。注意此漏洞的利用前提是Tomcat开启了AJP Connector默认配置下是开启的并且AJP服务端口默认8009暴露给了不可信的网络。在云原生和容器化环境下如果错误地将AJP端口映射到公网风险极高。3. 漏洞复现环境搭建与配置3.1 靶机环境准备为了安全地复现我们需要在隔离的环境如虚拟机或Docker中搭建一个存在漏洞的Tomcat。1. 选择有漏洞的Tomcat版本该漏洞影响以下版本Apache Tomcat 9.x 9.0.31Apache Tomcat 8.x 8.5.51Apache Tomcat 7.x 7.0.100Apache Tomcat 6.x这里我选择Apache Tomcat 8.5.32作为复现目标因为它是一个广泛使用的、且受漏洞影响的版本。2. 使用Docker快速搭建推荐这是最干净、便捷的方式。我们可以从Docker Hub拉取特定版本的Tomcat镜像并确保其AJP端口暴露。# 拉取Tomcat 8.5.32 官方镜像 docker pull tomcat:8.5.32-jre8 # 运行容器将Tomcat的HTTP端口8080和AJP端口8009都映射到宿主机 docker run -d -p 8080:8080 -p 8009:8009 --name vulnerable-tomcat tomcat:8.5.32-jre8执行后访问http://你的宿主机IP:8080应该能看到Tomcat的默认主页。3. 验证AJP服务使用netstat或nmap在宿主机上检查端口。# 在宿主机上执行 nmap -sV -p 8009 127.0.0.1如果看到8009端口开放且服务识别为ajp说明环境准备就绪。3.2 攻击机环境与工具准备我们需要一个能发送自定义AJP协议包的工具。最常用的是python-ajpy库但这里我推荐一个更易用、功能更集中的开源工具tomcat-ajp-poc。这个工具专为利用CVE-2020-1938而编写。1. 下载利用工具git clone https://github.com/YDHCUI/tomcat-ajp-poc.git cd tomcat-ajp-poc2. 安装Python依赖该工具需要Python3。pip3 install -r requirements.txt主要依赖是用于处理AJP协议数据包序列化的库。3. 工具结构预览ajp_shooter.py: 核心利用脚本可以发起文件读取请求。ajp_listener.py: 一个用于测试的AJP监听器本次复现不主要使用。4. 漏洞利用实操步步为营读取敏感文件现在我们进入最关键的实操环节。假设我们的靶机IP是192.168.1.100。4.1 基础利用读取WEB-INF/web.xmlWEB-INF/web.xml是Java Web应用的核心配置文件里面常包含数据库连接池配置、Servlet映射、安全约束等敏感信息。它是攻击者首要目标。使用ajp_shooter.py进行读取python3 ajp_shooter.py http://192.168.1.100:8080/ 8009 /WEB-INF/web.xml命令参数拆解http://192.168.1.100:8080/: 这是Tomcat的HTTP地址。工具需要这个地址来获取一些上下文信息如服务器名称、端口虽然AJP是独立端口。8009: AJP服务端口。/WEB-INF/web.xml: 指定要读取的文件路径。注意这里直接使用了Web应用根目录下的路径。执行结果分析如果漏洞存在且路径正确终端会直接打印出web.xml文件的内容。你可能会看到resource-ref标签内的数据库JDBC URL、用户名和密码可能是明文或加密过的。这已经构成了一次严重的信息泄露事件。4.2 目录遍历读取服务器任意文件基础利用只能读取Web应用目录内的文件。真正的威力在于目录遍历。AJP属性对路径的校验不严允许使用../。尝试读取系统文件python3 ajp_shooter.py http://192.168.1.100:8080/ 8009 /WEB-INF/../../../../etc/passwd路径构造解析我们从/WEB-INFWeb应用内的受保护目录出发通过多次../向上回退目录最终定位到Linux系统的/etc/passwd文件。Tomcat在处理javax.servlet.include.path_info时没有有效地规范化normalize和限制这个路径导致了穿越。执行成功后你将看到系统的/etc/passwd文件内容被打印出来。这证明了攻击者可以读取服务器操作系统上的任意文件只要Tomcat进程有权限读取通常Tomcat以非root用户运行但仍可读取大量敏感文件。其他敏感文件示例/proc/self/environ: 获取Tomcat进程的环境变量可能包含密钥。../../myapp/WEB-INF/classes/config.properties: 读取同一服务器上其他Web应用的配置文件。Windows系统下的C:\windows\system32\drivers\etc\hosts。实操心得路径的“起点”玄机在实际测试中path_info的解析“起点”有时会因Tomcat版本和具体请求上下文略有差异。除了从/WEB-INF开始遍历也可以尝试从/、/static/等已知或可能存在的目录开始构造。多尝试几种../的组合是必要的。工具可能内置了一些常见Payload但手动理解和构造能帮你应对更复杂的环境。4.3 利用场景扩展从文件包含到代码执行单纯的文件读取危害巨大但攻击者往往追求远程代码执行RCE。这需要结合其他条件配合文件上传功能如果目标应用存在任意文件上传漏洞且上传路径可知。攻击者可以先上传一个包含恶意JSP代码的文件如shell.jsp到某个可访问目录如/upload/。然后利用AJP文件包含漏洞通过/WEB-INF/../../upload/shell.jsp这样的路径去“包含”这个JSP文件。Tomcat在处理.jsp文件时会将其编译并执行其中的Java代码从而实现RCE。写入特定目录在某些老旧或不安全配置下如果攻击者能通过其他方式如SSRF、框架漏洞向Tomcat的webapps目录或work/Catalina临时目录写入JSP文件同样可以借助此漏洞触发执行。模拟利用流程概念性1. 发现目标存在AJP漏洞端口开放可读取文件。 2. 寻找目标应用的上传点上传 shell.jsp (内容为 % Runtime.getRuntime().exec(request.getParameter(cmd)); %)。 3. 确认上传后的文件路径例如 /uploads/shell.jsp。 4. 使用AJP漏洞包含该JSP文件python3 ajp_shooter.py ... /WEB-INF/../../../uploads/shell.jsp?cmdid 5. 如果包含成功Tomcat会执行id命令攻击者可通过其他方式如反弹shell获取输出。重要警告此部分仅为原理说明旨在强调漏洞组合利用的严重性。在实际安全测试中必须获得明确的书面授权方可进行任何可能影响系统可用性或数据完整性的操作。5. 漏洞修复与安全加固方案复现漏洞是为了更好地防御。针对CVE-2020-1938有以下几种根本性的解决方案。5.1 官方补丁升级首选最直接的方法是升级Tomcat到已修复的版本Tomcat 9.x 升级至 9.0.31 或更高Tomcat 8.5.x 升级至 8.5.51 或更高Tomcat 7.x 升级至 7.0.100 或更高升级后Tomcat会对AJP请求中的javax.servlet.include.*等属性进行严格的校验禁止从客户端设置这些用于请求派发的属性。5.2 禁用或保护AJP Connector治本如果业务上不需要使用AJP协议例如Tomcat独立运行前面没有Apache/Nginx做反向代理最彻底的安全措施是直接禁用AJP Connector。修改conf/server.xml找到如下配置段!-- 默认的AJP 1.3 Connector配置 -- Connector port8009 protocolAJP/1.3 redirectPort8443 /有两种处理方式直接删除或注释掉这整行Connector ... /配置。修改其监听地址仅允许本地或可信网络访问Connector port8009 protocolAJP/1.3 redirectPort8443 address127.0.0.1 /或Connector port8009 protocolAJP/1.3 redirectPort8443 address192.168.1.0 /修改后重启Tomcat生效。5.3 配置AJP认证与秘密缓解措施如果必须使用AJP例如与Apache HTTPD集成务必启用AJP连接器的secret认证。在conf/server.xml中配置Connector port8009 protocolAJP/1.3 redirectPort8443 address127.0.0.1 secretYourStrongAJPSecret123! /同时在前端服务器如Apache的AJP代理配置中例如mod_proxy_ajp也需要设置相同的秘密ProxyPass / ajp://localhost:8009/ secretYourStrongAJPSecret123!这样只有知道秘密的前端服务器才能与Tomcat建立AJP连接可以有效防止来自网络其他位置的直接AJP攻击。5.4 网络层访问控制在防火墙或安全组策略中严格限制对Tomcat服务器8009端口的访问。只允许前端代理服务器如Apache/Nginx所在主机的IP地址访问该端口禁止任何其他来源的访问。这是纵深防御中关键的一环。6. 排查技巧与深度防御建议在实际运维中如何发现和防御此类风险6.1 自查清单端口扫描自查定期从外部网络和内部不同网段扫描你的服务器检查8009端口是否意外暴露。命令如nmap -p 8009 你的服务器IP。配置审计检查Tomcat的server.xml确认AJP Connector的配置。关注port、address和secret属性。版本确认运行{TOMCAT_HOME}/bin/version.sh(Linux) 或version.bat(Windows)确认Tomcat版本是否在受影响范围。6.2 深度防御建议最小权限原则运行Tomcat的进程用户如tomcat用户应仅拥有必要的文件系统读/写权限避免使用root用户。这可以限制即使文件被读取危害范围也有限。独立部署为每个Web应用使用独立的Tomcat实例或至少是独立的环境变量、工作目录实现应用间隔离。安全基线遵循Apache Tomcat官方安全文档和业界安全基线如CIS Benchmarks for Tomcat进行配置。WAF/IPS防护在网络边界部署Web应用防火墙WAF或入侵防御系统IPS可以配置规则拦截对AJP端口8009的异常请求或包含特定攻击特征的流量。6.3 常见问题排查实录Q1: 我用了工具但返回“500 Internal Server Error”或读取不到文件A1: 可能有几种原因 *版本不对目标Tomcat版本可能已升级修复。 *路径不对Web应用的上下文路径Context Path可能不是根路径/。例如如果应用部署在http://host:8080/myapp你需要尝试在路径中包含/myapp如/myapp/WEB-INF/web.xml。或者应用本身没有WEB-INF目录。 *权限不足Tomcat进程用户对目标文件没有读取权限。 *网络问题防火墙阻止了AJP端口通信。Q2: 我的服务器在云上只开了80/443端口还有风险吗A2: 如果AJP端口8009没有在安全组或云防火墙中开放给公网那么来自互联网的直接攻击风险较低。但需警惕内部横向移动。如果攻击者通过其他漏洞如Web应用漏洞获取了服务器内网访问权限他们依然可以尝试攻击本地的8009端口。Q3: 除了文件读取这个漏洞还能用来做什么A3: 核心是文件包含。在特定条件下如果服务器配置了autoDeploy且允许部署WAR包理论上可能通过构造极其复杂的请求进行应用部署但难度极大非主流利用方式。主要危害还是敏感信息泄露为后续攻击提供“弹药”。这次完整的复现和分析让我对AJP协议的安全模型有了刻骨铭心的认识。安全往往崩溃在信任边界最模糊的地方。对于运维和开发同学定期审查网络暴露面、关闭不必要的服务、及时更新组件永远是成本最低且最有效的安全措施。在微服务和云原生架构下每个容器或Pod的端口暴露都需要格外审慎像AJP这种“内部协议”的端口绝不应该出现在服务对外暴露的清单里。