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 % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
2.2 Mecánica de la cadena
%local_dtdcarga la DTD local de DocBook (docbookx.dtd), que define varias parameter entities — entre ellasISOamso, usada por el subset ISO del estándar.%ISOamsose redefine con una declaración condicional: como el parser procesa la definición antes de la del original, gana la del atacante.- La redefinición declara
%file(lee/etc/passwd) y%eval, que construye dinámicamente%errorcomo una SYSTEM URI de un path inexistente que embebe el contenido de%fileen la ruta:
<!ENTITY error SYSTEM 'file:///nonexistent/root:x:0:0:root:/root:/bin/bash...'>
- Al expandir
%error, el parser intenta abrirfile:///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;=%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/passwdacaba 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-declla neutraliza por completo.