# RCA Report: Apache Tomcat Partial PUT Session Deserialization RCE (CVE-2025-24813)

## Summary

A real Apache Tomcat runtime code-execution path was reproduced via the DefaultServlet partial PUT implementation present in Tomcat 10.1.34. With the documented non-default preconditions enabled (`readonly=false` on the DefaultServlet, partial PUT support, Tomcat `PersistentManager` plus `FileStore` session persistence, and a deserialization gadget class available to the web application), an unauthenticated HTTP client can upload a serialized session object with a `Content-Range` PUT to `/payload.session`. Tomcat 10.1.34 stores the attacker-controlled body as `work/Catalina/localhost/ROOT/.payload.session`; a follow-up request carrying `Cookie: JSESSIONID=.payload` causes Tomcat to deserialize that file and execute the attacker-controlled command. Tomcat 10.1.35, the fixed negative control, does not create the FileStore session file and does not execute the command.

## Impact

- **Package/component affected**: Apache Tomcat 10.1.x, specifically DefaultServlet partial PUT handling interacting with `org.apache.catalina.session.PersistentManager` / `FileStore` session deserialization.
- **Affected versions**: Official Tomcat 10 security notes list CVE-2025-24813 as affecting Apache Tomcat 10.1.0-M1 through 10.1.34 and fixed in 10.1.35. Equivalent 9.x and 11.x ranges are also covered by the upstream advisory, but this run tested the Tomcat 10.1 line only.
- **Risk level and consequences**: High/Critical under the documented preconditions. A remote unauthenticated attacker can turn a writable DefaultServlet partial PUT into attacker-controlled session-file creation and then trigger Java deserialization in the web application classloader. The demonstrated consequence is command execution as the Tomcat process user.

## Impact Parity

- **Disclosed/claimed maximum impact**: Code execution / RCE in Apache Tomcat from a remote HTTP entrypoint.
- **Reproduced impact from this run**: Full code execution was demonstrated. The proof payload executed `/bin/sh -c id > bundle/repro/vuln_rce_marker.txt` from Tomcat deserialization, producing `uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)`.
- **Parity**: `full`.
- **Not demonstrated**: The run did not demonstrate internet-scale exploitation against a default Tomcat installation. The exploit requires the known non-default preconditions: DefaultServlet writes enabled, file-backed session persistence, and a deserialization-capable application/library class. The proof uses a deliberately deployed minimal webapp gadget class to make the deserialization effect deterministic and safe.

## Root Cause

Tomcat's vulnerable partial PUT implementation used a temporary/file-store name derived from the user-provided request path by replacing path separators with dots. When the DefaultServlet was writable, a `PUT /payload.session` request with a `Content-Range` header could write the attacker-controlled body to a file whose path overlapped Tomcat's default FileStore session location: `work/Catalina/localhost/ROOT/.payload.session`.

When the application used `PersistentManager` with the default `FileStore`, Tomcat looked up session ID `.payload` as that same file. A subsequent request with `Cookie: JSESSIONID=.payload` caused Tomcat to load and deserialize the attacker-written object using the web application's classloader. If the application classpath contained a gadget that performs attacker-influenced behavior during deserialization, this became code execution.

Upstream documents this as CVE-2025-24813 and states it was fixed in Apache Tomcat 10.1.35 by commit `f6c01d6577cf9a1e06792be47e623d36acc3b5dc`.

## Reproduction Steps

1. Reference script: `bundle/repro/reproduction_steps.sh`.
2. The script is self-contained for this bundle. It:
   - downloads official Apache Tomcat 10.1.34 and 10.1.35 binary tarballs if not already cached under the bundle;
   - compiles a minimal serializable webapp gadget class whose `readObject()` executes a supplied command;
   - configures both Tomcat instances with DefaultServlet `readonly=false`, `PersistentManager`, and `FileStore`;
   - deploys the gadget class and a session-enabled `index.jsp` into each ROOT webapp;
   - starts both real Tomcat servers on local HTTP ports;
   - sends real unauthenticated HTTP `PUT /payload.session` requests with `Content-Range: bytes 0-5/100`;
   - triggers session loading with `GET /index.jsp` and `Cookie: JSESSIONID=.payload`;
   - checks that the vulnerable Tomcat creates `bundle/repro/vuln_rce_marker.txt` while the fixed Tomcat does not create `bundle/repro/fixed_rce_marker.txt`.
3. Expected evidence: Tomcat 10.1.34 returns PUT `201`, trigger `500`, creates the FileStore session file and writes the `id` command output marker. Tomcat 10.1.35 returns PUT `201` for an ordinary webapp resource, trigger `200`, does not create the FileStore `.payload.session`, and does not create the marker.

## Evidence

- Primary proof log: `bundle/logs/cve2025_24813_requests.log`.
- Consecutive script run logs: `bundle/logs/repro_rce_run1.log`, `bundle/logs/repro_rce_run2.log`.
- Vulnerable Tomcat server log: `bundle/logs/tomcat-10.1.34-catalina.out`.
- Fixed Tomcat server log: `bundle/logs/tomcat-10.1.35-catalina.out`.
- Runtime manifest: `bundle/repro/runtime_manifest.json`.
- Code execution marker: `bundle/repro/vuln_rce_marker.txt`.

Key evidence excerpt from the final run:

```text
vulnerable-10.1.34 PUT /payload.session Content-Range status: 201
vulnerable-10.1.34 GET /index.jsp Cookie:JSESSIONID=.payload status: 500
fixed-10.1.35 PUT /payload.session Content-Range status: 201
fixed-10.1.35 GET /index.jsp Cookie:JSESSIONID=.payload status: 200
vulnerable work session created: true
fixed work session created: false
vulnerable marker: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)
fixed marker: <absent>
CONFIRMED: vulnerable Tomcat executed attacker-controlled command: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)
```

Environment details captured by the script include Apache Tomcat 10.1.34 and 10.1.35 startup logs, OpenJDK runtime information in `catalina.out`, and real HTTP request/response header artifacts under `bundle/logs/`.

## Recommendations / Next Steps

- Upgrade affected Tomcat deployments to Apache Tomcat 10.1.35 or later for the 10.1 branch; apply the corresponding fixed release for 9.x or 11.x deployments.
- Keep the DefaultServlet `readonly` setting at its safe default (`true`) unless writes are strictly required and strongly authenticated/authorized.
- Disable partial PUT support if it is not required by the application.
- Avoid file-based session persistence at the default FileStore location when it is not required; prefer safer session storage designs.
- Treat application classpath deserialization gadgets as security-sensitive. Add Java serialization filters and remove libraries/classes that are unsafe during deserialization.
- Regression-test that `Content-Range` PUTs cannot create files in `work/Catalina/.../.<session>.session` and that `JSESSIONID=.<name>` cannot load attacker-written files.

## Additional Notes

- **Idempotency**: Confirmed. `bundle/repro/reproduction_steps.sh` was executed twice after the final fix and exited 0 both times with the same vulnerable-vs-fixed divergence and command-execution marker.
- **Negative controls**: Tomcat 10.1.35 was configured with the same webapp and operational preconditions, but the exploit did not create the FileStore session file and did not execute the command.
- **Related cleared lead**: The previous attempt's Tomcat 10.1.55 security-constraint/DefaultServlet PUT bypass was extended toward JSP upload RCE, but direct `.jsp` and classic trailing-slash variants did not produce executable JSP upload on this current Tomcat path. The confirmed RCE path is therefore the partial PUT session deserialization chain against 10.1.34.
