
Injeção de SQL em PyAthena via DefaultParameterFormatter (CVE-2026-65321)
Gravidade: Crítica, CVSS v4.0 9.3 / CVSS v3.1 9.8 (atribuída pela VulnCheck, a CNA)
Vetor (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
Vetor (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Afetados: PyAthena <= 3.35.3 (todas as versões até 3.35.3)
Corrigido em: 3.35.4
CWE: CWE-89 (Neutralização Incorreta de Elementos Especiais usados em um Comando SQL, 'Injeção de SQL')
Reportado por: Rahul Karne
CNA: VulnCheck
Publicado: 3 de agosto de 2026
O PyAthena escapava corretamente a entrada não confiável em consultas e incorretamente em consultas .
SELECTDELETEO PyAthena, o amplamente utilizado cliente Python DB-API para Amazon Athena,
seleciona sua rotina de escape de strings com base na palavra-chave inicial da
instrução. Instruções que começam com SELECT, WITH, INSERT, UPDATE ou
MERGE recebem escape correto para o Trino, no qual uma aspa simples é
neutralizada pela sua duplicação (''). Qualquer outra instrução, mais
comumente um DELETE ou um CREATE TABLE … AS SELECT (CTAS), cai no escape com
barra invertida no estilo Hive (\'). O mecanismo do Athena é o Trino, que
trata uma barra invertida dentro de uma string entre aspas simples como um
caractere comum, portanto o escape com barra invertida não neutraliza nada. Um
atacante que consiga influenciar um parâmetro de string em tal instrução pode
terminar o literal e injetar SQL arbitrário, sem autenticação e sem interação do
usuário.
A falha está, portanto, ausente do caminho de leitura e presente exatamente nos tipos de instrução destrutivos onde causa mais dano. O pacote é baixado 22,3 milhões de vezes por mês.
O PyAthena é uma biblioteca cliente comunitária de terceiros para Amazon Athena. Não é um produto AWS, e esta não é uma vulnerabilidade na AWS ou no próprio Athena.
Um atacante que controle um parâmetro de string passado a uma instrução
vulnerável pode sair do literal de string pretendido e alterar a lógica da
instrução. O impacto mais direto e confiavelmente demonstrável é a exclusão
não autorizada de dados: uma carga útil como missing' OR 1=1 -- em uma
consulta DELETE … WHERE token = %(token)s neutraliza o predicado WHERE e
exclui todas as linhas que a função IAM do workgroup do Athena tem permissão de
excluir (por exemplo, todas as linhas de uma tabela Iceberg). Dependendo do tipo
de instrução e das permissões da função, um atacante também pode ser capaz de
criar tabelas definidas pelo atacante via injeção de CTAS e, onde puder
posteriormente ler a tabela resultante, exfiltrar dados de outras tabelas às
quais a função tem acesso.
Todo o impacto é limitado pelas permissões do workgroup do Athena / função IAM que o cliente utiliza. Esta é uma injeção no plano de dados no mecanismo SQL do Athena; ela não resulta em execução de código no host que executa o PyAthena, nem compromete a própria AWS.
Quem é afetado: aplicações que usam PyAthena < 3.35.4 com o
DefaultParameterFormatter padrão (substituição de parâmetros pyformat /
named no lado do cliente) que (1) constroem uma instrução que não começa com
SELECT/WITH/INSERT/UPDATE/MERGE, na prática DELETE, CTAS,
CREATE VIEW, DROP ou ALTER, e (2) passam dados influenciados pelo atacante
como um parâmetro de string para essa instrução.
Quem não é afetado:
3.35.4 ou posterior.SELECT/WITH/INSERT/UPDATE/MERGE, estas são roteadas para o escapador
seguro de duplicação de aspas.| Métrica | Valor | Fonte |
|---|---|---|
| Downloads, todos os tempos | 740,6M | pepy.tech/projects/pyathena |
| Downloads, últimos 30 dias | 22,3M | pepy.tech |
| Downloads, últimas 24 horas | 221,0K | pepy.tech |
| Taxa de instalação sustentada | 8,95/segundo | pepy.tech |
| Downstream notável | dbt-athena importa _escape_hive e _escape_presto diretamente de pyathena.formatter | connections_legacy.py#L23-L27 |
DefaultParameterFormatter.format() seleciona a função de escape de strings
puramente a partir da palavra-chave inicial da instrução. Apenas uma lista de
permissão de prefixos recebe o escapador correto para o Trino; qualquer outra
instrução cai no escape com barra invertida no 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}'"
O mecanismo SQL da Amazon Athena é o Trino (Presto em versões anteriores do
mecanismo). No Trino, o único escape para uma aspa simples dentro de um literal
de string entre aspas simples é duplicá-la (''); uma barra invertida é um
caractere literal. Portanto, _escape_hive não neutraliza uma aspa de forma
alguma para o Athena; ele emite ... = 'missing\' OR 1=1 -- ', que o Trino
interpreta como o literal de string 'missing\' seguido por OR 1=1 -- ', ou
seja, SQL controlado pelo atacante.
O projeto é perigoso-em-falha: ele usa lista de permissão para o caminho
seguro e define como padrão tudo o mais para o escapador inseguro. A correção
upstream inverte isso para seguro-em-falha (padrão para o escapador do Trino;
usa escape Hive apenas para DDL Hive genuíno, como CREATE DATABASE/DROP TABLE/MSCK REPAIR, enquanto trata CTAS e CREATE VIEW como Trino) e,
adicionalmente, remove comentários SQL iniciais para que um prefixo /* … */ DELETE … não possa derrotar a detecção do tipo de instrução.
_escape_hive não é falta de sanitização, ele é sanitização. É uma rotina de
escape correta e bem formada para a gramática de literais de string do Hive,
aplicada a um mecanismo que usa a do Trino. Ferramentas de rastreamento de fluxo
modelam injeção de SQL como dados não confiáveis alcançando um sumidouro sem
passar por um escapador; aqui os dados passam por um escapador em todos os
caminhos, e o escapador parece exatamente com código de remediação porque é
código de remediação, para o dialeto errado.
Correção de dialeto não é uma propriedade de rastreamento de fluxo, portanto nenhuma regra de taint a avalia. O defeito é estruturalmente invisível para CodeQL, Semgrep, Snyk e Socket, em vez de meramente negligenciado por eles, e é por isso que persistiu em um pacote instalado aproximadamente nove vezes por segundo.
Um atacante precisa de:
< 3.35.4 com o formatador de parâmetros
padrão do lado do cliente (pyformat / named paramstyle).SELECT/WITH/INSERT/UPDATE/MERGE, na prática DELETE ou CTAS.Parâmetros numéricos e qualquer instrução roteada para o escapador seguro não são exploráveis por meio desta falha.
A PoC chama o formatador real e não modificado do PyAthena (importado do lançamento publicado no PyPI, não uma reconstrução) e executa sua saída contra um banco de dados DuckDB local em memória. Sem conta AWS, sem credenciais, sem acesso à rede. A reprodução completa leva dois comandos:
pip install pyathena==3.35.2 duckdb
python poc_pyathena_cna_demo.py --no-pause
Fonte: poc_pyathena_cna_demo.py
Demonstração gravada: Assista à demonstração
O docstring de DefaultParameterFormatter afirma que ele escapa parâmetros para
prevenir injeção de SQL. A PoC imprime esse docstring e, em seguida, imprime
_escape_presto, _escape_hive e o ramo de seleção de prefixo diretamente do
pacote instalado via inspect.getsource, para que o leitor veja a contradição no
código-fonte da própria biblioteca, em vez de aceitar a palavra do advisory.
DELETEParâmetro controlado pelo atacante: missing' OR 1=1 --
DELETE FROM sessions WHERE token = 'missing\' OR 1=1 -- '
O lexer do Trino reconhece exatamente um escape para uma aspa simples dentro de
um literal de string entre aspas simples: duplicá-la (''). Uma barra invertida
não carrega significado de escape. Portanto, o literal termina na aspa que segue
missing\, e OR 1=1 -- é interpretado como SQL. O DuckDB compartilha essa
propriedade e, contra uma tabela sessions semeada com duas linhas, o resultado
é:
Rows before executing generated SQL: 2
Rows after executing generated SQL: 0
O predicado WHERE é neutralizado e todas as linhas são excluídas.
Parâmetro controlado pelo atacante: nobody' UNION SELECT secret FROM admin_credentials --
CREATE TABLE leaked AS SELECT name FROM users WHERE name = 'nobody\' UNION SELECT secret FROM admin_credentials -- '
O UNION injetado copia uma linha de uma tabela à qual a instrução original
nunca fez referência. Na demonstração, DEMO_SECRET_VALUE de
admin_credentials vai parar na tabela leaked visível ao atacante. No Athena,
isso é limitado pelo que a função IAM do workgroup pode ler.
As mesmas cargas úteis roteadas através de SELECT e UPDATE alcançam
_escape_presto e são corretamente neutralizadas pela duplicação de aspas:
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
Ambas as cargas úteis permanecem dentro do literal de string. SELECT retorna
zero linhas e UPDATE altera zero linhas, sem fuga. Esses controles estabelecem
que o ambiente de teste é sólido e que o defeito é específico da seleção do
escapador, não da configuração do teste.
As mesmas três cargas úteis contra 3.35.4 produzem todas saída com aspas duplicadas:
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 -- '
A correção também sobrevive a um prefixo de comentário inicial, que de outra forma derrotaria a detecção do tipo de instrução:
/* hi */ DELETE FROM sessions WHERE token = 'missing'' OR 1=1 -- '
Em todos os casos, a carga útil é contida como dado e nenhuma injeção ocorre.
Atualize para o PyAthena 3.35.4 ou posterior:
pip install --upgrade "pyathena>=3.35.4"
Se você não puder atualizar imediatamente: evite passar dados não confiáveis
como parâmetros para qualquer instrução que não comece com
SELECT/WITH/INSERT/UPDATE/MERGE. Para instruções destrutivas, valide/
use lista de permissão na entrada no lado do servidor ou execute a operação por
um caminho que não dependa do formatador do lado do cliente. Não há sinalizador
de configuração que altere a seleção do escapador nas versões afetadas;
atualizar é a correção confiável.
Nota para projetos que importam os escapadores diretamente. A correção
3.35.4 altera a seleção do escapador dentro de
DefaultParameterFormatter.format(). Ela não altera o _escape_hive em si, que
permanece correto-para-Hive e errado-para-Trino por projeto. Qualquer projeto
downstream que importe _escape_hive ou _escape_presto de pyathena.formatter
e faça seu próprio despacho de tipo de instrução não é remediado pela
atualização do PyAthena e deve auditar sua própria lógica de despacho quanto à
mesma questão de dialeto.
Como verificar se você é afetado:
pip show pyathena # check the installed version
pip-audit # flags CVE-2026-65321 once it propagates to the advisory feeds
A VulnCheck (a CNA) publicou duas pontuações, ambas Críticas: 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).
O ataque é remoto e não autenticado, sem interação do usuário
(AV:N/PR:N/UI:N) e não requer pré-condições especiais do lado do atacante
(AC:L/AT:N). O impacto no sistema vulnerável é alto em confidencialidade,
integridade e disponibilidade (VC:H/VI:H/VA:H na v4.0; C:H/I:H/A:H na v3.1):
um DELETE injetado pode destruir dados, e um CTAS/UNION SELECT injetado pode
ler e copiar dados aos quais a função do workgroup tem acesso. No vetor v4.0, as
métricas de sistema subsequente são todas None (SC:N/SI:N/SA:N); a falha está
confinada ao SQL do Athena e à fronteira de autorização e não resulta em
execução de código no host nem em um pivô para a própria AWS. Esse trio
SC/SI/SA:N é exatamente por que a v4.0 se situa em 9.3 em vez de um 10.0
máximo, e é a resposta honesta a "isso significa comprometimento total do
sistema?" — não. A pontuação v3.1 atinge 9.8 porque seu sinalizador binário de
escopo (S:U/S:C) colapsa em um único bit o que a v4.0 divide em três métricas
separadas de sistema subsequente; as duas pontuações são consistentes, não
contraditórias.
Uma ressalva que vale a pena declarar proativamente: a explorabilidade no mundo
real requer que a aplicação consumidora roteie entrada não confiável para uma
instrução parametrizada que não seja SELECT (DELETE/CTAS/DROP/ALTER), e o
raio concreto de explosão é limitado pelas permissões IAM do workgroup do
Athena. A pontuação base modela o pior caso razoável; uma implantação com
privilégio mínimo é afetada de forma menos severa.
| Data | Evento |
|---|---|
| 19 de julho de 2026 | Vulnerabilidade identificada |
| 20 de julho de 2026 | Reportada ao mantenedor |
| 20 de julho de 2026 | Mantenedor confirmou |
| 31 de julho de 2026 | Correção commitada |
| 31 de julho de 2026 | Versão corrigida 3.35.4 lançada |
| 2 de agosto de 2026 | CVE-2026-65321 atribuída pela VulnCheck |
| 3 de agosto de 2026 | Divulgação pública |
Descoberta e reportada por Rahul Karne, pesquisador de segurança e Membro Sênior do IEEE. Sua pesquisa concentra-se em falhas de injeção e tratamento de entrada em pacotes open-source de alta dependência; divulgações anteriores incluem CVEs no confluent-kafka (1,12B downloads), datamodel-code-generator (185M) e no plugin WordPress ElementsKit Elementor Addons (1M+ instalações ativas).
Contato: [email protected] · GitHub: rahulreddykarne
Consultas da imprensa: [email protected]. Gravação da demonstração em alta resolução, PoC e detalhes técnicos adicionais disponíveis mediante solicitação.