Back to Blog

#AshenVault Writeup

#TL;DR

AshenVault is a Web CTF Challenge (HTB Human Only) that features the CVE-2025-24813 vulnerability in Apache Tomcat. The vulnerability allows an attacker to abuse the Partial PUT feature (via the Content-Range header) to bypass restrictions and write a malicious session file. When combined with a PersistentManager configuration for session handling, this leads to Insecure Deserialization and ultimately, RCE. Boom!

#Analysis

The app is built on Apache Tomcat, using Java Servlets to handle requests. Nothing too fancy at first glance:

After poking around a bit, I noticed the frontend does some content filtering, but the backend API seemed pretty standard

Partial PUT to write a malicious session file

Checking the Dockerfile, we see it's running Apache Tomcat 9.0.98:

A quick search for CVEs on Tomcat 9.0.98 popped up two candidates: Since my goal is to read the flag (likely needing RCE to call /readflag), CVE-2025-24813 looked like the golden ticket

I dug into a detailed analysis of this CVE, and here's the gist of it:

  1. Partial PUT abuse: Apache Tomcat supports Partial PUT via the Content-Range header. If not handled correctly, this can be abused to overwrite specific files or bypass security checks.

    • Time to test if the challenge supports this. A normal PUT confirms we can write files:

    • Now, let's try a partial PUT:

    • The vulnerability lies in how path equivalence (handling / and .) is processed. After some internal calls (check the blog for the nitty-gritty), the session file gets written to the temporary cache at $TOMCAT_HOME/work/Catalina/<host>/<context>/. executePartialPut treats / as . And abc.txt appears in both webapps and the Catalina cache

  2. Insecure PersistentManager: The app uses PersistentManager for session management, as seen in context.xml. This configuration is a bit risky because it saves sessions to files

    When a session is loaded from a file, it triggers readObject() Unlike StandardManager, PersistentManager uses a FileStore to swap sessions, creating a file for each session in a directory. By default, this is the webapp's temporary work directory ($TOMCAT_HOME/work/Catalina/<host>/<context>/).

Preconditions to exploit:

  • Readonly is false in $TOMCAT_HOME/conf/web.xml
  • Supports Partial PUT (Confirmed)
  • Uses PersistentManager with FileStore (Confirmed)

We have all the ingredients! The plan: use Partial PUT to write a malicious session file, then trigger readObject() by referencing that session in an HTTP request. All we need now is a spicy payload for RCE

RCE via parseClass()

The lib folder shows the app uses groovy 3.0.25:

The Testing class uses GroovyClassLoader to handle its groovyScript attribute. And of course, we can control this property via instantiation or Java reflection.

Inspired by Orange Tsai's awesome blog: We can abuse parseClass() in the Testing class to modify the AST at compile time using @ASTTest or a custom transformation

GroovyClassLoader has an overload for parseClass(String text):

java
parseClass(String text)

This compiles the Groovy code in text into a Java class. It returns the "main" object defined in that code. This means we can tweak the AST at compile time to add whatever classes or properties we want. RCE definitely!

#Exploitation

With the analysis done, the attack path is clear:

First, we need a payload that triggers RCE.

We'll use groovy-3.0.25.jar to build it.

Commands:

bash
javac -cp .:com/example/groovy-3.0.25.jar com/example/Testing.java com/example/ExploitBuilder.java
bash
java -cp .:com/example/groovy-3.0.25.jar com.example.ExploitBuilder

The payload is successfully generated in the session file.

Next, we upload this payload to the server using a partial PUT. We'll base64 encode it to avoid issues with special characters

Finally, we call the file by setting the Cookie header to JSESSIONID=.pwn.session. This will trigger readObject() on our payload

Access the file created by our payload creates (pwned.txt) and grab the flag

Challenge pwned!

#Wrapping Up

This was a fun challenge! Learned some cool new tricks about Partial PUT and how GroovyClassLoader.parseClass() can be abused. Also got to mess around with Tomcat directories. Good times.

Hope this writeup is useful for you, thanks for reading :D

H4ppy H4ck1ng!