Apache Tomcat从文件包含到RCE漏洞原理深入分析
字数 2072 2025-08-18 11:39:30
Apache Tomcat AJP协议文件包含与RCE漏洞深入分析教学文档
漏洞概述
CVE编号:CVE-2020-1938
漏洞名称:Apache Tomcat AJP协议文件包含漏洞
影响版本:多个Tomcat版本(测试验证包括8.0.50等版本)
漏洞类型:文件包含→远程代码执行(RCE)
危害等级:高危
默认影响:Tomcat默认配置中监听8009端口(AJP协议)
漏洞背景
AJP协议简介
AJP(Apache JServ Protocol)是一个二进制的TCP传输协议,特点:
- 专为Tomcat与前端Web服务器(如Apache HTTP Server)通信设计
- 相比HTTP纯文本协议,AJP具有更高效率和性能
- 默认监听8009端口
- 浏览器不支持直接发送AJP请求
Tomcat连接器架构
Tomcat默认配置两个连接器(Connector):
-
HTTP Connector:
- 默认监听8080端口
- 处理所有HTTP请求(静态和动态资源)
-
AJP Connector:
- 默认监听8009端口
- 专用于与前端Web服务器通信
- 使用率约7.8%(相对较低)
漏洞原理分析
漏洞核心
攻击者可通过AJP协议控制以下三个关键参数:
javax.servlet.include.request_urijavax.servlet.include.path_infojavax.servlet.include.servlet_path
通过操纵这些参数实现:
- 任意文件读取(文件包含)
- 远程代码执行(RCE)
漏洞位置
在Tomcat 8.0.50版本中,漏洞存在于:
org.apache.coyote.ajp.AbstractAjpProcessor.prepareRequest()方法
文件包含漏洞分析流程
-
请求处理流程:
- AJP请求→SocketProcessor→AbstractAjpProcessor.prepareRequest()
- 攻击者控制的三个参数被存入Request对象的attributes(HashMap)
-
Servlet路由:
- 当请求URI无法匹配任何Servlet时,由DefaultServlet处理
- DefaultServlet主要用于处理静态资源(HTML/图片/CSS/JS等)
-
路径拼接关键点:
DefaultServlet.getRelativePath()进行路径拼接- 使用攻击者控制的
path_info和servlet_path参数 - 如果缺少
request_uri参数会导致利用失败
-
路径规范化限制:
RequestUtil.normalize()防止目录遍历:- 不允许以"/../"开头的路径
- 不允许连续出现两个"/../"
- 结果:只能读取webapps目录内文件,无法读取上层目录
RCE漏洞分析流程
-
前提条件:
- 需要先上传含有恶意代码的文件到webapps目录
- 文件扩展名不限(甚至无扩展名)
-
利用JspServlet:
- 默认处理.jsp和.jspx请求
- 功能:将JSP文件生成Servlet类→编译→执行
-
攻击流程:
- AJP请求URI必须以".jsp"结尾(文件可不存在)
path_info参数指向上传的恶意文件JspServlet.service()处理流程:- 拼接
servlet_path和path_info - 生成
JspServletWrapper - 解析并执行恶意文件中的代码
- 拼接
环境搭建与复现
环境准备
-
下载Tomcat 8.0.50:
- 源码:http://archive.apache.org/dist/tomcat/tomcat-8/v8.0.50/
- 二进制发行版
-
配置pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>Tomcat8.0</artifactId>
<version>8.0</version>
<build>
<finalName>Tomcat8.0</finalName>
<sourceDirectory>java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- 添加必要依赖 -->
</dependencies>
</project>
- IDEA配置:
- Main class:
org.apache.catalina.startup.Bootstrap - VM options:
-Dcatalina.home="apache-tomcat-8.5.34"
- Main class:
漏洞复现
-
任意文件读取:
- 构造AJP请求包,控制三个关键参数
- 示例读取WEB-INF/web.xml:
URI: /asdf javax.servlet.include.request_uri: / javax.servlet.include.path_info: WEB-INF/web.xml javax.servlet.include.servlet_path: /
-
RCE复现:
- 先上传含恶意JSP代码的文件
- 构造AJP请求:
URI: /anyname.jsp javax.servlet.include.request_uri: / javax.servlet.include.path_info: /upload/malicious.file javax.servlet.include.servlet_path: /
防御措施
-
临时缓解:
- 注释掉
conf/server.xml中的AJP Connector配置:<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
- 注释掉
-
长期方案:
- 升级到已修复版本
- 网络层面限制8009端口的访问
-
架构建议:
- 避免Tomcat直接暴露在公网
- 使用Apache/Nginx作为前端代理
技术总结
- 漏洞本质:AJP协议实现缺陷导致参数可控
- 利用条件:开启AJP服务(默认8009端口)
- 影响范围:所有使用默认配置的受影响Tomcat版本
- 利用限制:
- 文件读取限于webapps目录内
- RCE需要先上传恶意文件
附录:关键调用链
文件包含调用链
SocketProcessor→AbstractAjpProcessor.prepareRequest()→
Request.setAttribute()→DefaultServlet.doGet()→
serveResource()→getRelativePath()→
StandardRoot.getResource()→validate()
RCE调用链
JspServlet.service()→serviceJspFile()→
JspServletWrapper.service()→Servlet.service()
注:本文基于Apache Tomcat 8.0.50版本分析,其他版本细节可能略有不同。