← Back to Attack Research

Apache Tomcat CVE-2025-24813: when a partial PUT becomes conditional RCE

A single partial PUT can end in remote code execution on Apache Tomcat, but only when a precise chain of non-default settings all line up at once. Here is the path-equivalence mechanism, how it reaches file-based session deserialization, and why the honest framing is conditional.

CVE-2025-24813 is the rare bug that reads like a headline and behaves like a checklist. The headline is real: an unauthenticated attacker can, in the worst case, upload a serialized Java object through a partial PUT and have Apache Tomcat deserialize it into running code. The checklist is what most coverage skips. That worst case only exists when four separate, mostly non-default conditions hold at the same time. Change any one of them and the outcome collapses from remote code execution to file tampering or information disclosure. This is a story about a genuine path-equivalence flaw, a naming collision inside Tomcat's own temp-file handling, and the discipline of describing severity precisely instead of loudly.

On 10 March 2025 the Apache Tomcat project disclosed CVE-2025-24813, a weakness in how the default servlet handles a specific kind of file upload. The affected versions span the actively maintained branches: 11.0.0-M1 through 11.0.2, 10.1.0-M1 through 10.1.34, 9.0.0.M1 through 9.0.98, and the end-of-life 8.5.0 through 8.5.100. The fixes landed in 11.0.3, 10.1.35, and 9.0.99. Within three weeks the issue was in CISA's Known Exploited Vulnerabilities catalog, added on 1 April 2025, the catalog's way of recording that the bug is not theoretical but being used. Verifiable security.

What makes this one worth a careful read is the gap between how it scores and how it behaves. The NVD and CNA assigned a CVSS 4.0 base of 9.2, Critical. Many outlets translated that into a familiar CVSS 3.1 shape of 9.8 and ran with the word critical. Apache's own advisory rated it Important, a notch below their top severity. Those are not contradictions, they are the same fact viewed from different distances. The maximum impact is code execution; the probability that any given Tomcat instance sits in the exact configuration required for that maximum is low. A serious defender holds both truths at once.

The bug in one paragraph

Tomcat's default servlet can accept a partial PUT, an upload that carries a Content-Range header to say it represents only a slice of a larger resource. To assemble those slices, Tomcat writes the incoming body to a temporary file on disk before the final resource is materialized. The name of that temporary file is derived from the request's target path, and in the derivation Tomcat replaces the path separators with dots. That transformation is where the weakness lives. Because different input paths can be folded down to the same on-disk name, an attacker who understands the mapping can steer the body of a partial PUT to a temporary filename of their choosing rather than the one the deployment expected. That is the essence of CWE-44, Path Equivalence, in its internal-dot form: two spellings of a path that the code treats as different but the filesystem treats as the same.

On its own, a naming quirk in a temp file is a low-grade issue. It becomes dangerous only when a second Tomcat feature is reading from the same neighborhood of disk with very different assumptions. That second feature is file-based session persistence, and the collision between the two is the whole vulnerability.

Two features that were never meant to meet

Consider the two mechanisms independently. The first is the write path. When the default servlet is allowed to accept writes, a partial PUT lands its body in a temporary file. The naming rule, separators folded to dots, was an internal implementation detail, never intended to be attacker-influenced, and for most of Tomcat's history nothing security-sensitive read those temp files by name.

The second mechanism is session persistence. Tomcat can be configured to store HTTP sessions on disk so they survive a restart, and the default on-disk session manager serializes each session to a file whose name is derived from the session identifier, the JSESSIONID. When a later request arrives carrying a session cookie, the manager finds the matching file and deserializes its bytes into a live Java Session object. This is CWE-502, Deserialization of Untrusted Data, waiting to happen, because deserialization reconstructs whatever object graph the bytes describe, and those bytes are trusted precisely because Tomcat wrote them.

The path-equivalence flaw breaks that trust assumption. If an attacker can use the dot-for-slash naming of the partial-PUT temp file to land their uploaded body exactly where the persistent session manager expects to find a session file, then Tomcat is no longer deserializing bytes it wrote. It is deserializing bytes the attacker uploaded, while believing they are a legitimate saved session. Neither feature is dangerous alone. Together, through the naming collision, they form a bridge from an unauthenticated upload to object reconstruction.

CVE-2025-24813 : TWO PATHS, ONE COLLISION WRITE PATH (partial PUT, needs readonly=false) PUT /path... Content-Range: ... body = serialized object | v default servlet writes body to TEMP FILE temp name = target path with separators folded to '.' <-- CWE-44 | v attacker-chosen bytes land at a PREDICTABLE on-disk name READ PATH (persistent sessions, file store) GET /... Cookie: JSESSIONID=<crafted name> | v file session manager loads the matching on-disk file | v Java deserializes the file into a live Session object <-- CWE-502 | v if a usable GADGET is on the classpath -> code execution collision point: the two features address the SAME disk name

The naming collision, not either feature alone, is the vulnerability. Illustrative flow, not a working exploit.

The chain, at the level of mechanism

The exploitation is a two-step sequence, described here at the level of mechanism, not as a recipe. In the first step, the attacker sends a partial PUT whose body is a serialized Java object and whose target path is chosen so that the dot-for-slash temp-file naming places that body where the session store will later look. In the second step, the attacker sends an ordinary request carrying a Cookie: JSESSIONID= value crafted so the persistent session manager resolves it to the file just uploaded, reads it, and deserializes it. If the classpath contains a library with a usable deserialization gadget, the act of reconstructing the object triggers execution. If it does not, the deserializer still runs but there is no gadget to turn object construction into a command, and the chain stops short of code execution.

