{"repro_id":"REPRO-2026-00282","version":6,"title":"Apache Tomcat partial PUT session deserialization RCE","repro_type":"security","status":"published","severity":"high","description":"Goal: hunt for a real runtime security impact in Apache Tomcat on a real Tomcat server/webapp, scoped to apache/tomcat and recent official 2026 advisory classes plus adjacent code. Do not rerun the already confirmed CVE-2026-50229 examples XSS. Prioritize attacker-perspective runtime proof, highest achievable impact, and negative controls against fixed/current versions. Candidate surfaces: security-constraint bypasses including method omission and default servlet behavior; RewriteValve normalization/decoding bypasses including WEB-INF/META-INF exposure and PUT-to-RCE style chains when realistic; WebDAV unauthenticated body handling and DoS/side effects; AJP authentication and secret edge cases; cluster EncryptInterceptor replay/padding/bypass behaviors. Required proof: start a real Tomcat instance or integration harness, configure the relevant feature under realistic preconditions, send real HTTP/AJP/cluster requests, capture server/client logs, write reproduction_steps.sh, runtime_manifest.json, validation_verdict.json, and negative-control evidence. Avoid text-only/static assertions. If no exploitable path exists, produce a runtime-clear verdict with exact exercised surface and blocker.","root_cause":"# RCA Report: Apache Tomcat Partial PUT Session Deserialization RCE (CVE-2025-24813)\n\n## Summary\n\nA 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.\n\n## Impact\n\n- **Package/component affected**: Apache Tomcat 10.1.x, specifically DefaultServlet partial PUT handling interacting with `org.apache.catalina.session.PersistentManager` / `FileStore` session deserialization.\n- **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.\n- **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.\n\n## Impact Parity\n\n- **Disclosed/claimed maximum impact**: Code execution / RCE in Apache Tomcat from a remote HTTP entrypoint.\n- **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)`.\n- **Parity**: `full`.\n- **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.\n\n## Root Cause\n\nTomcat'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`.\n\nWhen 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.\n\nUpstream documents this as CVE-2025-24813 and states it was fixed in Apache Tomcat 10.1.35 by commit `f6c01d6577cf9a1e06792be47e623d36acc3b5dc`.\n\n## Reproduction Steps\n\n1. Reference script: `bundle/repro/reproduction_steps.sh`.\n2. The script is self-contained for this bundle. It:\n   - downloads official Apache Tomcat 10.1.34 and 10.1.35 binary tarballs if not already cached under the bundle;\n   - compiles a minimal serializable webapp gadget class whose `readObject()` executes a supplied command;\n   - configures both Tomcat instances with DefaultServlet `readonly=false`, `PersistentManager`, and `FileStore`;\n   - deploys the gadget class and a session-enabled `index.jsp` into each ROOT webapp;\n   - starts both real Tomcat servers on local HTTP ports;\n   - sends real unauthenticated HTTP `PUT /payload.session` requests with `Content-Range: bytes 0-5/100`;\n   - triggers session loading with `GET /index.jsp` and `Cookie: JSESSIONID=.payload`;\n   - 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`.\n3. 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.\n\n## Evidence\n\n- Primary proof log: `bundle/logs/cve2025_24813_requests.log`.\n- Consecutive script run logs: `bundle/logs/repro_rce_run1.log`, `bundle/logs/repro_rce_run2.log`.\n- Vulnerable Tomcat server log: `bundle/logs/tomcat-10.1.34-catalina.out`.\n- Fixed Tomcat server log: `bundle/logs/tomcat-10.1.35-catalina.out`.\n- Runtime manifest: `bundle/repro/runtime_manifest.json`.\n- Code execution marker: `bundle/repro/vuln_rce_marker.txt`.\n\nKey evidence excerpt from the final run:\n\n```text\nvulnerable-10.1.34 PUT /payload.session Content-Range status: 201\nvulnerable-10.1.34 GET /index.jsp Cookie:JSESSIONID=.payload status: 500\nfixed-10.1.35 PUT /payload.session Content-Range status: 201\nfixed-10.1.35 GET /index.jsp Cookie:JSESSIONID=.payload status: 200\nvulnerable work session created: true\nfixed work session created: false\nvulnerable marker: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)\nfixed marker: <absent>\nCONFIRMED: vulnerable Tomcat executed attacker-controlled command: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)\n```\n\nEnvironment 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/`.\n\n## Recommendations / Next Steps\n\n- 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.\n- Keep the DefaultServlet `readonly` setting at its safe default (`true`) unless writes are strictly required and strongly authenticated/authorized.\n- Disable partial PUT support if it is not required by the application.\n- Avoid file-based session persistence at the default FileStore location when it is not required; prefer safer session storage designs.\n- Treat application classpath deserialization gadgets as security-sensitive. Add Java serialization filters and remove libraries/classes that are unsafe during deserialization.\n- Regression-test that `Content-Range` PUTs cannot create files in `work/Catalina/.../.<session>.session` and that `JSESSIONID=.<name>` cannot load attacker-written files.\n\n## Additional Notes\n\n- **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.\n- **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.\n- **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.\n","cve_id":"CVE-2025-24813","package":{"name":"Apache Tomcat","ecosystem":"maven"},"reproduced_at":"2026-07-11T13:12:29.424988+00:00","duration_secs":2554.0,"tool_calls":240,"handoffs":3,"total_cost_usd":7.426812500000001,"agent_costs":{"hypothesis_generator":0.161903,"judge":1.265889,"repro":5.2338195,"support":0.7652010000000001},"cost_breakdown":{"hypothesis_generator":{"gpt-5.5-2026-04-23":0.161903},"judge":{"gpt-5.5":1.265889},"repro":{"accounts/fireworks/models/kimi-k2p6":1.1807495,"gpt-5.5":4.053070000000001},"support":{"gpt-5.5":0.7652010000000001}},"quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-07-11T13:12:49.509589+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":7939,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":12514,"category":"reproduction_script"},{"path":"bundle/repro/hypothesis_ledger.md","filename":"hypothesis_ledger.md","size":6547,"category":"documentation"},{"path":"bundle/artifact_promotion_manifest.json","filename":"artifact_promotion_manifest.json","size":4559,"category":"other"},{"path":"bundle/artifact_promotion_report.json","filename":"artifact_promotion_report.json","size":4577,"category":"other"},{"path":"bundle/logs/repro_run5.log","filename":"repro_run5.log","size":912,"category":"log"},{"path":"bundle/logs/repro_run6.log","filename":"repro_run6.log","size":912,"category":"log"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":758,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":1588,"category":"other"},{"path":"bundle/logs/cve2025_24813_requests.log","filename":"cve2025_24813_requests.log","size":830,"category":"log"},{"path":"bundle/logs/repro_rce_run2.log","filename":"repro_rce_run2.log","size":540,"category":"log"},{"path":"bundle/repro/vuln_rce_marker.txt","filename":"vuln_rce_marker.txt","size":54,"category":"other"},{"path":"bundle/logs/tomcat-10.1.34-catalina.out","filename":"tomcat-10.1.34-catalina.out","size":8553,"category":"other"},{"path":"bundle/logs/tomcat-10.1.35-catalina.out","filename":"tomcat-10.1.35-catalina.out","size":8697,"category":"other"}]}