Skip to content

CVE-2026-33264: Verified Reproduction

CVE-2026-33264: Apache Airflow <3.3.0 allows deserialization of attacker-controlled class paths in BaseSerialization.deserialize , enabling DAG authors to trigger RCE in the Scheduler/API Server via malicious serialized DAGs.

CVE-2026-33264 is verified against apache/airflow · PyPI. Affected versions: < 3.3.0. Fixed in 3.3.0. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00271.

REPRO-2026-00271 apache/airflow · PyPI RCE Jul 8, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.8
Confidence
HIGH
Reproduced in
34m 38s
Tool calls
291
Spend
$10.68
01 · Overview

What Is CVE-2026-33264?

CVE-2026-33264 is a critical deserialization vulnerability (CWE-502, CVSS 9.8) in Apache Airflow before 3.3.0 that lets a DAG author trigger remote code execution in the Scheduler/API Server via a malicious serialized DAG. Pruva reproduced it (reproduction REPRO-2026-00271).

02 · Severity & CVSS

CVE-2026-33264 Severity & CVSS Score

CVE-2026-33264 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-502 Deserialization of Untrusted Data — Deserialization of Untrusted Data

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected apache/airflow Versions

apache/airflow · PyPI versions < 3.3.0 are affected.

How to Reproduce CVE-2026-33264

$ pruva-verify REPRO-2026-00271
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00271/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-33264

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

serialized DAG default_args containing BASE_TRIGGER class path evil_trigger_module.EvilTrigger

Attack chain
  1. HTTP GET /ui/grid/structure/{dag_id}
  2. SerializedDagModel.dag
  3. DagSerialization.from_dict
  4. BaseSerialization.deserialize(BASE_TRIGGER)
  5. import_string(attacker class path)
How the agent worked 635 events · 291 tool calls · 35 min
35 minDuration
291Tool calls
123Reasoning steps
635Events
Agent activity over 35 min
Support
16
Hypothesis
2
Repro
503
Judge
26
Variant
84
0:0034:38

Root Cause and Exploit Chain for CVE-2026-33264

Versions: Apache Airflow before 3.3.0, reproduced here with Airflow 3.2.2.Fixed: Apache Airflow 3.3.0.

Apache Airflow before 3.3.0 deserializes attacker-controlled serialized DAG fields with BaseSerialization.deserialize() without sufficiently constraining class-path imports. In the reproduced path, a malicious serialized DAG contains a __type="base_trigger" value whose class path points to an attacker-controlled module. When the real Airflow API server handles a UI endpoint that loads the serialized DAG (/ui/grid/structure/{dag_id}), Airflow 3.2.2 reaches SerializedDagModel.dag -> DagSerialization.from_dict -> BaseSerialization.deserialize() and calls import_string() on that class path, executing module-level code inside the API server process. Airflow 3.3.0 no longer imports the same base_trigger payload and fails closed without executing the attacker module.

  • Package/component affected: apache-airflow, specifically serialized DAG deserialization in airflow.serialization.serialized_objects.BaseSerialization as reached by API server/UI serialized DAG loading.
  • Affected versions: Apache Airflow before 3.3.0, reproduced here with Airflow 3.2.2.
  • Fixed version: Apache Airflow 3.3.0.
  • Risk level and consequences: critical/important security-boundary violation. A DAG author who can cause malicious serialized DAG data to be loaded can execute Python code in the Scheduler/API Server process, not merely in an isolated task execution context. This can expose scheduler/API credentials, metadata database access, local files, and any privileges available to those Airflow control-plane processes.

Impact Parity

  • Disclosed/claimed maximum impact: code execution in Scheduler/API Server through unrestricted class-path import during serialized DAG deserialization.
  • Reproduced impact from this run: code execution in the real Airflow API server process. The proof module writes api_server_rce_marker.txt at import time and records the API server PID. The vulnerable endpoint returns HTTP 200 after deserializing the DAG, and the marker content shows it was written by the API server process.
  • Parity: full.
  • Not demonstrated: the proof stops at controlled file creation rather than running a shell command or exfiltrating secrets. This is sufficient to demonstrate arbitrary Python module import-time code execution, but no post-exploitation chain was attempted.

Root Cause

In Airflow 3.2.2, BaseSerialization.deserialize() contains a DAT.BASE_TRIGGER case that deserializes (trigger_class_path, kwargs), calls import_string(trigger_class_path), and instantiates the returned class. The serialized class path is attacker-controlled serialized DAG data. When a server-side process such as the API server loads serialized DAG rows from the metadata database, decorated DAG fields are passed through BaseSerialization.deserialize(), allowing the import to occur in the API server process.

The reproduction stores a malicious serialized DAG with dag.default_args = {"__type": "base_trigger", "__var": ["evil_trigger_module.EvilTrigger", ...]}. Airflow 3.2.2 accepts this type and imports evil_trigger_module, whose module-level code writes a marker file. Airflow 3.3.0 rejects the historical base_trigger type during deserialization and therefore does not import the attacker module.

Known fix references from the advisory:

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh.
  2. The script:
    • Reuses the prepared project cache at <project_cache_dir>/repo when available.
    • Uses/reuses an Airflow 3.2.2 virtualenv for the vulnerable product and an Airflow 3.3.0 virtualenv for the fixed control.
    • For each attempt, initializes a fresh Airflow metadata database, creates an attacker-controlled module in the DAG folder, and seeds a serialized DAG row containing a malicious base_trigger serialized value.
    • Starts the real airflow api-server on localhost, waits for /api/v2/monitor/health, then sends an HTTP request to /ui/grid/structure/{dag_id}?offset=0&limit=100.
    • Runs two vulnerable attempts and two fixed attempts.
  3. Expected evidence of reproduction:
    • Airflow 3.2.2 attempts: HTTP endpoint is reached, returns 200, and api_server_rce_marker.txt is created with import-time code execution in Airflow API server; pid=<api-server-pid>.
    • Airflow 3.3.0 attempts: HTTP endpoint is reached but returns a deserialization error (HTTP 500) and no marker file is created.

Evidence

Primary runtime evidence is written under bundle/logs/ and bundle/artifacts/ by bundle/repro/reproduction_steps.sh.

Key files:

  • bundle/logs/reproduction_steps.log — full script output, including both clean successful runs in this session.
  • bundle/logs/attempt_3.2.2_1.json and bundle/logs/attempt_3.2.2_2.json — vulnerable product attempts.
  • bundle/logs/attempt_3.3.0_1.json and bundle/logs/attempt_3.3.0_2.json — fixed product attempts.
  • bundle/artifacts/airflow_api_3.2.2_1/api_server_rce_marker.txt and bundle/artifacts/airflow_api_3.2.2_2/api_server_rce_marker.txt — vulnerable API-server import-time execution markers.
  • bundle/artifacts/airflow_api_*/airflow_api_server.log — API server startup and HTTP access logs.
  • bundle/artifacts/airflow_api_*/endpoint_headers.txt and bundle/artifacts/airflow_api_*/endpoint_response.json — captured HTTP endpoint responses.
  • bundle/repro/runtime_manifest.json — structured runtime evidence manifest.
  • bundle/repro/validation_verdict.json — structured verdict.

Representative vulnerable evidence from the second verified run:

  • attempt_3.2.2_1.json: service_started=true, healthcheck_passed=true, target_reached=true, http_status="200", import_side_effect_observed=true.
  • Marker content: import-time code execution in Airflow API server; pid=17188.
  • API server access log shows method=GET path=/ui/grid/structure/cve_2026_33264_probe_3_2_2_1 ... status_code=200.

