Apache Tomcat Vulnerability Analysis: Exploiting CVE-2020-1938 for Java Code Auditing

This article primarily centers on the source code analysis and exploitation of the Apache Tomcat vulnerability (CVE-2020-1938), specifically related to the Ajp protocol. It also uses this vulnerability as a means to learn JAVA code auditing.

Introduction to the AJP13 Protocol

AJP stands for Apache JServ Protocol. Web containers that support the AJP protocol include Apache Tomcat, JBoss AS / WildFly, and GlassFish. Tomcat’s general role is to serve as a servlet container to load dynamic resources. It can also function similarly to Apache, nginx, IIS, and other web containers to handle static resource requests.

In Tomcat, $CATALINA_BASE/conf/server.xml is configured by default with two Connectors, each listening on different ports. One is the HTTP Connector which listens on port 8080 by default, and the other is the AJP Connector which listens on port 8009 by default.

Apache Tomcat vulnerability >Configuration of port 8080Apache Tomcat vulnerability >Configuration of port 8009

The communication object of the HTTP Connector is the general user. It receives static or dynamic requests from users, equivalent to a web container that can handle servlet and jsp, but its performance is far from meeting business needs.

The communication object of the AJP Connector is the web server. In web architecture, considering performance and other factors, the common approach is to separate dynamic and static resources, directing static resource requests to the web server and servlet and jsp requests to Tomcat. When a user request comes in, it first encounters the web server. If the request type is servlet or jsp, it is passed to Tomcat via the AJP Connector. The communication protocol between the web server and Tomcat here is called the AJP protocol.

Vulnerability Environment Setup

Operating System: Windows 10

Tomcat: apache-tomcat-8.5.47-src

1. Import the source code into IDEA for easier debugging. Since the Tomcat source code is compiled and packaged with Ant, if we want to use Maven, we need to add a file named pom.xml

Language: JavaScriptCopy

 4.0.0 org.apache.tomcat Tomcat8.0 Tomcat8.0 8.0   Tomcat8.0  java         java              test              org.apache.maven.plugins    maven-compiler-plugin    3.8.1         UTF-8     1.8     1.8                junit   junit   4.12   test       org.easymock   easymock   3.4       ant   ant   1.6.5       wsdl4j   wsdl4j   1.6.2       javax.xml   jaxrpc-api   1.1       org.eclipse.jdt.core.compiler   ecj   4.4.2   

2. Then add an Application configuration

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. Start Tomcat

In the logs, we can see that the 8080 and 8009 Connectors have been opened

Access http://127.0.0.1:8080

Vulnerability Analysis

1. Here we open Tomcat in debug mode

As most articles online mention, we first find the class org.apache.coyote.ajp.AjpProcessor using the IDE’s built-in find in path feature to locate AjpProcessor

Through source code analysis, we extract content within ajp and set it as the request object’s Attribute property in the prepareRequest function of AjpProcessor. We then find the following code, which decodes these additional attributes. From other analyses, we know the injection points exist in the following three Attributes.

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path

The prepareRequest function processes these three attributes, allowing us to manually control their values.

2. Here we place a breakpoint at request.setAttribute(n, v ); then start the POC script: python2 .\cve-2020-1938.py 127.0.0.1 -p 8009 -f WEB-INF/web.xml

Step Over to request.setAttribute(n, v );

Step Over to request.setAttribute(n, v );

Here we pass the three values into a HashMap. These values are received via the AJP protocol. We can use Wireshark to capture packets and view the incoming parameters of the AJP protocol.

After crafting and sending an AJP13 protocol request to Tomcat, Tomcat forwards it to a servlet for processing. In Tomcat’s $CATALINA_BASE/conf/web.xml configuration file, two Servlets are defined by default: one is DefaultServlet and the other is JSPServlet.

DefaultServletDefaultServletJspServletJspServlet

Here there is an important parameter that wasn’t mentioned earlier, which is the URI. If the URI specified in the AJP13 request can be found, the request goes through the JspServlet; otherwise, if it cannot be found, it goes through the DefaultServlet. In the simulated request, the URI provided was random and could not be found, so the current request follows the DefaultServlet path.

3. Here we place a breakpoint in the doGet method within the java.org.apache.catalina.servlets.DefaultServlet.java file since the protocol uses a GET request.

Breakpoint at serveResource, then send the AJP request again.

Find the three passed-in parameters

4. Step Info to getRelativePath and analyze the following code.

If the parameter RequestDispatcher.INCLUDE_REQUEST_URI is not null, then

Check RequestDispatcher.INCLUDE_REQUEST_URI = javax.servlet.include_uri

So servletPath and pathInfo are assigned external attributes, with pathInfo = javax.servlet.include.path_info && servletPath = javax.servlet.include.servlet_path.

The three attributes defined in the POC code allow arbitrary file reading under the WEB directory’s effect

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path.

5. Upon completing the retrieval of pathInfo and servletPath values, path concatenation occurs, resulting in “/WEB-INF/web.xml”

After stepping through the info, revert to the serveResource method. Since debug = 0, it is skipped

Continue single-step debugging; this code retrieves the resource file

View the getResource code. Note how the validate function processes the incoming path. Avoid delving deeper into the validate function and bypass it initially.

Then, return to the getResource function to obtain the processed path = /WEB-INF/web.xml.

Upon finishing the getResource function, retrieve the ultimately returned file resource. The acquisition of the /WEB-INF/web.xml path, which ideally should not be accessed, becomes evident.

Conclusion

The root cause of this vulnerability lies in the core parameters of the AJP protocol being susceptible to malicious modification, allowing attackers to craft specific parameters to read any files under the server’s webapp/ROOT directory.