F4DE F4DE
首页
技术
笔记
随笔
关于
友链
归档

F4DE

Web Security
首页
技术
笔记
随笔
关于
友链
归档
  • Java安全

    • Tomcat

      • Tomcat AJP协议漏洞
        • 简介
        • Tomcat架构简单分析
        • AJP数据包处理
        • 漏洞复现
          • 环境搭建
          • 文件读取
        • 其他
          • 读取其他Context
          • 目录穿越可读性
        • 修复
        • 参考
      • 基于Tomcat Response对象获取回显
      • 通过动态注册filter实现Tomcat内存🐎
      • 基于Tomcat全局存储进行回显
    • Shiro

    • 字节码

    • 反序列化

  • 技术
  • Java安全
  • Tomcat
F4DE
2021-03-16

Tomcat AJP协议漏洞

CVE-2020-1938复现,漏洞利用难度不大,但是还是学到了很多新的东西。

# 简介

AJP协议是一种定向包协议,采用二进制形式代替文本形式,主要是为了提高性能和添加SSL的支持,在tomcat中使用的是AJP1.3,简称为ajp13。

在tomcat的配置文件server.xml中配置了两种连接方式:

image-20210315141921787

image-20210315141933682

一个是默认开放在8080端口的HTTP Connector,另一个则是默认开放在8009端口的AJP Connertor,而在tomcat中这个协议的监听一直是默认开启的。

tomcat可以通过AJP协议和另一个web容器进行交互通信,比如Apache。tomcat可以作为Servlet或者JSP的容器,但是在处理一些静态资源的速度却不如其他的HTTP服务器(如IIS、Apache)。所以实际应用中常常把tomcat和其他服务器进行集成,对于不支持Servlet、JSP的服务器,可以通过AJP协议把请求转发给tomcat,从而实现优势互补。

正常情况下用户使用客户端通过HTTP协议来和tomcat服务器进行通信,但是也可以通过AJP协议来和tomcat通信,这时候就会由AJP Connertor来处理AJP请求。显然浏览器不直接支持AJP协议,所以要想使用AJP协议,要么通过中间代理服务器进行转发,要么自己根据AJP协议的格式来实现一个客户端。

解析Tomcat HTTP协议与AJP协议解析Tomcat HTTP协议与AJP协议

但是Tomcat AJP协议存在安全漏洞(CVE-2020-1938),攻击者可以通过构造AJP协议请求中的特定属性值,来进行任意文件读取(一般为webapp/ROOT路径下),同时如果AJP服务器存在文件上传接口,则还可能通过漏洞进行文件包含,实现远程代码执行。

漏洞影响范围:

  • Tomcat 6.x
  • Tomcat 7.x < 7.0.100
  • Tomcat 8.x < 8.5.51
  • Tomcat 9.x < 9.0.31

# Tomcat架构简单分析

Tomcat架构分析:https://blog.csdn.net/xlgen157387/article/details/79006434

Tomcat Service主要包含两个部分:Conector和Container。

  • Connector:主要用于处理连接,并提供socket和request及response直接的转化。
  • Container:用于封装和管理Servlet,处理具体的请求。

在Container中有四个子容器:

  • Engine:引擎,用来管理多个站点,一个service最多只能有一个Engine。
  • Host:代表一个站点,也叫虚拟主机,通过配置Host可以添加站点。
  • Context:代表一个应用程序,对应一套程序,或者一个WEB-INF目录以及目录中的web.xml文件。
  • Wrapper:每一个Wrapper封装着一个Servlet。

以下面的tomcat文件目录做对照:

image-20210315160205337

Webapps目录代表一个Host,而其下的每个目录都对应着一个Context,其中ROOT目录存放着主应用,其他目录存放着子应用。当我们访问Context的时候,如果是ROOT目录下的,则可以直接通过www.xxx.com/来访问,而如果要访问其他子目录,则需要通过www.xxx.com/docs来访问。当然,主应用目录是可以修改的,默认情况下为ROOT目录。

tomcat

Tomcat处理一个请求的流程大致如下:

image-20201013113437489

  1. 在获取到TCP/IP数据包的时候,会交给Processor将解析,并封装成我们熟悉的request和response对象,然后传递给下一步处理。
  2. Engine来进行Host、Context、Mapping中Servlet的匹配。
  3. Servlet中调用方法(service、doGet、doPost······)来进行处理,并返回结果。

# AJP数据包处理

对于HTTP请求和AJP请求的数据包,在封装成request和response对象之后的流程并无差别,主要区别就是对socket流量处理和Processor解析的过程不同。

org.apache.coyote.Processor接口提供了这些功能,对于不同的协议,有不同的接口实现类。负责处理AJP请求的实现类为AjpProcessor:

image-20210315161900016

在AjpProcessor的service()方法中调用了prepareRequest()方法进行数据预处理:

image-20210315162453835

而之后会调用Adapter来将请求交给Container来处理:

image-20210315162639014

AJP协议中的漏洞,就出现在prepareRequest()方法中。

# 漏洞复现

poc:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi

# 环境搭建

在环境搭建这里我还是踩了一点小坑,先说一些我本地的环境:

  • Windows10 专业版
  • JDK 8u281
  • IDEA专业版 2020.3.2

以下是我搭建出正常复现环境的步骤:

  1. github上下载存在漏洞的Tomcat版本:https://github.com/apache/tomcat/archive/9.0.19.zip

  2. 解压,在文件目录中添加pom.xml,改为maven方式构建项目,其中pom.xml文件内容如下:

    <?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>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-jaxrpc_1.1_spec</artifactId>
                <version>2.1</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>
    
  3. 在根目录里新建home文件夹,把webapps和conf文件夹放入home文件夹中:

    image-20210316001203592

  4. 在IDEA中导入项目,选择我们新建的pom.xml文件:

    image-20210316001439225

