
SQL injection in PyAthena via DefaultParameterFormatter (CVE-2026-65321)
Severity: Critical, CVSS v4.0 9.3 / CVSS v3.1 9.8 (assigned by VulnCheck, the CNA)
Vector (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
Vector (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Affected: PyAthena <= 3.35.3 (all versions through 3.35.3)
Fixed in: 3.35.4
CWE: CWE-89 (Improper Neutralization of Special Elements used in an SQL Command, 'SQL Injection')
Reported by: Rahul Karne
CNA: VulnCheck
Published: August 3, 2026
PyAthena escaped untrusted input correctly in SELECT queries and incorrectly
in DELETE queries.
PyAthena, the widely used Python DB-API client for Amazon Athena, selects its
string-escaping routine from the statement's leading keyword. Statements
beginning with SELECT, WITH, INSERT, UPDATE, or MERGE receive
Trino-correct escaping, in which a single quote is neutralized by doubling it
(''). Every other statement, most commonly a DELETE or a CREATE TABLE … AS SELECT (CTAS), falls through to Hive-style backslash escaping (\').
Athena's engine is Trino, which treats a backslash inside a single-quoted string
as an ordinary character, so backslash escaping neutralizes nothing. An attacker
who can influence a string parameter in such a statement can terminate the
literal and inject arbitrary SQL, with no authentication and no user
interaction.
The flaw is therefore absent from the read path and present in exactly the destructive statement types where it does the most damage. The package is pulled 22.3 million times a month.
PyAthena is a third-party community client library for Amazon Athena. It is not an AWS product, and this is not a vulnerability in AWS or in Athena itself.
An attacker who controls a string parameter passed to a vulnerable statement can
break out of the intended string literal and alter the statement's logic. The
most direct and reliably demonstrable impact is unauthorized data deletion:
a payload such as missing' OR 1=1 -- in a DELETE … WHERE token = %(token)s
query neutralizes the WHERE predicate and deletes every row the Athena
workgroup's IAM role is permitted to delete (for example, all rows of an Iceberg
table). Depending on the statement type and the role's permissions, an attacker
may also be able to create attacker-defined tables via CTAS injection and, where
they can subsequently read the resulting table, exfiltrate data from other
tables the role can access.
All impact is bounded by the permissions of the Athena workgroup / IAM role the client uses. This is a data-plane injection into Athena's SQL engine; it does not yield code execution on the host running PyAthena, nor compromise of AWS itself.
Who is affected: Applications using PyAthena < 3.35.4 with the default
DefaultParameterFormatter (client-side pyformat / named parameter
substitution) that (1) construct a statement not starting with
SELECT/WITH/INSERT/UPDATE/MERGE, in practice DELETE, CTAS,
CREATE VIEW, DROP, or ALTER, and (2) pass attacker-influenced data as a
string parameter to that statement.
Who is not affected:
3.35.4 or later.SELECT/WITH/INSERT/UPDATE/MERGE, these route to the safe
quote-doubling escaper.| Metric | Value | Source |
|---|---|---|
| Downloads, all-time | 740.6M | pepy.tech/projects/pyathena |
| Downloads, last 30 days | 22.3M | pepy.tech |
| Downloads, last 24 hours | 221.0K | pepy.tech |
| Sustained install rate | 8.95/second | pepy.tech |
| Notable downstream | dbt-athena imports _escape_hive and _escape_presto from pyathena.formatter directly | connections_legacy.py#L23-L27 |
DefaultParameterFormatter.format() selects the string-escaping function purely
from the statement's leading keyword. Only an allowlist of prefixes receives the
Trino-correct escaper; every other statement falls through to Hive-style
backslash escaping.
# 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}'"
Amazon Athena's SQL engine is Trino (Presto in earlier engine versions). In
Trino, the only escape for a single quote inside a single-quoted string literal
is to double it (''); a backslash is a literal character. _escape_hive
therefore does not neutralize a quote at all for Athena, it emits
... = 'missing\' OR 1=1 -- ', which Trino parses as the string literal
'missing\' followed by OR 1=1 -- ', i.e. attacker-controlled SQL.
The design is fail-dangerous: it allowlists the safe path and defaults
everything else to the unsafe escaper. The upstream fix inverts this to
fail-safe (default to the Trino escaper; use Hive escaping only for genuine Hive
DDL such as CREATE DATABASE/DROP TABLE/MSCK REPAIR, while treating CTAS and
CREATE VIEW as Trino), and additionally strips leading SQL comments so a
/* … */ DELETE … prefix cannot defeat statement-type detection.
_escape_hive is not missing sanitization, it is sanitization. It is a
correct, well-formed escaping routine for Hive's string-literal grammar, applied
to an engine that uses Trino's. Taint-tracking tools model SQL injection as
untrusted data reaching a sink without passing through an escaper; here the data
passes through an escaper on every path, and the escaper looks exactly like
remediation code because it is remediation code, for the wrong dialect.
Dialect correctness is not a taint property, so no taint rule evaluates it. The defect is structurally invisible to CodeQL, Semgrep, Snyk, and Socket rather than merely overlooked by them, which is why it persisted in a package installed roughly nine times per second.
An attacker needs:
< 3.35.4 with the default client-side
parameter formatter (pyformat / named paramstyle).SELECT/WITH/INSERT/UPDATE/MERGE, in practice DELETE or CTAS.Numeric parameters, and any statement routed to the safe escaper, are not exploitable via this flaw.