Vulnerabilidades XXE (XML eXternal Entity injection) y contramedidas

Las inyecciones de entidad externa XML (XXE) son un tipo de vulnerabilidades que se han hecho muy populares en los últimos años, de hecho ahora forma parte del Top 10 de OWASP en el punto A4

Básicamente se trata de un tipo de ataque contra una aplicación que analiza la entradas XML. A grandes rasgos, los DTD (Document Type Definition) se utilizan para definir la estructura de un documento XML y dentro de las mismas se pueden declarar entidades XML. 

Existe un tipo especial de entidades XML llamadas "entidades externas", que se utilizan para acceder a contenido local o remoto con una URL. 

Por ejemplo, esta DTD declara una entidad externa denominada "file" que apunta a file:///secrets.txt en el sistema de archivos local. El parser XML reemplazará cualquier referencia en el documento con el contenido de file:///secrets.txt.

<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE example [
   <! ENTITY file SYSTEM "file: ///secrets.txt">
]>
<example> & file; </example>

Si los usuarios pueden declarar entidades XML arbitrariamente en sus cargas, pueden declarar una entidad externa a cualquier ubicación de su máquina. 

Por ejemplo, este archivo XML contiene una entidad externa que apunta a file:////etc/shadow en su servidor.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "file:////etc/shadow" >
]>
<example>&file;</example>

Cuando se documento XML sea mostrado/parseado mostrará el fichero shadow del sistema. 

De XXE a SSRF 

Además de leer archivos del sistema, los atacantes pueden utilizar las vulnerabilidades XXE para lanzar ataques SSRF contra la red local. Por ejemplo, pueden iniciar un escaneo de puertos cambiando la URL de la entidad externa con diferentes puertos en el servidor. Con esta técnica, se pueden escanear la red local para encontrar otras máquinas o servicios vulnerables a los que apuntar.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "http://10.0.0.1:80" >
]>
<example>&file;</example>
Los XXE también se pueden utilizar para lanzar un ataque SSRF para leer metadatos de instancias de servicios en la nube de AWS. Al acceder a la dirección 169.254.169.254, los atacantes podrían recuperar tokens de acceso, secretos y claves de token de sesión del proveedor de alojamiento en la nube.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/" >
]>
<example>&file;</example>

Denegación de servicio 

Otra opción es provocar denegaciones de servicio mediante XXE. Por ejemplo, mediante el siguiente XML esta DTD incrusta entidades dentro de entidades, lo que hace que el parser XML desreferencie de forma recursiva hasta llegar al valor de entidad "/"

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE example [<!ELEMENT example ANY ><!ENTITY lol "lol"><!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"><!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"><!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"><!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"><!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"><!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"><!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"><!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"><!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]><example>&lol9;</example>

Cada entidad "lol9" se expandiría en diez "lol8", y cada una de ellas se convertiría en diez "lol7", y así sucesivamente. Eventualmente, un "lol9" se expandirá a mil millones de "lol"s. Esto sobrecargará la memoria del parser XML, lo que podría provocar que se bloquee. Este método de ataque se denomina "ataque de mil millones de risas" o "bomba XML". Curiosamente, aunque este ataque a menudo se clasifica como un ataque XXE, ¡no implica el uso de entidades externas! En su lugar, utiliza el procesamiento recursivo de entidades internas. 

Lo que si sería un ataque más sencillo pero a veces efectivo sería intentar leer un archivo potencialmente interminable, como por ejemplo:

<!ENTITY xxe SYSTEM "file:///dev/random" >]>

Normas generales a tener en cuenta por los desarrolladores contra XXE 

Como con la mayoría de las vulnerabilidades, la formación de los desarrolladores es fundamental para identificar y mitigar XXE.

Luego, la mejor manera de prevenir XXE es limitar las capacidades de sus parsers XML. Dado que el procesamiento DTD es un requisito para los ataques XXE, los desarrolladores deben deshabilitar el procesamiento DTD en sus parsers XML si fuera posible. 

Si es imposible deshabilitar las DTD por completo, entonces se deben deshabilitar las entidades externas, las entidades de parámetros y las DTD inline. También puede deshabilitar la expansión de entidades XML por completo. La forma en que puede configurar el comportamiento de un parser XML dependerá del parser XML que se utilice. 

