
Um PoC para demonstrar CVE-2026-25604
Injeção de Cabeçalho Host levando ao bypass de autenticação SAML no AWS Auth Manager do Apache Airflow
Um atacante pode injetar um cabeçalho Host malicioso no fluxo de login SAML, fazendo com que a URL do Assertion Consumer Service (ACS) aponte para um servidor controlado pelo atacante. Isso permite que o atacante capture respostas SAML válidas e as repita para obter acesso não autorizado à instância Airflow da vítima — ou reutilize tokens em diferentes instâncias Airflow com controles de acesso distintos.
| Pacote | Afetado | Corrigido |
|---|---|---|
apache-airflow-providers-amazon | 8.0.0 – 9.21.x | 9.22.0 |
CVE-2026-25604: Origin Validation Error in AWS Auth Manager (CWE-346)
No AWS Auth Manager, a origem da autenticação SAML era usada conforme fornecida pelo cliente e não era verificada em relação à URL real da instância. Isso permitia obter acesso a diferentes instâncias com controles de acesso potencialmente diferentes ao reutilizar respostas SAML de outras instâncias.
— NVD
| Origem | Link |
|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-25604 |
| PR de correção | https://github.com/apache/airflow/pull/61368 |
O AWS Auth Manager do Apache Airflow usa SAML 2.0 via AWS IAM Identity Center para autenticação. Ao construir a solicitação de autenticação SAML, o método _prepare_flask_request() lê o cabeçalho Host diretamente da requisição HTTP recebida para montar a URL de callback do 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,
...
}
Os valores resultantes http_host e server_port são usados para construir a URL AssertionConsumerService do SAML. Como o Provedor de Identidade (IdP) confia nessa URL, ele redireciona o usuário autenticado — juntamente com a resposta SAML assinada — para onde o cabeçalho Host apontar.
┌──────────┐ ┌──────────────┐ ┌─────────────┐
│ 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! │ │
│<─────────────────────│ │
└──────────────────────┴────────────────────────┘
Cenário A — Roubo de Token via Phishing: Um atacante envia um link de login manipulado (com um cabeçalho Host forjado por meio de um proxy reverso) a um usuário legítimo. Após o usuário autenticar-se no IAM Identity Center, a resposta SAML é redirecionada para o servidor do atacante. O atacante a reproduz contra a instância Airflow real.
Cenário B — Reutilização de Token Entre Instâncias: Em ambientes Airflow multi-tenant ou com múltiplas instâncias, uma resposta SAML válida da Instância A pode ser reproduzida na Instância B. Como a origem nunca é validada em relação à URL real da instância, controles de acesso diferentes na Instância B são contornados.
CVE-2026-25604-PoC/
├── README.md # This file
└── mock_airflow.py # Mock vulnerable Airflow server
pip install flask python3-saml
Configure um aplicativo SAML 2.0 no AWS IAM Identity Center seguindo a documentação do AWS Auth Manager do Airflow:
http://<airflow-host>:<port>/login_callbackaws-auth-manager-saml-clientpython mock_airflow.py <SAML_METADATA_URL> [PORT]
Por exemplo:
python mock_airflow.py https://portal.sso.us-east-1.amazonaws.com/saml/metadata/XXXX 8080
Em um terminal separado, inicie um login SAML com um cabeçalho Host manipulado:
curl -v -H "Host: attacker.com:9090" http://127.0.0.1:8080/login
O servidor responde com um 302 Redirect para a página de login do AWS IAM Identity Center. Inspecione o AuthnRequest SAML — a URL AssertionConsumerService apontará para attacker.com:9090/login_callback em vez do servidor legítimo.
No console do servidor Airflow simulado:
[LOGIN] Host header: attacker.com:9090
[DEBUG] http_host=attacker.com, server_port=9090
O AuthnRequest SAML agora instrui o IdP a entregar a resposta SAML autenticada para attacker.com:9090, dando ao atacante um token válido para reutilizar.
airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py — o método _prepare_flask_request():
host = request.headers.get("Host", request.host)
Essa linha confia no cabeçalho Host fornecido pelo cliente sem validá-lo em relação à URL base configurada do Airflow (AIRFLOW__API__BASE_URL).
A correção (PR #61368, mesclada em 3 de fevereiro de 2026) substitui o host derivado da requisição pelo valor da configuração do Airflow:
- host = request.headers.get("Host", request.host)
+ host = conf.get("api", "base_url")
Isso garante que a URL do ACS sempre corresponda à URL real da instância configurada pelo administrador, independentemente do cabeçalho Host que o cliente enviar.
Esta prova de conceito é fornecida apenas para fins educacionais e de teste de segurança autorizado. Use-a com responsabilidade e somente contra sistemas que você possua ou para os quais tenha permissão explícita para testar.
| Commit de correção | 1a86aec |