
Iniezione SQL in PyAthena tramite DefaultParameterFormatter (CVE-2026-65321)
Gravità: Critica, CVSS v4.0 9.3 / CVSS v3.1 9.8 (assegnati da VulnCheck, il CNA)
Vettore (v4.0): CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Vettore (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Versioni interessate: PyAthena <= 3.35.3 (tutte le versioni fino alla 3.35.3)
Corretta in: 3.35.4
CWE: CWE-89 (Improper Neutralization of Special Elements used in an SQL Command, 'SQL Injection')
Segnalato da: Rahul Karne
CNA: VulnCheck
Pubblicato: 3 agosto 2026
PyAthena ha applicato correttamente l'escape degli input non attendibili nelle query e in modo errato nelle query .
SELECTDELETEPyAthena, il client Python DB-API ampiamente utilizzato per Amazon Athena, seleziona la propria routine di escape delle stringhe in base alla parola chiave iniziale dell'istruzione. Le istruzioni che iniziano con SELECT, WITH, INSERT, UPDATE o MERGE ricevono un escape corretto per Trino, in cui un apice singolo viene neutralizzato raddoppiandolo (''). Ogni altra istruzione, più comunemente una DELETE o una CREATE TABLE … AS SELECT (CTAS), ricade nell'escape in stile Hive con backslash (\'). Il motore di Athena è Trino, che tratta un backslash all'interno di una stringa racchiusa tra apici singoli come un carattere ordinario, quindi l'escape con backslash non neutralizza nulla. Un aggressore che può influenzare un parametro stringa in tale istruzione può terminare il letterale e iniettare SQL arbitrario, senza autenticazione e senza interazione con l'utente.
La vulnerabilità è quindi assente dal percorso di lettura ed è presente esattamente nei tipi di istruzione distruttivi dove causa il maggior danno. Il pacchetto viene scaricato 22,3 milioni di volte al mese.
PyAthena è una libreria client comunitaria di terze parti per Amazon Athena. Non è un prodotto AWS e questa non è una vulnerabilità in AWS o in Athena stesso.
Un aggressore che controlla un parametro stringa passato a un'istruzione vulnerabile può uscire dal letterale di stringa previsto e alterare la logica dell'istruzione. L'impatto più diretto e dimostrabile in modo affidabile è la cancellazione non autorizzata di dati: un payload come missing' OR 1=1 -- in una query DELETE … WHERE token = %(token)s neutralizza il predicato WHERE ed elimina ogni riga che il ruolo IAM del workgroup Athena è autorizzato a eliminare (ad esempio, tutte le righe di una tabella Iceberg). A seconda del tipo di istruzione e dei permessi del ruolo, un aggressore potrebbe anche essere in grado di creare tabelle definite dall'aggressore tramite iniezione CTAS e, laddove possa successivamente leggere la tabella risultante, di esfiltrare dati da altre tabelle accessibili al ruolo.
Tutto l'impatto è limitato dai permessi del workgroup Athena / ruolo IAM utilizzato dal client. Si tratta di un'iniezione sul piano dati nel motore SQL di Athena; non consente l'esecuzione di codice sull'host che esegue PyAthena, né la compromissione di AWS stesso.
Chi è interessato: applicazioni che utilizzano PyAthena < 3.35.4 con il DefaultParameterFormatter predefinito (sostituzione dei parametri pyformat / named lato client) che (1) costruiscono un'istruzione che non inizia con SELECT/WITH/INSERT/UPDATE/MERGE, in pratica DELETE, CTAS, CREATE VIEW, DROP o ALTER, e (2) passano dati influenzati dall'aggressore come parametro stringa a tale istruzione.
Chi non è interessato:
3.35.4 o successiva.SELECT/WITH/INSERT/UPDATE/MERGE: queste vengono instradate verso l'escaper sicuro con raddoppio delle virgolette.| Metrica | Valore | Fonte |
|---|---|---|
| Download, da sempre | 740.6M | pepy.tech/projects/pyathena |
| Download, ultimi 30 giorni | 22.3M | pepy.tech |
| Download, ultime 24 ore | 221.0K | pepy.tech |
| Tasso di installazione sostenuto | 8.95/second | pepy.tech |
| Downstream degno di nota | dbt-athena importa _escape_hive e _escape_presto direttamente da pyathena.formatter | connections_legacy.py#L23-L27 |
DefaultParameterFormatter.format() seleziona la funzione di escape delle stringhe esclusivamente in base alla parola chiave iniziale dell'istruzione. Solo una allowlist di prefissi riceve l'escaper corretto per Trino; ogni altra istruzione ricade nell'escape in stile Hive con backslash.
# src/pyathena/formatter.py, DefaultParameterFormatter.format(), lines ~271-275 (v3.35.2)
operation_upper = operation.upper()
if operation_upper.startswith(("SELECT", "WITH", "INSERT", "UPDATE", "MERGE")):
escaper = _escape_presto # safe: doubles single quotes
else:
escaper = _escape_hive # UNSAFE for Trino: backslash-escapes quotes
# src/pyathena/formatter.py, lines ~157-165 (v3.35.2)
def _escape_hive(val: str) -> str:
escaped = (
val.replace("\\", "\\\\")
.replace("'", "\\'") # produces \' (not a quote escape in Trino)
.replace("\r", "\\r")
.replace("\n", "\\n")
.replace("\t", "\\t")
)
return f"'{escaped}'"
Il motore SQL di Amazon Athena è Trino (Presto nelle versioni precedenti del motore). In Trino, l'unico escape per un apice singolo all'interno di un letterale di stringa racchiuso tra apici singoli è raddoppiarlo (''); un backslash è un carattere letterale. _escape_hive quindi non neutralizza affatto un apice per Athena: emette ... = 'missing\' OR 1=1 -- ', che Trino interpreta come il letterale di stringa 'missing\' seguito da OR 1=1 -- ', cioè SQL controllato dall'aggressore.
Il design è fail-dangerous: mette in allowlist il percorso sicuro e fa ricadere ogni altra cosa nell'escaper non sicuro per impostazione predefinita. La correzione upstream inverte questo comportamento rendendolo fail-safe (usa per impostazione predefinita l'escaper Trino; usa l'escape Hive solo per DDL Hive genuino come CREATE DATABASE/DROP TABLE/MSCK REPAIR, trattando CTAS e CREATE VIEW come Trino) e inoltre rimuove i commenti SQL iniziali, così un prefisso /* … */ DELETE … non può vanificare il rilevamento del tipo di istruzione.
_escape_hive non è priva di sanitizzazione: è sanitizzazione. È una routine di escape corretta e ben formata per la grammatica dei letterali di stringa di Hive, applicata a un motore che usa quella di Trino. Gli strumenti di taint tracking modellano l'iniezione SQL come dati non attendibili che raggiungono un sink senza passare attraverso un escaper; qui i dati passano attraverso un escaper su ogni percorso, e l'escaper assomiglia esattamente a codice di rimedio perché è codice di rimedio, per il dialetto sbagliato.
La correttezza del dialetto non è una proprietà di taint, quindi nessuna regola di taint la valuta. Il difetto è strutturalmente invisibile a CodeQL, Semgrep, Snyk e Socket, non semplicemente trascurato da essi: è per questo che è persistito in un pacchetto installato circa nove volte al secondo.
Un aggressore ha bisogno di:
< 3.35.4 con il formatter di parametri predefinito lato client (paramstyle pyformat / named).SELECT/WITH/INSERT/UPDATE/MERGE, in pratica DELETE o CTAS.I parametri numerici e qualsiasi istruzione instradata verso l'escaper sicuro non sono sfruttabili tramite questa vulnerabilità.
Il PoC chiama il vero formatter PyAthena non modificato (importato dalla release PyPI pubblicata, non una ricostruzione) ed esegue il suo output contro un database DuckDB locale in memoria. Nessun account AWS, nessuna credenziale, nessun accesso di rete. La riproduzione completa richiede due comandi:
pip install pyathena==3.35.2 duckdb
python poc_pyathena_cna_demo.py --no-pause
Fonte: poc_pyathena_cna_demo.py
Demo registrata: Guarda la demo
Il docstring di DefaultParameterFormatter afferma che esegue l'escape dei parametri per prevenire l'iniezione SQL. Il PoC stampa quel docstring, poi stampa _escape_presto, _escape_hive e il ramo di selezione del prefisso direttamente dal pacchetto installato tramite inspect.getsource, così il lettore vede la contraddizione nel codice sorgente della libreria stessa invece di credere alla parola dell'advisory.
DELETEParametro controllato dall'aggressore: missing' OR 1=1 --
DELETE FROM sessions WHERE token = 'missing\' OR 1=1 -- '
Il lexer di Trino riconosce esattamente un escape per un apice singolo all'interno di un letterale di stringa racchiuso tra apici singoli: il raddoppio (''). Un backslash non ha alcun significato di escape. Il letterale quindi termina all'apice che segue missing\, e OR 1=1 -- viene interpretato come SQL. DuckDB condivide questa proprietà e, contro una tabella sessions pre-populata con due righe, il risultato è:
Rows before executing generated SQL: 2
Rows after executing generated SQL: 0
Il predicato WHERE viene neutralizzato e ogni riga viene eliminata.
Parametro controllato dall'aggressore: nobody' UNION SELECT secret FROM admin_credentials --
CREATE TABLE leaked AS SELECT name FROM users WHERE name = 'nobody\' UNION SELECT secret FROM admin_credentials -- '
La UNION iniettata copia una riga da una tabella che l'istruzione originale non ha mai referenziato. Nella demo, DEMO_SECRET_VALUE da admin_credentials finisce nella tabella leaked visibile all'aggressore. Su Athena questo è limitato da ciò che il ruolo IAM del workgroup può leggere.
Gli stessi payload instradati attraverso SELECT e UPDATE raggiungono _escape_presto e vengono correttamente neutralizzati dal raddoppio delle virgolette:
SELECT name FROM users WHERE name = 'nobody'' UNION SELECT secret FROM admin_credentials -- '
UPDATE update_control SET token = 'x'' OR 1=1 -- ' WHERE id = 999
Entrambi i payload rimangono all'interno del letterale di stringa. SELECT restituisce zero righe e UPDATE modifica zero righe: nessuna rottura. Questi controlli stabiliscono che il banco di prova è valido e che il difetto è specifico della selezione dell'escaper, non della configurazione del test.
Gli stessi tre payload contro la 3.35.4 producono tutti output con virgolette raddoppiate:
DELETE FROM sessions WHERE token = 'missing'' OR 1=1 -- '
CREATE TABLE leaked AS SELECT name FROM users WHERE name = 'nobody'' UNION SELECT secret FROM admin_credentials -- '
La correzione resiste anche a un prefisso di commento iniziale, che altrimenti vanificherebbe il rilevamento del tipo di istruzione:
/* hi */ DELETE FROM sessions WHERE token = 'missing'' OR 1=1 -- '
In ogni caso il payload è contenuto come dato e non si verifica alcuna iniezione.
Aggiorna a PyAthena 3.35.4 o successiva:
pip install --upgrade "pyathena>=3.35.4"
Se non puoi aggiornare immediatamente: evita di passare dati non attendibili come parametri a qualsiasi istruzione che non inizi con SELECT/WITH/INSERT/UPDATE/MERGE. Per le istruzioni distruttive, valida/metti in allowlist l'input lato server oppure esegui l'operazione attraverso un percorso che non dipenda dal formatter lato client. Non esiste un flag di configurazione che modifichi la selezione dell'escaper nelle versioni interessate; l'aggiornamento è la correzione affidabile.
Nota per i progetti che importano direttamente gli escapers. La correzione 3.35.4 modifica la selezione dell'escaper all'interno di DefaultParameterFormatter.format(). Non modifica _escape_hive in sé, che rimane corretto-per-Hive e sbagliato-per-Trino by design. Qualsiasi progetto a valle che importi _escape_hive o _escape_presto da pyathena.formatter ed esegua la propria distribuzione del tipo di istruzione non viene quindi corretto aggiornando PyAthena, e dovrebbe verificare la propria logica di distribuzione rispetto alla stessa questione di dialetto.
Come verificare se sei interessato:
pip show pyathena # check the installed version
pip-audit # flags CVE-2026-65321 once it propagates to the advisory feeds
VulnCheck (il CNA) ha pubblicato due punteggi, entrambi Critici: CVSS v4.0 = 9.3 (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) e CVSS v3.1 = 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
L'attacco è remoto e non autenticato, senza interazione con l'utente (AV:N/PR:N/UI:N) e non richiede precondizioni speciali lato aggressore (AC:L/AT:N). L'impatto sul sistema vulnerabile è elevato su riservatezza, integrità e disponibilità (VC:H/VI:H/VA:H in v4.0; C:H/I:H/A:H in v3.1): una DELETE iniettata può distruggere dati, e una CTAS/UNION SELECT iniettata può leggere e copiare dati accessibili al ruolo del workgroup. Nel vector v4.0 le metriche del sistema successivo sono tutte None (SC:N/SI:N/SA:N): la vulnerabilità è confinata al confine SQL e di autorizzazione di Athena e non consente l'esecuzione di codice sull'host né un pivot verso AWS stesso. Questa tripla SC/SI/SA:N è esattamente il motivo per cui v4.0 si attesta a 9.3 piuttosto che a un 10.0 al massimo, ed è la risposta onesta alla domanda «significa compromissione totale del sistema?»: no. Il punteggio v3.1 raggiunge 9.8 perché il suo flag di scope binario (S:U/S:C) comprime in un singolo bit ciò che v4.0 suddivide su tre metriche separate del sistema successivo; i due punteggi sono coerenti, non contraddittori.
Un'avvertenza che vale la pena dichiarare proattivamente: la sfruttabilità nel mondo reale richiede che l'applicazione consumatrice instradi input non attendibili in un'istruzione parametrizzata non-SELECT (DELETE/CTAS/DROP/ALTER), e il raggio d'esplosione concreto è limitato dai permessi IAM del workgroup Athena. Il punteggio base modella il caso peggiore ragionevole; una distribuzione con privilegi minimi è interessata in modo meno grave.
| Data | Evento |
|---|---|
| 19 luglio 2026 | Vulnerabilità identificata |
| 20 luglio 2026 | Segnalata al maintainer |
| 20 luglio 2026 | Confermata dal maintainer |
| 31 luglio 2026 | Correzione committata |
| 31 luglio 2026 | Rilasciata la versione corretta 3.35.4 |
| 2 agosto 2026 | CVE-2026-65321 assegnata da VulnCheck |
| 3 agosto 2026 | Divulgazione pubblica |
Scoperta e segnalata da Rahul Karne, ricercatore di sicurezza e IEEE Senior Member. La sua ricerca si concentra su difetti di iniezione e gestione degli input in pacchetti open-source ampiamente utilizzati come dipendenze; le segnalazioni precedenti includono CVE in confluent-kafka (1,12 miliardi di download), datamodel-code-generator (185 milioni) e il plugin WordPress ElementsKit Elementor Addons (oltre 1 milione di installazioni attive).
Contatto: [email protected] · GitHub: rahulreddykarne
Richieste dei media: [email protected]. Registrazione demo ad alta risoluzione, PoC e ulteriori dettagli tecnici disponibili su richiesta.