# CVE-2020-1938:Tomcat AJP文件包含漏洞分析

## 0x01 前言

最近这个漏洞特别的火，各大媒体都在报道，在第一时间进行复现过后，我就想着找个时间分析分析，但是看了老半天的代码也没找到漏洞的触发点，只是根据PoC确定了是由于ajp协议可以自定义request的某些属性值导致的漏洞，而且我翻源码也找到了ajp协议的逻辑代码。但是更进一步，就卡住了，不知道自定义属性怎么就导致了漏洞的产生\~没办法，只有去看看别的师傅的分析文章，恍然大悟，原来经过我们自定义属性过后的request对象的后续处理依旧符合tomcat的处理流程，就是针对某个地址的请求会被预先设置好的servlet处理，而任意文件包含也就出现在这些servlet中。下面我们就来看下具体的代码吧

## 0x02 漏洞分析

在分析之前我们还需要了解一下ajp协议的作用。移步： <https://blog.csdn.net/jeikerxiao/article/details/82745516>

我翻了下tomcat的源码，发现ajp协议实现在下面这一堆源码里：

![](https://1467638070-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXDNL45uu6Yo5aUtcMFVW%2Fuploads%2Fgit-blob-2ab487750bf8ec24a3990e6736e050ed1401df4e%2Fajp.png?alt=media)

然后我把这几个文件都翻了一遍，确定了几个可疑的文件以及可疑的函数，那么怎么确定我们的怀疑是否正确呢？只有下断点，然后发送payload,看看是否能够触发断点23333。结合网上公开的poc

![](https://1467638070-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXDNL45uu6Yo5aUtcMFVW%2Fuploads%2Fgit-blob-a69e68e7e3e2a093ad1f504aec11c14cb562cb4b%2Fpoc.png?alt=media)

可以看到是设置了几个属性的，而且name是req\_attrubute,然后我就去ajp协议源码中去找对这些属性进行处理的部分，一顿操作猛如虎，反正就是反复的调试，最后确定到prepareRequest这个方法，而且这个方法中可以看到这么一段代码：

![](https://1467638070-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXDNL45uu6Yo5aUtcMFVW%2Fuploads%2Fgit-blob-359d6b6e2ec9253d8f172013c26c099010ee3f2c%2Freq_attribute.png?alt=media)

经过几次调试，最终确定这里就是设置request属性的地方了，但是设置了request属性过后，request对象又会怎么被处理呢？我就是一直卡在这里了，当然，后面看到师傅的文章，知道请求会发送到对应的servlet，如果我们请求的是一个jsp文件，根据tomcat的默认web.xml文件：

```xml
    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>
```

可以看到是被JspServlet处理了，会执行到JspServlet的service方法：

```java
    public void service (HttpServletRequest request,
                             HttpServletResponse response)
                throws ServletException, IOException {

        //jspFile may be configured as an init-param for this servlet instance
        String jspUri = jspFile;

        if (jspUri == null) {
            // JSP specified via <jsp-file> in <servlet> declaration and
            // supplied through custom servlet container code
            String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
            if (jspFile != null) {
                jspUri = jspFile;
                request.removeAttribute(Constants.JSP_FILE);
            }
        }
        if (jspUri == null) {
            /*
             * Check to see if the requested JSP has been the target of a
             * RequestDispatcher.include()
             */
            jspUri = (String) request.getAttribute(
                    RequestDispatcher.INCLUDE_SERVLET_PATH);
            if (jspUri != null) {
                /*
                 * Requested JSP has been target of
                 * RequestDispatcher.include(). Its path is assembled from the
                 * relevant javax.servlet.include.* request attributes
                 */
                String pathInfo = (String) request.getAttribute(
                        RequestDispatcher.INCLUDE_PATH_INFO);
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            } else {
                /*
                 * Requested JSP has not been the target of a
                 * RequestDispatcher.include(). Reconstruct its path from the
                 * request's getServletPath() and getPathInfo()
                 */
                jspUri = request.getServletPath();
                String pathInfo = request.getPathInfo();
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("JspEngine --> " + jspUri);
            log.debug("\t     ServletPath: " + request.getServletPath());
            log.debug("\t        PathInfo: " + request.getPathInfo());
            log.debug("\t        RealPath: " + context.getRealPath(jspUri));
            log.debug("\t      RequestURI: " + request.getRequestURI());
            log.debug("\t     QueryString: " + request.getQueryString());
        }

        try {
            boolean precompile = preCompile(request);
            serviceJspFile(request, response, jspUri, precompile);
        } catch (RuntimeException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(e);
        }

    }
```

经过上面代码的处理，最终jspUri的值变成了`javax.servlet.include.servlet_path + javax.servlet.include.path_info`，而这两个属性的值都是我们可控的。然后jspUri传入了serviceJspFile方法，也就是把jspUri指定的文件当作jsp文件解析了，所以能够造成RCE。

![](https://1467638070-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXDNL45uu6Yo5aUtcMFVW%2Fuploads%2Fgit-blob-cd9f7e6537ddfd970986193db41169834f237fa3%2Fcalc.png?alt=media)

而如果我们构造一个请求普通文件的ajp请求，tomcat最终根据web.xml中的配置调用DefaultServlet处理，也会造成LFI漏洞。具体就不分析了

## 0x03 其他

参考： <https://www.colabug.com/2020/0222/7029362/>

这篇文章写的比较水，主要是能写的并不多，其实就是通过ajp协议向tomcat发送请求包是可以控制request对象的`javax.servlet.include.request_uri, javax.servlet.include.path_info, javax.servlet.include.servlet_path` 这三个关键属性，导致我们可以访问任意的文件。

阅读本文需要结合PoC，因为很多线索都在PoC中： <https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi/blob/master/CNVD-2020-10487-Tomcat-Ajp-lfi.py>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://esonhugh.gitbook.io/javasec/tomcat-ajp-ren-yi-wen-jian-bao-han-lou-dong-fen-xi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
