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.
>Configuration of port 8080 >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.
DefaultServletJspServlet
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.