← Writeups

Blind XXE basado en errores: repurpose de DTD local (docbookx.dtd) para exfiltrar /etc/passwd

Exfiltración blind de /etc/passwd repurposando la DTD local /usr/share/yelp/dtd/docbookx.dtd: redefinición de la parameter entity ISOamso para forzar un error de ruta inexistente.

1. Target Overview & Vulnerability Summary

El endpoint Check stock parsea XML sin reflejar la salida (blind XXE) y, adicionalmente, bloquea o impide el loading de DTDs externas controladas por el atacante. Para un DTD remoto, el patrón OOB clásico no aplica; la alternativa es repurpose de DTD local: referenciar una DTD que ya existe en el servidor (/usr/share/yelp/dtd/docbookx.dtd) y redefinir una de sus parameter entities para inyectar una cadena de exfiltración basada en error.

Severidad Endpoint vulnerable Root cause
High POST /product/stock (cuerpo XML) Parser XML con parameter entities y loading de DTD local habilitados; las parameter entities de una DTD local pueden redefinirse (ISOamso) para crear un canal de error con contenido del archivo objetivo

2. Technical Breakdown & Exploitation Flow

2.1 Payload de repurpose

<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
]>

2.2 Mecánica de la cadena

  1. %local_dtd carga la DTD local de DocBook (docbookx.dtd), que define varias parameter entities — entre ellas ISOamso, usada por el subset ISO del estándar.
  2. %ISOamso se redefine con una declaración condicional: como el parser procesa la definición antes de la del original, gana la del atacante.
  3. La redefinición declara %file (lee /etc/passwd) y %eval, que construye dinámicamente %error como una SYSTEM URI de un path inexistente que embebe el contenido de %file en la ruta:
<!ENTITY error SYSTEM 'file:///nonexistent/root:x:0:0:root:/root:/bin/bash...'>
  1. Al expandir %error, el parser intenta abrir file:///nonexistent/<contenido de /etc/passwd>. La ruta no existe → el parser lanza un error cuyo mensaje incluye la URI completa, es decir, el contenido del archivo. El endpoint blind materializa ese error en la respuesta.

El lab se resuelve cuando el contenido de /etc/passwd aparece en el mensaje de error reflejado.

3. Root Cause Analysis

Este patrón explota la semántica de las parameter entities durante el procesamiento de DTD:

  • En XML, una parameter entity solo puede declararse una vez; la primera definición gana. Al referenciar una DTD local, el atacante puede pre-declarar cualquier parameter entity que esa DTD defina (aquí ISOamso), secuestrando la definición original.
  • Las entidades internas pueden expandirse dentro de la literal de otra entidad (&#x25; = %, &#x26;#x25; = &#x25; a nivel texto) para construir declaraciones anidadas, un truco estándar para encadenar %file → %eval → %error.
  • La fase de inclusion replacement no valida la ruta hasta la apertura del recurso, así que el texto de /etc/passwd acaba dentro de la URI del error, y el parser lo refleja en el mensaje de excepción.

El vector funciona sin servidor externo y sin canal OOB: convierte el mensaje de error del parser en el medio de transporte.

4. Secure Remediation

La mitigación de mayor cobertura es deshabilitar por completo DTD y entidades externas (incluye parameter entities y, por tanto, el repurpose de DTD local):

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setExpandEntityReferences(false);

Consideraciones complementarias:

  • No renderizar mensajes de error crudos del parser al cliente (las excepciones de XML pueden filtrar contenido); devolver errores genéricos.
  • Eliminar DTDs innecesarias del filesystem si el runtime no las usa (reduce la superficie de "reuse"), aunque disallow-doctype-decl la neutraliza por completo.