# CVE-2024-25128: Apache Airflow: Authentication Bypass when Legacy OpenID(2.0) is in use as AUTH_TYPE

Flask-AppBuilder's legacy OpenID 2.0 auth (AUTH_OID) failed to validate the client-supplied IDP URL, letting an attacker point login at their own IDP and authenticate as any existing Airflow user.

- Payout: hidden
- Severity: medium
- Program: Internet Bug Bounty
- Found by: parantheses
- Disclosed: 2024-04-28
- CVE: CVE-2024-25128
- Write-up quality: 6/10
- Original: https://hackerone.com/reports/2401359
- Library page: https://bugbountylibrary.com/r/h1%3A2401359
- Tags: area:web domain:web impact:ato impact:privesc kind:bounty platform:hackerone sev:medium tech:python technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Flask-AppBuilder /login/ OpenID 2.0 authentication endpoint
**Root cause:** The backend accepted the `openid` IDP URL from the client POST body without checking it against the configured allowed IDP list, so any attacker-controlled OpenID 2.0 provider was trusted.
**Steps:**
1. Configure Airflow with `AUTH_TYPE = AUTH_OID` and a list of allowed OpenID providers.
2. Open the login page and select a provider; observe `POST /login/` with body parameter `openid=<idp_url>`.
3. Replace `openid` with an attacker-controlled IDP URL (PoC used `https://openstackid.org`).
4. Complete authentication at the malicious IDP; backend trusts it and redirects back logged in as the target existing user.
**How found:** Noticed the login POST carries the IDP URL as a client-controlled `openid` parameter and hypothesized the backend never validates it against the configured allowed-provider list; confirmed by swapping in an arbitrary IDP.
**Exploitation:** Attacker deploys their own OpenID 2.0 IDP, submits its URL as the `openid` parameter on `/login/`, authenticates there, and is redirected into Airflow as an arbitrary existing account, achieving full account hijacking.
**Insight:** When an auth flow passes the identity provider URL/identifier as a client parameter, test whether the server actually enforces its allowlist — legacy OpenID 2.0 (not OIDC) paths in Flask-AppBuilder-style frameworks often skip that check.
