Skip to content

CVE-2026-33264: Verified Repro With Script Download

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 Variant found 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://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)
Variants tested

Airflow 3.3.0 removes/rejects the parent BASE_TRIGGER serialized-DAG import path, but BaseSerialization.deserialize() still imports attacker-controlled AIRFLOW_EXC_SER exc_cls_name from serialized DAG data. The API Server loads the malicious serialized DAG through /ui/grid/structure/{dag_id} and imports evil_exception…

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.

Variant Analysis & Alternative Triggers for CVE-2026-33264

Versions: versions tested: Airflow 3.2.2 and fixed/latest Airflow 3.3.0. PyPI reported 3.3.0 as both the installed fixed version and latest available release during this run.

A distinct bypass was confirmed against the fixed/latest Apache Airflow 3.3.0 release. The parent reproduction used serialized DAG data with __type="base_trigger"; Airflow 3.3.0 removes that DAT.BASE_TRIGGER deserialization branch. However, BaseSerialization.deserialize() still accepts __type="airflow_exc_ser" and still calls import_string() on attacker-controlled exc_cls_name. By placing an AIRFLOW_EXC_SER object in the serialized DAG's dag.default_args field and loading the DAG through the real API-server UI endpoint, the fixed Airflow 3.3.0 API server imported an attacker-controlled module and executed import-time Python code.

Fix Coverage / Assumptions

The original fix appears to rely on the invariant that removing the historical BASE_TRIGGER case closes the dangerous serialized-DAG class-path import path. The fix explicitly covers the parent trigger path:

  • Parent path in 3.2.2: SerializedDagModel.dag -> DagSerialization.from_dict -> BaseSerialization.deserialize() -> DAT.BASE_TRIGGER -> import_string(trigger_class_path).
  • Fixed behavior in 3.3.0: DAT.BASE_TRIGGER is no longer handled by BaseSerialization.deserialize(), so the parent base_trigger payload fails closed before importing the attacker module.

The fix does not cover all class-path imports reachable from serialized DAG fields. In both Airflow 3.2.2 and 3.3.0, BaseSerialization.deserialize() keeps this branch:

  • DAT.AIRFLOW_EXC_SER / DAT.BASE_EXC_SER deserializes a dict containing exc_cls_name, args, and kwargs.
  • For DAT.AIRFLOW_EXC_SER, it calls import_string(exc_cls_name) directly.
  • No allowlist, namespace check, or fixed exception-class map is applied before importing.

The target's documented security model was reviewed. Airflow states that DAG authors can execute arbitrary code on workers, the Dag File Processor, and the Triggerer; such execution alone is expected behavior and not a vulnerability. The same security model separately says deployment managers may isolate Scheduler/API Server from DAG-author code and that no DAG-author provided code should execute in those processes except through selected entrypoint mechanisms. The variant uses the same security boundary as the parent advisory: attacker-controlled serialized DAG data causes import-time code execution inside the API Server process, not merely inside a task, Dag File Processor, or Triggerer.

Variant / Alternate Trigger

The bypass uses a different serialized object type and field path from the parent:

  • Parent trigger: dag.default_args = {"__type": "base_trigger", "__var": ["evil_trigger_module.EvilTrigger", ...]}.
  • Variant trigger: dag.default_args = {"__type": "airflow_exc_ser", "__var": {"__type": "dict", "__var": {"exc_cls_name": "evil_exception_module.EvilException", "args": [], "kwargs": ...}}}.

Exact runtime entry point:

  • HTTP endpoint: GET /ui/grid/structure/{dag_id}?offset=0&limit=100 on airflow api-server.
  • Product path: API server route loads SerializedDagModel.dag, DagSerialization.from_dict walks serialized DAG fields, and BaseSerialization.deserialize() processes AIRFLOW_EXC_SER in dag.default_args.
  • Sink: airflow/serialization/serialized_objects.py, BaseSerialization.deserialize(), DAT.AIRFLOW_EXC_SER branch, import_string(exc_cls_name).

The variant is materially distinct because it does not use the removed BASE_TRIGGER branch. It uses the still-present exception-serialization branch in fixed 3.3.0.

  • Package/component affected: apache-airflow / apache-airflow-core, serialized DAG deserialization in airflow.serialization.serialized_objects.BaseSerialization as reached by the API Server.
  • Affected versions tested: Airflow 3.2.2 and fixed/latest Airflow 3.3.0. PyPI reported 3.3.0 as both the installed fixed version and latest available release during this run.
  • Risk level and consequences: the same security-boundary violation as the parent. A DAG author or actor who can cause malicious serialized DAG data to be loaded can execute Python code in the API Server process. This may expose API-server process privileges, local files, secrets, configuration, and metadata-database access available to that process.

Impact Parity

  • Disclosed/claimed maximum impact for the parent: code execution in Scheduler/API Server through unrestricted class-path import during serialized DAG deserialization.
  • Reproduced impact from this variant run: code execution in the real Airflow API Server process on fixed/latest Airflow 3.3.0. The imported module writes api_server_airflow_exc_marker.txt at import time and records the API-server PID.
  • Parity: full.
  • Not demonstrated: the proof writes a marker file only. It does not run a shell command, persist a backdoor, exfiltrate secrets, or attempt post-exploitation.

Root Cause

The same underlying root cause remains: BaseSerialization.deserialize() treats serialized DAG data as trusted enough to control Python import paths. Airflow 3.3.0 removes the BASE_TRIGGER import sink, but leaves the AIRFLOW_EXC_SER import sink reachable from arbitrary serialized fields in a DAG object. When dag.default_args contains an AIRFLOW_EXC_SER object, the recursive deserializer reaches:

elif type_ == DAT.AIRFLOW_EXC_SER or type_ == DAT.BASE_EXC_SER:
    deser = cls.deserialize(var)
    exc_cls_name = deser["exc_cls_name"]
    ...
    if type_ == DAT.AIRFLOW_EXC_SER:
        exc_cls = import_string(exc_cls_name)

The tested fixed tag resolution for Airflow 3.3.0 was refs/tags/3.3.0^{} = 1438ea3587031417cc85d74323235cf087a058fb. The vulnerable comparison tag resolution for Airflow 3.2.2 was refs/tags/3.2.2^{} = cde4885818be51a6cdcfdf9275e100bf070025de. Known advisory fix references are GitHub PRs apache/airflow#66002 and apache/airflow#68528; the local package comparison shows the relevant effective change for the parent path is removal of the DAT.BASE_TRIGGER branch from BaseSerialization.deserialize().

Reproduction Steps

  1. Run bundle/vuln_variant/reproduction_steps.sh.
  2. The script:
    • Reuses prepared Airflow virtualenvs when available, or installs apache-airflow==3.2.2 and apache-airflow==3.3.0 into stage-local virtualenvs.
    • Creates a fresh Airflow home and SQLite metadata DB for each version.
    • Places evil_exception_module.py in the DAG folder; the module writes a marker file at import time.
    • Seeds a serialized DAG row containing an AIRFLOW_EXC_SER object in dag.default_args with exc_cls_name="evil_exception_module.EvilException".
    • Starts the real airflow api-server, waits for /api/v2/monitor/health, then requests /ui/grid/structure/{dag_id}?offset=0&limit=100.
    • Runs the same variant against vulnerable 3.2.2 and fixed/latest 3.3.0 side by side.
  3. Expected evidence:
    • Both versions return HTTP 200 from the UI endpoint.
    • Both versions create api_server_airflow_exc_marker.txt containing AIRFLOW_EXC_SER import-time code execution in Airflow API server; pid=<api-server-pid>.
    • The script exits 0 only when the fixed/latest version also reproduces the import-time execution, i.e. a bypass is confirmed.

Evidence

Primary evidence files:

  • bundle/logs/vuln_variant/reproduction_steps.log — full output from the stage-specific reproduction script. The script was executed twice successfully and confirmed idempotency.
  • bundle/logs/vuln_variant/airflow_exc_3.2.2_variant.json — vulnerable-version attempt.
  • bundle/logs/vuln_variant/airflow_exc_3.3.0_variant.json — fixed/latest-version attempt.
  • bundle/vuln_variant/artifacts/airflow_exc_3.2.2_variant/api_server_airflow_exc_marker.txt — vulnerable API-server marker.
  • bundle/vuln_variant/artifacts/airflow_exc_3.3.0_variant/api_server_airflow_exc_marker.txt — fixed/latest API-server marker.
  • bundle/logs/vuln_variant/base_deserialize_diff.txt — captured source excerpts showing 3.3.0 removed BASE_TRIGGER but retained AIRFLOW_EXC_SER import.
  • bundle/logs/vuln_variant/fixed_version.txt and bundle/logs/vuln_variant/latest_version.txt — tested release identity and PyPI/latest checks.
  • bundle/vuln_variant/runtime_manifest.json — structured runtime evidence manifest.

Key evidence from the verified second run:

  • Airflow 3.3.0 attempt JSON: service_started=true, healthcheck_passed=true, target_reached=true, http_status="200", and import_side_effect_observed=true.
  • Fixed/latest marker content: AIRFLOW_EXC_SER import-time code execution in Airflow API server; pid=17602.
  • API-server access log tail in the 3.3.0 attempt shows method=GET path=/ui/grid/structure/ghsa_2943_airflow_exc_variant_3_3_0 ... status_code=200.
  • Script verdict: RESULT: CONFIRMED BYPASS - Airflow 3.3.0 still imports attacker-controlled AIRFLOW_EXC_SER exc_cls_name during serialized DAG deserialization.

Recommendations / Next Steps

  • Treat serialized DAG deserialization as untrusted input whenever it is loaded in Scheduler/API Server contexts.
  • Extend the fix beyond BASE_TRIGGER: remove or constrain the AIRFLOW_EXC_SER class-path import path.
  • If exception deserialization must remain, use a fixed map of built-in/Airflow exception classes or a strict allowlist that cannot include DAG-bundle modules by default.
  • Apply the same policy consistently to all BaseSerialization.deserialize() branches that can import or instantiate classes from serialized data.
  • Consider rejecting AIRFLOW_EXC_SER in DAG fields where exception objects are not semantically expected, such as dag.default_args, or validating serialized DAG schemas before recursive object deserialization.
  • As defense in depth, deployments should keep Scheduler/API Server unable to import DAG-author-controlled modules and set [core] allowed_deserialization_classes narrowly, but the code fix should not rely on deployment hardening alone.

Additional Notes

The reproduction script was run twice in this stage and completed successfully both times with exit code 0, confirming idempotency. The proof uses a local API-server instance, SQLite metadata database, and localhost HTTP requests. The exploit precondition matches the parent reproduction: the attacker-controlled class path resolves in the API Server process. The test does not claim that ordinary DAG author code execution in workers, Dag File Processor, or Triggerer is itself a vulnerability; the finding is specifically code execution in API Server during serialized DAG deserialization.

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.