También y dentro del marco de desarrollo seguro otras recomendaciones para prevenir XXE serían: 

  • Siempre que sea posible, utilizar formatos de datos menos complejos, como JSON, y evitar la serialización de datos confidenciales. 
  • Parchear o actualizar todos los procesadores XML y las librerías que utilizan la aplicación o el sistema operativo. 
  • Utilizar comprobadores de dependencias. 
  • Actualizar SOAP a SOAP 1.2 o superior. 
  • Deshabilitar la entidad externa XML y el procesamiento DTD en todos los parsers XML de la aplicación, según el cheat sheet de OWASP 'Prevención XXE'. 
  • Implementar whitelisting del lado del servidor para evitar datos maliciosos en documentos XML, headers o nodos. 
  • Verificar que la funcionalidad de carga de archivos XML o XSL valida el XML entrante mediante la validación XSD o similar. 
  • Las herramientas SAST pueden ayudar a detectar XXE en el código fuente, aunque la revisión manual del código es la mejor alternativa en aplicaciones grandes y complejas con muchas integraciones. 
  • Si estos controles no son posibles, considerar usar virtual patching, firewalls de aplicaciones web (WAF) o herramientas de pruebas de seguridad de aplicaciones interactivas (IAST) para detectar, monitorizar y bloquear ataques XXE. 
  • Mantener las librerías actualizadas No son solo hay que estar pendientes de los parsers XML. Muchas librerías de terceros se ocupan de XML y, por lo tanto, son susceptibles a ataques XXE. Es indispensable que sus dependencias estén a salvo de ataques XXE y actualice las bibliotecas a versiones seguras 

Prevención de XXE en C/C++ 

Para protegerse contra las vulnerabilidades XXE en C/C++, Enum xmlParserOption no debe tener las siguientes opciones definidas dentro de su configuración: 

  • XML_PARSE_NOENT: esta opción amplía las entidades y luego las sustituye con texto de reemplazo, que es donde los atacantes pueden insertar código malicioso. 
  • XML_PARSE_DTDLOAD: esta opción permite llamar y ejecutar DTD, las mencionadas entidades externas. 

C/C++ libxerces-c 

También se recomienda ysar XercesDOMParser para prevenir XXE:

XercesDOMParser "parser = new XercesDOMParser;
parser->setCreateEntityReferenceNodes(false);
Al usar SAXParser podemos obtener resultados parecidos:
SAXParser "parser = new SAXParser";
parser->setDisableDefaultEntityResolution(true);
Igual que con SAX2XMLReader:
SAX2XMLReader "reader = XMLReaderFactory::createXMLReader();
parser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);

Prevención de XXE en Java 

Las aplicaciones Java son particularmente propensas a XXE porque la mayoría de los parsers XML de Java tienen los requisitos para XXE habilitados de forma predeterminada. 

Para evitar ataques XXE en una aplicación Java, se debe deshabilitar explícitamente estas funcionalidades. 

DocumentBuilderFactory 

Por ejemplo, con la librería DocumentBuilderFactory se puede denegar DTD con esta línea:

dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 
Si no es posible deshabilitar completamente las DTD, se puede deshabilitar las entidades de parámetros y las entidades externas XML. Las entidades de parámetro son entidades XML a las que solo se puede hacer referencia en cualquier otro lugar dentro de la DTD. También podrían permitir a los atacantes lanzar XXE.
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
También se deben deshabilitar las DTD externas para evitar que los atacantes alojen una DTD externa y hagan referencia a ella en un documento XML.
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
XInclude es una función XML especial que crea un documento XML separado de una etiqueta. También puede permitir a los atacantes activar XXE. Por lo tanto, se recomienda establecer "setXIncludeAware" a false para no permitir el procesamiento de XInclude. Por último, hay que establecer "setExpandEntityReferences" para evitar que los analizadores expandan de forma recursiva las entidades XML en bombas XML.
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
XMLInputFactory Para XMLInputFactory, estas líneas deshabilitan DTD y entidades externas.
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
XMLReader Y para proteger XMLReader de XXE, se puede desautorizar el uso de DTD y DTD externas:
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
O deshabilitar el uso de entidades externas y de parámetros.
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

El código necesario para proteger a los parsers de XXE varía para los diferentes analizadores Java XML. Para obtener más información sobre cómo proteger varios parsers, se recomienda echar un ojo a la hoja de referencia de OWASP aquí

Prevención de XXE en .NET

La siguiente info proviene directamente de este enlace: https://github.com/deanf1/dotnet-security-unit-tests. Esta aplicación web cubre todos los analizadores XML de .NET actuales que son compatibles y tiene casos de prueba para cada método, lo que demuestra cuándo una implementación está a salvo de la inyección XXE y cuándo es vulnerable.

Esta tabla enumera todos los analizadores XML de .NET compatibles, así como sus niveles de seguridad predeterminados:


LINQ a XML

Los objetos XElement y XDocument que se encuentran en la biblioteca System.Xml.Linq están a salvo de la inyección XXE de forma predeterminada. Esto se debe a que XElement analiza solo los elementos dentro del archivo XML, por lo que las DTD se ignoran por completo. XDocument tiene DTD deshabilitadas de forma predeterminada, y solo se considera inseguro si se ha construido con un parser XML diferente o inseguro.

XmlDictionaryReader

System.Xml.XmlDictionaryReader también es seguro de forma predeterminada y, cuando intenta analizar la DTD, el compilador generará una excepción que indica que "los elementos CData no son válidos en el nivel superior de un documento XML". Solo se vuelve inseguro si se construye con un parser XML diferente o inseguro.

XmlDocument