Representative fixed-control evidence:

  • attempt_3.3.0_1.json and attempt_3.3.0_2.json: service_started=true, healthcheck_passed=true, target_reached=true, http_status="500", import_side_effect_observed=false.
  • Fixed response excerpt: An error occurred while trying to deserialize Dag, with no api_server_rce_marker.txt created.

Environment details captured:

  • Vulnerable Airflow: 3.2.2 loaded from <project_cache_dir>/repo/airflow_product_venv.
  • Fixed Airflow: 3.3.0 loaded from <project_cache_dir>/repo/airflow_3.3.0_cve_2026_33264_venv.
  • Product entrypoint: airflow api-server (uvicorn) reached over localhost HTTP.

Recommendations / Next Steps

  • Upgrade apache-airflow to 3.3.0 or later.
  • Keep [core] allowed_deserialization_classes restricted to a narrow allowlist as defense in depth, especially in deployments where DAG authors are not fully trusted.
  • Add regression tests that insert or load serialized DAG payloads containing historical/unknown serialized types such as base_trigger and assert that API server and scheduler code reject them without importing user-controlled modules.
  • Audit other serialized DAG fields that call generic deserialization helpers to ensure no other user-controlled class path reaches import_string() in control-plane processes.

Additional Notes

  • Idempotency confirmation: bundle/repro/reproduction_steps.sh was executed twice consecutively and completed successfully both times after the fixed-control payload-generation correction.
  • The proof uses a local HTTP API server and a local SQLite metadata database, but it exercises the real Airflow API server product path and a real HTTP endpoint rather than calling BaseSerialization.deserialize() directly.
  • The seeded malicious serialized DAG row represents the advisory scenario: attacker-controlled serialized DAG data containing a class path is loaded by the API server. The exploit module is placed on the API server's DAG-folder PYTHONPATH, matching deployments where control-plane processes can import DAG-bundle modules.

