
Inyección SQL en PyAthena mediante DefaultParameterFormatter (CVE-2026-65321)
Gravedad: Crítica, CVSS v4.0 9.3 / CVSS v3.1 9.8 (asignada por VulnCheck, la 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
Afectado: PyAthena <= 3.35.3 (todas las versiones hasta la 3.35.3)
Corregido en: 3.35.4
CWE: CWE-89 (Neutralización incorrecta de elementos especiales utilizados en un comando SQL, 'Inyección SQL')
Reportado por: Rahul Karne
CNA: VulnCheck
Publicado: 3 de agosto de 2026
PyAthena escapaba correctamente la entrada no confiable en las consultas SELECT e incorrectamente en las consultas DELETE.
PyAthena, el cliente Python DB-API de Amazon Athena ampliamente utilizado, selecciona su rutina de escape de cadenas según la palabra clave inicial de la sentencia. Las sentencias que comienzan con SELECT, WITH, INSERT, UPDATE o MERGE reciben un escape correcto para Trino, en el que una comilla simple se neutraliza duplicándola (''). Cualquier otra sentencia, más comúnmente un DELETE o un CREATE TABLE … AS SELECT (CTAS), cae en el escape con barra invertida estilo Hive (\'). El motor de Athena es Trino, que trata una barra invertida dentro de una cadena entre comillas simples como un carácter ordinario, por lo que el escape con barra invertida no neutraliza nada. Un atacante que pueda influir en un parámetro de cadena en dicha sentencia puede terminar el literal e inyectar SQL arbitrario, sin autenticación y sin interacción del usuario.
Por lo tanto, la falla está ausente de la ruta de lectura y presente exactamente en los tipos de sentencias destructivas donde causa más daño. El paquete se descarga 22,3 millones de veces al mes.
PyAthena es una biblioteca cliente comunitaria de terceros para Amazon Athena. No es un producto de AWS, y esta no es una vulnerabilidad en AWS ni en Athena en sí.
Un atacante que controle un parámetro de cadena pasado a una sentencia vulnerable puede salirse del literal de cadena previsto y alterar la lógica de la sentencia. El impacto más directo y demostrable de manera fiable es la eliminación no autorizada de datos: una carga útil como missing' OR 1=1 -- en una consulta DELETE … WHERE token = %(token)s neutraliza el predicado WHERE y elimina todas las filas que el rol IAM del workgroup de Athena tiene permitido eliminar (por ejemplo, todas las filas de una tabla Iceberg). Dependiendo del tipo de sentencia y de los permisos del rol, un atacante también podría crear tablas definidas por el atacante mediante inyección CTAS y, cuando pueda leer posteriormente la tabla resultante, exfiltrar datos de otras tablas a las que el rol tenga acceso.
Todo el impacto está limitado por los permisos del workgroup de Athena / rol IAM que utiliza el cliente. Esta es una inyección en el plano de datos hacia el motor SQL de Athena; no permite ejecución de código en el host que ejecuta PyAthena, ni compromete a AWS en sí.
Quiénes están afectados: Aplicaciones que usan PyAthena < 3.35.4 con el DefaultParameterFormatter predeterminado (sustitución de parámetros pyformat / named en el lado del cliente) que (1) construyen una sentencia que no comienza con SELECT/WITH/INSERT/UPDATE/MERGE, en la práctica DELETE, CTAS, CREATE VIEW, DROP o ALTER, y (2) pasan datos influenciados por el atacante como parámetro de cadena a esa sentencia.
Quiénes no están afectados:
3.35.4 o posterior.SELECT/WITH/INSERT/UPDATE/MERGE; estas se enrutan al escapador seguro de duplicación de comillas.| Métrica | Valor | Fuente |
|---|---|---|
| Descargas, total histórico | 740.6M | pepy.tech/projects/pyathena |
| Descargas, últimos 30 días | 22.3M | pepy.tech |
| Descargas, últimas 24 horas | 221.0K | pepy.tech |
| Tasa de instalación sostenida | 8.95/segundo | pepy.tech |
| Dependiente destacado | dbt-athena importa _escape_hive y _escape_presto directamente desde pyathena.formatter | connections_legacy.py#L23-L27 |
DefaultParameterFormatter.format() selecciona la función de escape de cadenas únicamente a partir de la palabra clave inicial de la sentencia. Solo una lista de permitidos de prefijos recibe el escapador correcto para Trino; cualquier otra sentencia cae en el escape con barra invertida estilo Hive.
# 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}'"
El motor SQL de Amazon Athena es Trino (Presto en versiones anteriores del motor). En Trino, el único escape para una comilla simple dentro de un literal de cadena entre comillas simples es duplicarla (''); una barra invertida es un carácter literal. Por lo tanto, _escape_hive no neutraliza una comilla en absoluto para Athena: emite ... = 'missing\' OR 1=1 -- ', que Trino analiza como el literal de cadena 'missing\' seguido de OR 1=1 -- ', es decir, SQL controlado por el atacante.
El diseño es de fallo peligroso (fail-dangerous): incluye en la lista blanca la ruta segura y establece como predeterminado el escapador inseguro para todo lo demás. La corrección upstream invierte esto a un diseño de fallo seguro (fail-safe): predetermina el escapador de Trino; usa el escape estilo Hive solo para DDL genuino de Hive como CREATE DATABASE/DROP TABLE/MSCK REPAIR, trata CTAS y CREATE VIEW como Trino; y, adicionalmente, elimina los comentarios SQL iniciales para que un prefijo /* … */ DELETE … no pueda eludir la detección del tipo de sentencia.
_escape_hive no carece de sanitización, es sanitización. Es una rutina de escape correcta y bien formada para la gramática de literales de cadena de Hive, aplicada a un motor que usa la de Trino. Las herramientas de seguimiento de taint modelan la inyección SQL como datos no confiables que alcanzan un sumidero sin pasar por un escapador; aquí los datos pasan por un escapador en todas las rutas, y el escapador se ve exactamente como código de remediación porque es código de remediación, para el dialecto equivocado.
La corrección del dialecto no es una propiedad de taint, por lo que ninguna regla de taint la evalúa. El defecto es estructuralmente invisible para CodeQL, Semgrep, Snyk y Socket, no meramente pasado por alto por ellos, razón por la cual persistió en un paquete instalado aproximadamente nueve veces por segundo.
Un atacante necesita: