# Exploiting outdated Apache Airflow instances

Airflow shipped a default Flask cookie signing key 'temporary_key', letting attackers forge user_id=1 admin sessions and read secrets, run ad-hoc queries, and view DAGs.

- Payout: $13,000
- Severity: critical
- Program: -
- Credited: Ian Carroll
- Disclosed: 2021-06-14
- CVE: CVE-2020-17526, CVE-2021-26559, CVE-2021-26697
- Write-up quality: 7/10
- Original: https://ian.sh/airflow
- Library page: https://bugbountylibrary.com/r/blog%3Aian-sh-airflow
- Tags: domain:web impact:ato impact:data-leak impact:privesc kind:bounty platform:blog sev:critical tech:python technique:default-creds technique:recon vuln:access-control vuln:auth-bypass vuln:info-disclosure vuln:misconfiguration

**Target:** Airflow web UI Flask session cookie / experimental API
**Root cause:** Airflow used Flask's stateless signed session cookies with a hardcoded default signing key 'temporary_key' that was never changed, so the user_id attribute could be forged.
**Steps:**
1. Browse to Airflow login page and capture the unauthenticated session cookie.
2. Run `flask-unsign --unsign --cookie '<cookie>' --wordlist` or test `flask-unsign -s --secret "temporary_key" -c "{'_fresh': True, '_id': '<id>', 'csrf_token': '<csrf>', 'user_id': '1'}"`.
3. If signing succeeds, set the forged cookie as your session cookie.
4. Access admin UI to read env vars, DAG source/logs, and run ad-hoc queries.
**How found:** Ran into an exposed Airflow instance during bug bounty recon; recognized Flask stateless signed cookies and tested the known default key 'temporary_key' with flask-unsign. Automated detection via `GET /api/v1/version` returning a distinctive v1 404 page or unauthenticated v2 version response.
**Exploitation:** Attacker captures an unauthenticated cookie, forges user_id=1 with the default key, sets it as session cookie, and gains admin UI access to environment variables (AWS keys, DB creds), DAG source/logs, and ad-hoc query execution against connected data sources.
**Escalation:** CVE-2021-26559: authenticated user retrieves config file via unauthenticated-RBAC API endpoint to get the real signing key, escalating to admin even when the default key was changed; also chains with misconfigured Google OAuth allowing any Google account login.
**Insight:** Fingerprint Airflow with `GET /api/v1/version`: a distinctive v1 404 or unauthenticated v2 version response flags an outdated/misconfigured instance; then test Flask cookies against the default 'temporary_key' with flask-unsign before assuming auth is solid.