CVE-2026-33264 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:13
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · GHSA-2943-9672-R45W · REPRO-20
0:01
0:03
0:05
web search
0:06
0:07
web search
0:09
0:13
0:16
0:30
0:30
extract_facts
no facts extracted
0:31
0:31
supportrepro
1:01
1:03
1:03
1:03
1:04
1:04
1:04
1:04
1:04
1:06
1:06
1:06
$ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/ 2>/dev/null && echo "---REPO---" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo 2>/dev/null | head -50 && echo "---MIRROR---" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo-mirrors 2>/dev/null
{"cwd":"/data/pruva/runs/e6d20256-b0e8-49c3-a908-a69cffcc1535","duration_ms":7,"exit_code":0,"stderr":"","stdout":"total 0\ndrwxr-xr-x 1 vscode vscode   44 Jul  6 05:49 .\ndrwxr-xr-x 1 vscode vscode 5710 Jul  7 21:53 ..\ndrwxr-xr-x 1 vscode vscode   22 Jul  6 05:49 .pruva\ndrwxr-xr-x 1 vscode vscode  270 Jul  6 09:29 repo\ndrwxr-xr-x 1 vscode vscode    0 Jul  6 05:14 repo-mirrors\n---REPO---\ntotal 68\ndrwxr-xr-x 1 vscode vscode   270 Jul  6 09:29 .\ndrwxr-xr-x 1 vscode vscode    44 Jul  6 05:49 ..\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 05:42 airflow_product_venv\n-rw-r--r-- 1 vscode vscode 65728 Jul  6 05:25 install_22.1.0.log\ndrwxr-xr-x 1 vscode vscode   476 Jul  6 05:24 pkg\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 05:34 probe_venv\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 05:34 probe_venv2\ndrwxr-xr-x 1 vscode vscode    62 Jul  6 09:23 tools\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 05:18 venv\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 09:28 venv_google_provider_22.1.0\ndrwxr-xr-x 1 vscode vscode    76 Jul  6 09:29 venv_google_provider_22.2.1\ndrwxr-xr-x 1 vscode vscode     0 Jul  6 05:42 wheelhouse\n---MIRROR---\ntotal 0\ndrwxr-xr-x 1 vscode vscode  0 Jul  6 05:14 .\ndrwxr-xr-x … [truncated]
1:09
1:09
$echo "===PKG===" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo/pkg/ && echo "===PRUVA===" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/.pruva/ && find /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/.pruva/ -maxdepth 4 -type f 2>/dev/null | head -30 && echo "===TOOLS===" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo/tools/ && echo "===VENV===" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo/venv/ && echo "===AIRFLOW_PRODUCT_VENV===" && ls -la /data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/repo/airflow_product_venv/
{"cwd":"/data/pruva/runs/e6d20256-b0e8-49c3-a908-a69cffcc1535","duration_ms":13,"exit_code":0,"stderr":"","stdout":"===PKG===\ntotal 4244\ndrwxr-xr-x 1 vscode vscode     476 Jul  6 05:24 .\ndrwxr-xr-x 1 vscode vscode     270 Jul  6 09:29 ..\n-rw-r--r-- 1 vscode vscode 1078972 Jul  6 05:24 apache_airflow_providers_google-22.0.0-py3-none-any.whl\n-rw-r--r-- 1 vscode vscode 1082710 Jul  6 05:24 apache_airflow_providers_google-22.1.0-py3-none-any.whl\n-rw-r--r-- 1 vscode vscode 1088000 Jul  6 05:23 apache_airflow_providers_google-22.2.0-py3-none-any.whl\n-rw-r--r-- 1 vscode vscode 1088679 Jul  6 05:23 apache_airflow_providers_google-22.2.1-py3-none-any.whl\ndrwxr-xr-x 1 vscode vscode     110 Jul  6 05:24 v220\ndrwxr-xr-x 1 vscode vscode     110 Jul  6 05:24 v2200\ndrwxr-xr-x 1 vscode vscode     110 Jul  6 05:24 v221\ndrwxr-xr-x 1 vscode vscode     110 Jul  6 05:24 v2210\n===PRUVA===\ntotal 0\ndrwxr-xr-x 1 vscode vscode 22 Jul  6 05:49 .\ndrwxr-xr-x 1 vscode vscode 44 Jul  6 05:49 ..\ndrwxr-xr-x 1 vscode vscode 60 Jul  6 09:56 proof-carry\n/data/pruva/project-cache/03610ec6-e6fb-4086-9681-103dd9199da6/.pruva/proof-carry/latest_attempt/repro/reproduction_steps.sh\n/data/pruva/project-cac… [truncated]
1:13
1:13
08 · How to Fix

How to Fix CVE-2026-33264

Upgrade apache/airflow · PyPI to 3.3.0 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-33264 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-33264

How does the CVE-2026-33264 deserialization RCE work?

When Airflow's API server handles a UI endpoint that loads a serialized DAG (/ui/grid/structure/{dag_id}), it reaches SerializedDagModel.dag -> DagSerialization.from_dict -> BaseSerialization.deserialize(), which calls import_string() on the attacker's base_trigger class path — executing module-level code inside the API server process rather than an isolated task worker.

Which Airflow versions are affected by CVE-2026-33264, and where is it fixed?

Apache Airflow before 3.3.0 is affected (reproduced on 3.2.2); it is fixed in 3.3.0, which no longer imports the same base_trigger payload and fails closed.

How severe is CVE-2026-33264?

It is rated critical, CVSS 9.8: a DAG author who can get malicious serialized DAG data loaded executes Python code in the Scheduler/API Server process, exposing control-plane credentials, the metadata database, and local files.

How can I reproduce CVE-2026-33264?

Download the verified script from this page and run it in an isolated environment against Airflow 3.2.2. It submits a crafted serialized DAG containing a malicious base_trigger class path, triggers the /ui/grid/structure/{dag_id} endpoint, and shows code executing in the API server process, then confirms 3.3.0 rejects the same payload.
11 · References

References for CVE-2026-33264

Authoritative sources for CVE-2026-33264 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.