0%

Tomcat Ajp协议漏洞

Tomcat主要是提供Servlet/JSP容器,静态资源的处理速度以及Web服务管理功能方面不如其他专业的HTTP服务器,所以使用效率和性能更高二进制TCP传输协议ajp协议以供其他web服务器进行反向代理或者用于集群,而8009端口的ajp服务默认在公网开启,通过ajp协议可以控制request对象的某些Attribute属性从而造成webapps目录下的任意文件读取/包含

IDEA调试Tomcat

github下载存在漏洞的版本https://github.com/apache/tomcat/releases/tag/9.0.19

解压源码后,在源码根目录新建 home 文件夹,把 conf 文件夹和 webapps 文件夹移动到 home 文件夹里,然后在源码根目录新建 pom.xml 文件(原来为 Ant 工程,这里把它改为 Maven 工程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat</artifactId>
<name>tomcat</name>
<version>9.0.19</version>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>

<!--<dependency>-->
<!--<groupId>javax.xml</groupId>-->
<!--<artifactId>jaxrpc</artifactId>-->
<!--<version>1.1</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jaxrpc_1.1_spec</artifactId>
<version>2.1</version>
</dependency>

<!-- <dependency>-->
<!-- <groupId>org.eclipse.jdt.core.compiler</groupId>-->
<!-- <artifactId>ecj</artifactId>-->
<!-- <version>4.5</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
<version>3.17.0</version>
</dependency>


<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

image-20200326102909478

用IDEA导入

image-20200326103304189

将java文件夹标记为Sources Root,test标记为Test Sources Root

image-20200326103548188

运行org.apache.catalina.startup.Bootstrap#main方法

image-20200326103949800

找不到trailers,把 home/webapps/examples/WEB-INF/classes/trailers 目录拷贝到 test 目录下

image-20200326104556122

再次运行,找不到CookieFilter,把 home/webapps/examples/WEB-INF/classes/util/CookieFilter.java 文件拷贝到 test/util 目录下
image-20200326104920331

conf/server.xml 找不到,设置下 jvm 参数(就是指定之前创建的 home 目录)

-Dcatalina.home=

image-20200326110137180

访问网页报错

image-20200326110306603

删除webapps /examples文件夹

编辑org.apache.catalina.startup.ContextConfig#configureStart方法,添加初始化 JSP 解析器的代码

context.addServletContainerInitializer(new JasperInitializer(), null);

image-20200326110641559

image-20200326110809101

Tomcat Ajp协议

Tomcat默认的conf/server.xml中配置了2个Connector

一个为8080端口的HTTP协议,另外一个就是8009端口的AJP协议

image-20200326114142570

Tomcat最主要的功能是提供Servlet/JSP容器,尽管它也可以作为独立的Java Web服务器,但它在对静态资源(如HTML文件或图像文件)的处理速度,以及提供的Web服务器管理功能方面都不如其他专业的HTTP服务器,如IIS和Apache的服务器。因此在实际应用中,常常把Tomcat的与其他HTTP服务器集成。对于不支持的Servlet/JSP的HTTP服务器,可以通过的Tomcat服务器来运行的Servlet/JSP组件。而Tomcat和其他服务器的集成,就是通过ajp协议来完成的

ajp是一个二进制的TCP传输协议,相比HTTP这种纯文本的协议来说,效率和性能更高,也做了很多优化。显然,浏览器并不能直接支持ajp协议,只支持HTTP协议。所以通常是通过Apache的proxy_ajp模块进行反向代理,暴露成http协议给客户端访问

image-20200326115020723

Debug运行Tomcat,nc 尝试连接可以看到8009端口开放

image-20200326125943121

漏洞分析

文件读取

Tomcat在处理ajp请求的时候调用org.apache.coyote.ajp.AjpProcessor#prepareRequest来解析一些请求头

在该处下断点,并利用poc发送ajp请求

image-20200326150114592

org/apache/coyote/ajp/AjpProcessor.java

image-20200326151300045

当ajp数据包的头设置为SC_REQ_ATTRIBUTE时,Connector会紧接着读取变量n(属性名)和v(值),当n不是SC_A_REQ_LOCAL_ADDRSC_A_REQ_REMOTE_PORTSC_A_SSL_PROTOCOL时,就会用v来赋值属性n

image-20200326151900051

image-20200326151918810

image-20200326151932440

接着,service()方法将修改过的request代入后面的调用

image-20200326151535450

poc请求的url为/asdf

image-20200326152237580

当请求的uri无法匹配其他servlet时会由DefaultServlet处理,其中的调用流程如下

image-20200326152441914

org.apache.catalina.servlets.DefaultServlet中,当我们的请求声明的是GET方法时,存在调用service()->doGet()->serveResource()

org.apache.catalina.servlets.DefaultServlet#serveResource

image-20200326152624852

org.apache.catalina.servlets.DefaultServlet#getRelativePath

image-20200326153216070

serveResource()方法获得path后会将path带入到getResource方法中造成任意文件读取

image-20200326153814678

之后的代码逻辑是把通过path获取的资源序列化输出,因此客户端再按照AJP协议解析数据包就能得到文件内容

文件包含

Tomcat默认将jsp/jspx结尾的请求交给org.apache.jasper.servlet.JspServlet处理

修改一下POC,将请求url改为jsp结尾

image-20200326160730549

WEB-INF下新建一个文件

image-20200326160942966

image-20200326161040592

在org.apache.jasper.servlet.JspServlet#service下断点

image-20200326161648547

这里同样会获取javax.servlet.include.path_info、javax.servlet.include.servlet_path这两个属性(可以通过ajp协议控制这两个属性)

将这两个属性 对应的值拼接到jspURi变量中,最后交给serviceJspFile方法处理

image-20200326161818325

image-20200326161856522

可读路径

文件读取

该漏洞只能读取webapps目录下的文件,不能通过目录穿越读取其他路径

尝试在paylaod中加入../

image-20200326164001646

步入DefaultServlet中调用的getResource

image-20200326164040472

org.apache.catalina.webresources.StandardRoot#getResource

image-20200326164128950

image-20200326164157798

org.apache.catalina.webresources.StandardRoot#validate

image-20200326164647071

org.apache.tomcat.util.http.RequestUtil#normalize(java.lang.String, boolean)

image-20200326165032990

其中检测到了/../就会return null

在org/apache/catalina/webresources/StandardRoot抛出异常

image-20200326165249071

文件包含

先修改一下POC

image-20200326165724644

image-20200326170101382

跟入

org.apache.jasper.servlet.JspServlet#serviceJspFile

image-20200326165837963

image-20200326165918980

跟着jspUri变量,会发现后面就进入了和文件读取一样的检测逻辑

image-20200326170508191

跨webapp读取/包含

目前都是读取默认的ROOT下的文件,如果想跨webapp读取,可以修改POC中的请求url

image-20200326170809752

创建test目录并写入文件

image-20200326171827276

image-20200326171840099

修改POC,url中换成test目录,并且后面跟随随机字符(当请求的uri无法匹配其他servlet时才会由DefaultServlet处理)

image-20200326172233725

image-20200326172326694

修改POC,包含文件并执行

image-20200326172506497

image-20200326172452719

修复方案

  • 升级最新版本

  • 禁用AJP协议

    删除或注释conf/server.xml中的

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

  • 配置secret来设置AJP协议的认证凭证

    <Connector port="8009"protocol="AJP/1.3" redirectPort="8443"address="YOUR_TOMCAT_IP_ADDRESS" secret="YOUR_TOMCAT_AJP_SECRET"/>

参考

CNVD-2020-10487-Tomcat-Ajp-lfi

IDEA 导入 Tomcat9 源码

Tomcat-Ajp漏洞:我是如何一步步写出POC的?

The Apache Tomcat Connectors - AJP Protocol Reference

Tomcat HTTP协议与AJP协议

Tomcat Ajp协议文件包含漏洞分析

Tomcat-Ajp协议漏洞分析(CVE-2020-1938)