Tomcat Ajp协议文件包含漏洞分析
2020-03-09 10:53:11 Author: xz.aliyun.com(查看原文) 阅读量:605 收藏

漏洞描述

编号:CVE-2020-1938/CNVD-2020-10487

细节:Tomcat服务器存在文件包含漏洞,攻击者可利用该漏洞读取或包含Tomcat上所有webapp目录下的任意文件,如:webapp配置文件或源代码等。

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

影响范围

  • Apache Tomcat 6
  • Apache Tomcat 7 < 7.0.100
  • Apache Tomcat 8 < 8.5.51
  • Apache Tomcat 9 < 9.0.31

漏洞成因

两个配置文件

Tomcat在部署时有两个重要的配置文件conf/server.xmlconf/web.xml。前者定义了tomcat启动时涉及的组件属性,其中包含两个connector(用于处理请求的组件):

<!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
    -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

与此对应,tomcat启动后会监听8080、8009端口,它们分别负责接受http、ajp协议的数据。后者则和普通的java Web应用一样,用来定义servlet,这里是tomcat内建的几个servlet:

<!-- The default servlet for all web applications, that serves static     -->
  <!-- resources.  It processes all requests that are not mapped to other   -->
  <!-- servlets with servlet mappings (defined either here or in your own   -->
  <!-- web.xml file).                                                       -->
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        **** SNIP ****
        <load-on-startup>1</load-on-startup>
    </servlet>
  <!-- The default servlet for all web applications, that serves static     -->
  <!-- resources.  It processes all requests that are not mapped to other   -->
  <!-- servlets with servlet mappings (defined either here or in your own   -->
  <!-- web.xml file).                                                       -->
    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        **** SNIP ****
        <load-on-startup>3</load-on-startup>
    </servlet>

    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

就像注解中描述的default servlet用来处理所有未被匹配到其他servlet的uri请求,jsp servlet用来处理以.jsp、.jspxz做后缀名的uri请求,这俩都随tomcat一起启动。

tomcat结构简介

tomcat的整体架构如上图所示,一个tomcat就是一个server,其中可以包含多个service(这里指是一个抽象的逻辑层)。而每个service由Connector、Container、Jsp引擎、日志等组件构成,与此次漏洞相关的组件主要是前两者。

Connector前面已经说过,是用来接受客户端的请求,请求中的数据包在被Connector解析后就会由Container处理。这个过程大致如下图:

Container中可以包含多个Host(虚拟主机,同Apache中定义),一个Host对应一个域名,因此Tomcat也可以配置多域名;每个Host又可以有多个Context,每个context其实就是一个web应用;而context下又有多个Wrapper,wrapper和servlet一一对应,只是它封装了一些管理servlet的函数。更进一步,客户端请求就交由servlet进入应用级的处理逻辑。

有问题的代码段一

tomcat默认监听的8009端口用来处理AJP协议。AJP协议建立在TCP socket通信之上,tomcat使用该协议和前级的Web Server传递信息,这次的漏洞就出在客户端可以利用ajp协议数据包控制request对象的一些字段。

具体地,tomcat源码的org.apache.coyote.ajp.AjpProcessor类的service()方法如下:

它调用的prepareRequest()方法用来解析一些请求头,部分内容如下:

可以看到,当ajp数据包的头设置为SC_REQ_ATTRIBUTE时(具体数值可以查询AJP协议规范),Connector会紧接着读取变量n(属性名)和v(值),当n不是SC_A_REQ_LOCAL_ADDRSC_A_REQ_REMOTE_PORTSC_A_SSL_PROTOCOL时,就会用v来赋值属性n。接着,service()方法将修改过的request代入后面的调用。

前面提到,当请求的uri无法匹配其他servlet时会由DefaultServlet处理,其中的调用流程如下图所示:

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

其调用的getRelativePath()方法内容如下:

protected String getRelativePath(HttpServletRequest request, boolean allowEmptyPath) {
    String servletPath;
    String pathInfo;

    if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
        pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
        servletPath = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
    } else{
        ......
    }
    StringBuilder result = new StringBuilder();
    if (servletPath.length() > 0) {
        result.append(servletPath);
    }
    if (pathInfo != null) {
        result.append(pathInfo);
    }
    ......
    return result.toString();
}

javax.servlet.RequestDispatcher中可以看到这三个属性的名称:

static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";

所以,我们就能通过AJP协议改变request的这三个属性来控制请求的路径,serveResource()方法获得path后的代码大致如下:

/*
*   serveResource()方法后续
*/
    ......  
    WebResource resource = resources.getResource(path);
    ......
    ServletOutputStream ostream = null;
    ostream = response.getOutputStream();
    ......
    byte[] resourceBody = resource.getContent();
    if (resourceBody == null) {
    ......
    } else {
    // Use the resource content directly
        ostream.write(resourceBody);
    }
    ......

它会直接把通过path获取的资源序列化输出,因此客户端再按照AJP协议解析数据包就能得到文件内容。

有问题的代码段二

同样的道理,tomcat默认将jsp/jspx结尾的请求交给org.apache.jasper.servlet.JspServlet处理,它的service()方法如下:

可以看到jspUri也是由两个可控的属性定义的,而在后续的代码中将会把jspUri指向的资源当做jsp文件执行,这就造成了文件包含漏洞。当Web应用上有某个文件内容可被我们控制时,譬如某应用头像上传后的存储路径位于webapp目录中,就能构造RCE。

复现过程

在复现时我在一台内网的虚拟机上搭建了Tomcat8.5.40,IP是10.0.12.93。然后下载了github上别人的POC,稍微修改了里面的代码,使它支持jsp-servlet的利用方式。首先测试任意文件下载漏洞,结果如下:

然后测试文件包含漏洞,我在webapps/examples/新建一个文件evilman.png,当做通过某接口上传的头像:

其中只有一句jsp:

<% out.println(new java.io.BufferedReader(new java.io.InputStreamReader(Runtime.getRuntime().exec("whoami").getInputStream())).readLine()); %>

最后,验证结果如下:

防御措施

  • tomcat升级到最新版
  • 临时禁用AJP协议端口,将conf/server.xml文件中8009的Connector注释掉

参考链接

  1. https://blog.csdn.net/qq_35262405/article/details/101780644
  2. https://blog.csdn.net/w1992wishes/article/details/79242797
  3. https://blog.csdn.net/u010900754/article/details/80154933
  4. https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
  5. https://www.anquanke.com/post/id/199448
  6. https://www.anquanke.com/post/id/199347

文章来源: http://xz.aliyun.com/t/7325
如有侵权请联系:admin#unsafe.sh