2 - vulnerability analysis - tomcat AJP protocol file contains vulnerability analysis [CVE-2020-1938]

Posted by genom on Mon, 03 Jan 2022 09:47:55 +0100

Vulnerability Description:

tomcat is a small and medium-sized Java EE server developed by Apache organization. It implements Java EE specifications such as servlet and JSP, and can provide web resource access services. tomcat mainly provides two communication modes to access web resources: http protocol and AJP protocol.

The tomcat server will open an AJP service on port 8009 by default, By establishing AJP communication with port 8009 of tomcat server, the client (browser) can access the web resources of the server. However, there are some defects in the design of tomcat AJP protocol. Through this, the attacker can construct a malicious request for File Inclusion operation, so as to read any file in the webapp directory of tomcat server.

Vulnerability environment:



Vulnerability recurrence:

There is a server in the conf directory of tomcat XML configuration file. There are two connectors in this file. One is used to process http protocol and the other is to process AJP protocol. The tomcat server will use the corresponding Connector according to the request of the client.

According to the characteristics of the vulnerability, you can use the network tool to scan the 8009 port of the target server to determine whether there is a vulnerability. I tried with the nmap tool and can scan the ajp service corresponding to the 8009 port of the target server. Nmap is still very powerful.

Start the tomcat server, and then execute poc to read the tomcat server directory / WEB-INF / Web XML file, you can see the successful reading of the web XML file, indicating that the vulnerability is successfully replicated.

Since the AJP protocol is based on the tcp protocol, the burpsuite tool cannot be used to capture its packets. We can only capture the packets through the wireshark tool. Here, we only need to focus on the AJP protocol packets and filter all AJP packets through the wireshark filtering rules. We can see that the packet format of the AJP protocol is somewhat similar to that of the HTTP protocol

The whole communication process can be divided into three parts: establishing tcp connection stage, AJP communication stage and releasing tcp connection stage. Here we focus on AJP communication process.

Frame 61 is the first AJP packet sent by the client (browser). Select frame 61 packet to view the details of its AJP packet and analyze what content the AJP packet sent:

You can see that the Method field in the head part of the AJP request specifies that the request Method of the AJP protocol is GET, the URI represents the web resource of the AJP request, the Version represents the Version information of the AJP request, and finally the data part of the AJP request.

In the whole AJP request packet, this part is malicious data constructed by poc program vulnerability code

Vulnerability analysis:

When the tomcat server receives the AJP request, it will be handled by the AjpProcessor class, that is, the AjpProcessor class is used to process the AJP protocol, but the parent class org. Of the AjpProcessor actually processes the AJP request data apache. coyote. ajp. AbstractAjpProcessor.

There is a process method in the AbstractAjpProcessor class to process AJP requests, but the internal logic of the process method is too complex for in-depth analysis. Here we remember that this method will call a prepareRequest method to process AJP request data, as shown below:

Inside the prepareRequest method, the setAttribute method is invoked to encapsulate the data part in the AJP request to the attributes attribute in the request request. Then tomcat distributes AJP requests to Servlet processing, while tomcat server web.. XML has two servlets by default, jspservlets that handle JSP requests and defaultservlets that handle all requests.

Because the resource of RUI field of AJP request sent by POC is jsp

According to the web of tomcat server The mapping rule of JSP request in XML file. The request will be matched by JspServlet, and the AJP request will be distributed to JspServlet for processing.

Then the JspServlet will call the service method to process the AJP request:

    @SuppressWarnings("deprecation") // Use of JSP_FILE to be removed in 8.5.x
    public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//Determine whether the jspFile is null
        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;
        if (jspUri == null) {
			//Get servlet from request domain_ path
            jspUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
            if (jspUri != null) {
				//Get path from request field_ info
                String pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
				//Add servlet_path and path_ Concatenate info into uri path
                if (pathInfo != null) {
                    jspUri += pathInfo;
            } else {
                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);
			//Then call the serviceJspFile method.
            serviceJspFile(request, response, jspUri, precompile);
        } catch (RuntimeException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        } catch (Throwable e) {
            throw new ServletException(e);


The service method will judge whether the jspFile is null. If the jspFile is null, it will call the getAttribute method to obtain the JSP resource path from the attribute attribute attribute of the request field (the values of the two constants INCLUDE_SERVLET_PATH and INCLUDE_PATH_INFO are controllable). Finally, it will get such a uri path: / WEB-INF/web.xml.

These two constants are defined as follows:

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

Then a preCompile method is called, which will get the parameter part of the request and return false in most cases

    boolean preCompile(HttpServletRequest request) throws ServletException {

		//Gets the parameter part of the request
        String queryString = request.getQueryString();
		//If the parameter is empty, false will be returned directly
        if (queryString == null) {
            return (false);


The resulting uri path is passed to the serviceJspFile method parameter jspUri

	private void serviceJspFile(HttpServletRequest request , HttpServletResponse response , String jspUri , boolean precompile)throws ServletException, IOException {
		//Get JSP based on uri path
        JspServletWrapper wrapper = rctxt.getWrapper(jspUri);
        if (wrapper == null) {
            synchronized(this) {
                wrapper = rctxt.getWrapper(jspUri);
                if (wrapper == null) {
					//Check whether the jsp resource specified by the uri exists
                    if (null == context.getResource(jspUri)) {
                        handleMissingResource(request, response, jspUri);
					//Parse jsp resources when they exist
                    wrapper = new JspServletWrapper(config, options, jspUri,rctxt);

        try {
            wrapper.service(request, response, precompile);
        } catch (FileNotFoundException fnfe) {
            handleMissingResource(request, response, jspUri);


The serviceJspFile method mainly parses and displays the JSP according to the path of the JSP resource specified by the parameter jspUri. The getResource method will judge whether the specified file exists according to the uri path in the jspUri. If it exists, it will parse the resource specified by the path.

How to achieve RCE execution through file containing vulnerabilities?

The premise is that a JSP file embedded in RCE code must be uploaded to the target tomcat server. When we access this JSP file, JspServlet will convert the specified JSP file into java code class file, and then when the bytecode file is executed, RCE code will be executed to pop up the computer (for the specific principle, please refer to the implementation principle of JspServlet source code).

Create a new 123 Insert the java code into the JSP file and upload it to the WEB-INF directory of the target tomcat server

  Created by IntelliJ IDEA.
  User: yl
  Date: 2021/8/8
  Time: 9:51
  To change this template use File | Settings | File Templates.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%-- embed RCE code --%>
<% Runtime.getRuntime().exec("calc.exe"); %>

Successfully ejected the computer

In addition to JspServlet, DefaultServlet can also generate arbitrary file inclusion vulnerabilities. The utilization ideas of these two methods are roughly the same, so we won't continue to analyze them here. Interested students can analyze them by themselves.

Vulnerability fix:

1. On the server Comment out the Connector of port 8009 in the XML and disable the AJP protocol

2. Upgrade to the latest version

Topics: Java Web Server Tomcat Cyber Security security hole