Las versiones de .NET Framework anteriores a la 4.5.2 contienen versiones de System.Xml.XmlDocument que no son seguras de forma predeterminada. El objeto XmlDocument tiene un objeto XmlResolver dentro de él que debe establecerse en nulo en versiones anteriores a la 4.5.2. En las versiones 4.5.2 y posteriores, este XmlResolver se ha establecido en nulo de forma predeterminada. El siguiente ejemplo muestra cómo se hace seguro:



(XmlDocument puede volverse inseguro si crea su propio XmlResolver no nulo con configuraciones predeterminadas o inseguras).

XmlNodeReader

Los objetos System.Xml.XmlNodeReader son seguros de forma predeterminada e ignorarán las DTD, incluso si están hechos con un parser inseguro o están empaquetados en otro parser inseguro.

XmlReader

Los objetos System.Xml.XmlReader son seguros de forma predeterminada y están configurados para tener su propiedad ProhibitDtd establecida en false en .NET Framework versiones 4.0 y anteriores. Su propiedad DtdProcessing también está establecida en Prohibir en las versiones 4.0 y posteriores de .NET de forma predeterminada. Las versiones de .NET 4.5.2 y posteriores tienen XmlReaderSettings que pertenecen a XmlReader, lo que significa que XmlResolver está configurado como nulo de forma predeterminada, lo que brinda una capa adicional de seguridad.

Esto significa que los objetos XmlReader solo se volverán inseguros en la versión 4.5.2 y posteriores si tanto la propiedad DtdProcessing está configurada como "Parse" y el XmlResolver de XmlReaderSetting está configurado como un XmlResolver no nulo, con configuraciones predeterminadas / inseguras. Si necesita habilitar el procesamiento DTD, aquí se describen las instrucciones sobre cómo hacerlo de forma segura.

XmlTextReader

System.Xml.XmlTextReader es otro elemento que no es seguro de forma predeterminada en las versiones de .NET Framework anteriores a la 4.5.2.

Antes de .NET 4.0

En las versiones de .NET Framework anteriores a 4.0, el comportamiento de análisis de DTD para objetos XmlReader como XmlTextReader está controlado por la propiedad Boolean ProhibitDtd que se encuentra en las clases System.Xml.XmlReaderSettings y System.Xml.XmlTextReader. Hay que establecer estos valores a verdadero para deshabilitar completamente las DTD online.


.NET 4.0 - .NET 4.5.2

En .NET Framework versión 4.0, se cambió el comportamiento de análisis de DTD. La propiedad ProhibitDtd quedó obsoleta para la nueva propiedad DtdProcessing. Pero esto no necesariamente ha resuelto la inseguridad predeterminada porque no cambiaron la configuración predeterminada, lo que significa que XmlTextReader sigue siendo vulnerable a XXE al instalarlo. Si establece DtdProcessing en Prohibir, hará que el tiempo de ejecución genere una excepción si un elemento <!DOCTYPE> está presente en el XML. Para establecer este valor manualmente, debe utilizar lo siguiente:


Otra opción que se puede utilizar es establecer la propiedad DtdProcessing en Ignore, que no generará una excepción al encontrar un elemento <! DOCTYPE>. En cambio, simplemente lo omitirá y no lo procesará. Además, los desarrolladores también pueden configurar DtdProcessing en "Parse" si quieren permitir y procesar DTD en línea.

.NET 4.5.2 y posterior

En las versiones de .NET Framework 4.5.2 y posteriores, el XmlResolver interno de XmlTextReader se establece en nulo de forma predeterminada, lo que hace que XmlTextReader ignore los DTD de forma predeterminada. XmlTextReader puede volverse inseguro si crea su propio XmlResolver no nulo con una configuración predeterminada o insegura.

XPathNavigator

System.Xml.XPath.XPathNavigator no es seguro de forma predeterminada en las versiones de .NET Framework anteriores a la 4.5.2. Esto se debe a que los objetos IXPathNavigable como XmlDocument no son seguros de forma predeterminada en las versiones anteriores a la 4.5.2. Puede hacer que XPathNavigator sea seguro dándole un parser seguro como XmlReader (que es seguro por defecto) en el constructor de XPathDocument. Aquí hay un ejemplo:

XslCompiledTransform


System.Xml.Xsl.XslCompiledTransform (un transformador XML) es seguro de forma predeterminada siempre que el parser que se le ha proporcionado sea seguro. Es seguro de forma predeterminada porque el parser predeterminado de los métodos Transform() es un XmlReader, que también es seguro de forma predeterminada. El código fuente de este método se puede ver aquí. Algunos de los métodos Transform() pueden aceptar un XmlReader o IXPathNavigable (por ejemplo, XmlDocument) como entrada y, si pasa un parser XML inseguro, Transform tampoco será seguro.

Prevención de XXE en PHP

Como se especifica en la documentación de PHP, estos elementos deben establecerse cuando se usa el parser XML PHP predeterminado para evitar instancias de XXE:

libxml_disable_entity_loader(true);

Una descripción de cómo abusar de esta función en PHP se detalla en este artículo de SensePost, que describe una vulnerabilidad XXE basada en PHP que se solucionó en Facebook. 

Fuentes:

Comentarios