That last sentence is the hinge of the entire vulnerability, and it is why the honest description is conditional. Full remote code execution through CVE-2025-24813 requires all of the following to be true at once:

  1. The default servlet must be writable. That means readonly is set to false, which is not the default. Out of the box Tomcat's default servlet rejects writes, and the partial-PUT path never opens.
  2. Partial PUT support must be enabled. This one is on by default when writes are allowed, so it is the precondition an operator is least likely to have consciously changed.
  3. File-based session persistence must be in use, at the default storage location. The read side of the collision only exists if sessions are being serialized to disk where the temp-file naming can reach them.
  4. A usable deserialization gadget must be present on the classpath. Deserialization by itself does not execute commands. It needs a library whose classes, when reconstructed, perform dangerous work as a side effect. Whether one is present depends on the application's dependencies.

Line those four up and you have unauthenticated remote code execution on a widely deployed application server, which fully justifies the Critical score at the top of the range. Remove any single one and the maximum impact drops. That is not a reason to relax; it is a reason to know your own configuration precisely.

What the bug still does when the full chain fails

A precondition that is not met removes the code-execution ending, but it does not make the path-handling weakness harmless. If the default servlet is writable and partial PUT is enabled, but there is no file-based session store or no gadget on the classpath, the attacker can still abuse the path-equivalence mapping to write uploaded content to an unexpected location, or to influence which file a later read resolves to. That means unauthorized modification of files the deployment never meant to expose to writes, plus information disclosure or corruption where the collision resolves a request to the wrong resource. Lower-severity outcomes than RCE, but on a public-facing server they are still incidents, not curiosities.

This layered impact is exactly why the scoring looks inconsistent at a glance. The CVSS 4.0 vector the CNA published, AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H, encodes the story faithfully if you read it closely. AV:N with PR:N is the scary part: network-reachable, no privileges required. AT:P, attack requirements present, is the quiet admission that the attack depends on conditions beyond the attacker's control, the whole precondition chain compressed into a single metric. Apache's Important rating and the CVSS 4.0 base of 9.2 are both defensible readings of the same vector: one weights the conditionality, the other weights the ceiling. The CVSS 3.1 translations that landed on 9.8 simply lack the attack-requirements dimension that 4.0 added for cases like this one, where impact is high but reachability is gated.

The distance between critical and merely serious here is four settings, and settings drift. Knowing your own configuration is the finding.

The reason this matters for real remediation decisions is triage bandwidth. If you read only the 9.8 and treat every Tomcat in the estate as an active RCE, you burn the same emergency response on the read-only instance that you spend on the truly exposed one, and exhaust the team before you reach the box that holds all four conditions. If you read only Apache's Important and downgrade the whole class, you miss the instance where a well-meaning operator flipped readonly to false for a legitimate upload feature and quietly assembled the chain. The correct posture is neither panic nor dismissal. It is enumeration.

What to do about it

The clean fix is the version upgrade. Tomcat 11.0.3, 10.1.35, and 9.0.99 correct the path-equivalence handling so the partial-PUT temp name can no longer be steered into the session store's namespace. If you are on 8.5.x, that branch is end-of-life at 8.5.100 and gets no fix on its own line; the move there is off 8.5 entirely, not a point release. Where you cannot upgrade immediately, the compensating controls attack the preconditions directly, and any one of them breaks the RCE chain on its own.

Close CVE-2025-24813 by version or by precondition

How to detect the attempt

Because the chain has a distinctive request signature, it is observable at the edge before you finish patching. Two shapes stand out. The first is a PUT carrying a Content-Range header aimed at an unusual path, the upload half of the chain, rare enough in normal traffic to be high-signal on most applications. The second is a request presenting a JSESSIONID cookie with a leading dot or otherwise unusual value that maps to an on-disk session file, the read half. Alerting on either, and correlating the two from the same source in sequence, gives you a detection that does not depend on knowing whether your particular instance is fully vulnerable. Log the partial PUTs, watch the session-cookie shapes, and treat the pair as an incident.

How Celvex catches this

Find. Prove. Fix. Verify.

Find

A read-only sweep fingerprints every internet-facing Tomcat, pins the exact build against the affected ranges, and reads the reachable configuration signals for the write path and session persistence, so the finding reflects your real precondition state, not a blanket CVE match.

Prove

A confirmed exposed, in-range instance becomes an Ed25519-signed Proof Capsule carrying the host, the version evidence, the CVE-2025-24813 mapping, and the KEV listing, reproducible offline by you or your auditor without rerunning anything against production.

Fix

The capsule's remediation block names the specific move: upgrade to 11.0.3, 10.1.35, or 9.0.99, or if you cannot patch yet, keep the default servlet read-only and disable partial PUT to break the chain at its first step.

Verify

A fresh sweep confirms the build is fixed or the write path is closed. The finding moves to resolved and the verified-fix event is recorded for the audit trail, so remediation is provable, not asserted.

CVE-2025-24813 is a good teacher precisely because it resists a one-word verdict. It is critical and it is conditional, and the professional work is holding both. The path-equivalence flaw is real, the deserialization ending is real, and the KEV listing confirms the chain is being exploited somewhere. Yet the box in front of you is only in that worst case if four settings agree, which no version banner can tell you. The value is not in shouting the 9.8. It is in determining, for each Tomcat you run, which build it is, whether its default servlet can write, whether it persists sessions to disk, and what sits on its classpath, and then closing whichever link is present. That determination, signed and reproducible, is the finding.

Verifiable security. Find it. Prove it. Fix it. Verify the fix held. That is what we ship.

Sources

Which of your Tomcat instances are actually in the worst case?

Free Exposure Check, no signup required. We fingerprint your internet-facing Apache Tomcat builds against CVE-2025-24813, read the preconditions that separate conditional RCE from file tampering, and ship a signed Proof Capsule for the highest-confidence finding.

Run a Free Scan →