前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Apache Tomcat AJP协议漏洞分析(CVE-2020-1938)

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

原创
作者头像
半月弧
修改2020-05-07 18:04:53
3.4K0
修改2020-05-07 18:04:53
举报
文章被收录于专栏:半月弧のhome半月弧のhome

本篇文章主要针对于Apache Tomcat Ajp(CVE-2020-1938)漏洞进行源码分析和漏洞利用,顺便通过这个漏洞来学习JAVA代码审计。

AJP13协议介绍

AJP的全程是Apache JServ Protocol,支持AJP协议的Web容器包括Apache Tomcat,JBoss AS / WildFly和GlassFish。Tomcat一般性的作用是作为serverlet容器来加载动态资源, 它也可以作为类似于apache、nginx、IIS等web容器来处理静态资源的请求。

在Tomcat $CATALINA_BASE/conf/server.xml默认配置了两个Connector,分别监听两个不同的端口,一个是HTTP Connector 默认监听8080端口,一个是AJP Connector 默认监听8009端口。

8080端口配置
8080端口配置
8009端口配置
8009端口配置

HTTP Connector通信对象为普通用户,它接收来自用户的静态或动态请求,相当于是一个可以处理servlet和jsp的web容器,但是性能上是远远不能满足业务需求的。

AJP Connector通信对象为web服务器, 在web架构中考虑到性能等要素, 通常的做法是把动静态分离, 把静态资源请求给web服务器去做, servlet和jsp请求给tomcat来处理。当用户请求进来的时候首先遇到的是web服务器, web服务器判断请求的类型如果是servlet或jsp则通过AJP Connector来传递给Tomcat,这里web服务器和Tomcat之间的通信协议就叫做AJP协议。

漏洞环境搭建

操作系统:windows10

Tomcat: apache-tomcat-8.5.47-src

1. 将源代码导入至IDEA中方便调试,因为tomcat源代码是用ant编译打包的,如果我们想要使用mavend hua, 需要增加一个文件pom.xml

代码语言:javascript
复制
<?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>Tomcat8.0</artifactId>
 <name>Tomcat8.0</name>
 <version>8.0</version>

 <build>
  <finalName>Tomcat8.0</finalName>
  <sourceDirectory>java</sourceDirectory>
  <resources>
   <resource>
    <directory>java</directory>
   </resource>
  </resources>
  <testResources>
   <testResource>
    <directory>test</directory>
   </testResource>
  </testResources>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
     <encoding>UTF-8</encoding>
     <source>1.8</source>
     <target>1.8</target>
    </configuration>
   </plugin>
  </plugins>
 </build>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.easymock</groupId>
   <artifactId>easymock</artifactId>
   <version>3.4</version>
  </dependency>
  <dependency>
   <groupId>ant</groupId>
   <artifactId>ant</artifactId>
   <version>1.6.5</version>
  </dependency>
  <dependency>
   <groupId>wsdl4j</groupId>
   <artifactId>wsdl4j</artifactId>
   <version>1.6.2</version>
  </dependency>
  <dependency>
   <groupId>javax.xml</groupId>
   <artifactId>jaxrpc-api</artifactId>
   <version>1.1</version>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jdt.core.compiler</groupId>
   <artifactId>ecj</artifactId>
   <version>4.4.2</version>
  </dependency>
 </dependencies>
</project>

2. 然后增加一个Application配置

Main Class: org.apache.catalina.startup.Bootstrap

VM options: -Dfile.encoding=UTF-8 -Dcatalina.home="D:\SourceCode\tomcat-study\apache-tomcat-8.5.47-src\catalina-home"

JRE: jdk1.8

3. 启动tomcat

日志中我们可以看到8080和8009这2个Connector被打开了

访问http://127.0.0.1:8080

漏洞分析

1. 此处用debug模式打开tomcat

根据网上大部分的文章所提到的那样,我们先找到org.apache.coyote.ajp.AjpProcessor这个类,通过IDEA中自带的find in path功能来找到AjpProcesser

通过源码分析AjpProcessor中的prepareRequest函数将ajp里面的内容取出来设置成request对象的Attribute属性,接下来我们找到以下这段代码,这段代码的意思是解码额外的属性,从其他人的分析文章中我们能知道注入点是存在于下面这个3个Attribute.

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path

prepareRequest函数会处理这3个属性,在这里我们可以手动的控制这3个属性的值。

2. 这里我们给request.setAttribute(n, v );下断点, 然后启动POC脚本:python2 .\cve-2020-1938.py 127.0.0.1 -p 8009 -f WEB-INF/web.xml

Sep Over至request.setAttribute(n, v );处

Sep Over至request.setAttribute(n, v );处

在这里我们将3个值传入HashMap,这3个值就是我们通过AJP协议传入的值。我们可以通过wireshark抓包来查看AJP协议传入的参数。

我们把精心制作的AJP13协议请求组装好发送给tomcat后,Tomcat会把该请求交给servlet来处理,在Tomcat $CATALINA_BASE/conf/web.xml这个配置文件中默认定义了两个Servlet,一个是DefaultServlet,另一个JSPServlet。

DefaultServlet
DefaultServlet
JspServlet
JspServlet

在这里有一个很重要的参数刚才没提到,就是URI, 如果AJP13请求中指定的URI地址可以被找到的话,请求就走JspServlet,否则找不到的话, 就走DefaultServlet. 在模拟请求中,我们给的URI地址是一个随机地址,肯定无法被找到,所以当前请求走的是DefaultServlet路径。

3. 在这里我们下断点给java.org.apache.catalina.servlets.DefaultServlet.java文件中的doGet方法,因为协议走的Get请求。

下断点至serveResource处,且再次发送AJP请求。

找到传入的3个参数

4. Setp Info至getRelativePath, 分析下面这段代码。

如果这个参数RequestDispatcher.INCLUDE_REQUEST_URI非空的话,则

查看RequestDispatcher.INCLUDE_REQUEST_URI = javax.servlet.include_uri

所以servletPath和pathInfo就被赋予外置attribute,pathInfo = javax.servlet.include.path_info && servletPath = javax.servlet.include.servlet_path.

我们在POC代码中定义的三个属性达到了WEB目录下任意文件读取的作用

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path。

5. 完成pathInfo和servletPath值的获取后,将进行路径的拼接, 拼接的结果仍然是“/WEB-INF/web.xml”

接着step info, 跳回至serveResource方法,这里debug = 0所以跳过

继续单步调试, 这里的代码将获取资源文件

查看getResource代码, 发现validate函数处理了传进来的path, 这里不跟进到validate函数内部了,先跳过。

接着我们跳回至getResource函数,得到处理过的path = /WEB-INF/web.xml.

getResource函数结束, 得到最后返回的文件资源,可以看到我们获取到了/WEB-INF/web.xml这个本不应该得到的文件地址。

总结

这个漏洞的成因是因为AJP协议的核心参数可以被恶意修改,攻击者利用漏洞构造特定参数,读取服务器webapp/ROOT下的任意文件。

引用

1. https://paper.seebug.org/1142/

2. https://www.freebuf.com/column/234123.html

3. https://www.freebuf.com/column/227973.html

4. https://www.freebuf.com/news/232748.html

5. poc代码:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi/blob/master/CNVD-2020-10487-Tomcat-Ajp-lfi.py

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AJP13协议介绍
  • 漏洞环境搭建
  • 漏洞分析
  • 总结
  • 引用
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档