image-20210316001506159

  1. 导入项目之后,将java文件夹标记为Sources Root,把test文件夹标记为Test Sources Root:

  2. 把 home/webapps/examples/WEB-INF/classes/trailers 目录拷贝到 test 目录下:

    image-20210316001838650

  3. 把 home/webapps/examples/WEB-INF/classes/util/CookieFilter.java 文件拷贝到 test/util 目录下:

    image-20210316001919101

  4. 找到 BootStrap类的main方法,运行,这时候应该会出现下面错误:

    image-20210316002035809

  5. 添加VM参数:-Dcatalina.home={你的项目路径}\home:

    image-20210316002211736

  6. 在org.apache.catalina.startup.ContextConfig类中的configureStart方法中添加context.addServletContainerInitializer(new JasperInitializer(), null);,用于初始化JSP解析器:

    image-20210316002433890

  7. 删除 home/webapps/examples 文件夹,然后运行 main 方法,访问127.0.0.1:8080:

    image-20210316002617780

  8. 同时可以看到8009端口已经开放:

image-20210315144452701

# 文件读取

在AjpProcessor#prepareRequest方法打上断点,调试启动tomcat,并使用POC来发送ajp请求:

image-20210315163241922

在699行出进行了数据包头部信息的读取,当头设置为SC_A_REQ_ATTRIBUTE的时候,则会读取出n和v:

image-20210315163647369

然后当n不为SC_A_REQ_LOCAL_ADDR、SC_A_REQ_REMOTE_PORT、SC_A_SSL_PROTOCOL的时候,会用v来对n进行赋值:

image-20210315163728511

POC中发送的数据包一共会读取三次:

n = javax.servlet.include.request_uri	v = /

n = javax.servlet.include.path_info		v = WEB-INF/web.xml

n = javax.servlet.include.servlet_path	v = /

对应POC中的这一部分代码:

image-20210315164145601

之后进入getAdapter().service()方法中,这里可以看到我们请求的URL为/asdf:

image-20210315164452905

/asdf这个路由是我们故意构造的一个不存在的路由,当路由无法匹配对应的servlet的时候会由org.apache.catalina.servlets.DefaultServlet来处理请求,是在{CATALINA_HOME}/conf/web.xml中默认配置的:

image-20210315164813262

image-20210316004730879

在DefaultServlet的service()方法中打下断点,当我们请求方式为GET方式的时候,存在service()->doGet()->serveResource()的调用链:

image-20210316003333899

再贴一个调用栈:

image-20210316003409095

在serveResource()中调用了getRelativePath()方法:

image-20210316003649513

在这个方法中会读取出我们POC中构造的恶意属性,然后把两个属性拼接成一个path:

image-20210316003824993

然后在serveResource()方法中会把这个path传入resources.getResource()方法中造成任意文件读取:

image-20210316003942820

之后会把根据path获取到文件资源序列化输出,这样客户端再根据AJP协议的数据包进行解包,就能读取到文件内容了:

image-20210316004240365

# JSP文件包含

Tomcat会将以.jsp、.jspx结尾的url交给org.apache.jasper.servlet.JspServlet来处理:

image-20210316010505803

image-20210316010516564

所以我们可以修改POC中的请求路径:

image-20210316011052683

然后再JspServlet#service方法中打下断点:

image-20210316012336568

这里同样会获取INCLUDE_SERVLET_PATH和INCLUDE_PATH_INFO两个属性,而这两个属性是可通过构造AJP请求数据包来控制的,这两个属性拼接到jspUri之后,会进入到serviceJsp方法:

image-20210316012711652

这个方法会把jspUri(上述为/WEB-INF/testfile)所表示的文件解析为JSP文件,从而形成文件包含:

image-20210316013019379

值得注意的是,这个所包含的文件和我们请求的url(/asdf/f4de.jsp)是没有关系的。

# 其他

# 读取其他Context

默认情况下是读取的主目录(ROOT目录)中的文件内容,此外还可以读取其他Context中文件的内容,只需要修改一下请求的url即可(文件包含同理):

image-20210316102141936

image-20210316102213823

# 目录穿越可读性

我们尝试穿越出webapps这个目录,读取其他文件夹中的内容:

DefaultServlet#serveResource打下断点,在resources.getResource方法中:

image-20210316103039869

image-20210316103129512

跟进RequestUtil.normalize:

image-20210316103339267

只要包含/../,就会返回null,直接抛出异常:

image-20210316103512792

# 修复

  • 升级Tomcat版本

  • 禁用AJP协议(删除或注释conf/server.xml中对应的内容)

  • 为AJP Connector配置secret,或为AJP设置协议认证凭证

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

# 参考

[攻击Java Web应用 - Java Web安全] (zhishihezi.net) (opens new window)

Tomcat Ajp协议漏洞 | l3yx's blog (opens new window)

IDEA 导入 Tomcat9 源码 - 江湖小小白 - 博客园 (cnblogs.com) (opens new window)

https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi

四张图带你了解Tomcat系统架构--让面试官颤抖的Tomcat回答系列!_徐刘根的博客-CSDN博客 (opens new window)

apache tomcat AJP漏洞修复方案(2020-02-21) - Exception List (xieyonghui.com) (opens new window)

基于Tomcat Response对象获取回显

基于Tomcat Response对象获取回显→

最近更新
01
在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
04-19
02
CommonsBeanUtils
04-19
03
基于Tomcat全局存储进行回显
04-16
更多文章>
Theme by Vdoing | Copyright © 2020-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×