
PyAthena의 DefaultParameterFormatter를 통한 SQL 인젝션 (CVE-2026-65321)
심각도: Critical, CVSS v4.0 9.3 / CVSS v3.1 9.8 (CNA인 VulnCheck이 배정)
벡터 (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
벡터 (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
영향 범위: PyAthena <= 3.35.3 (3.35.3까지의 모든 버전)
수정 버전: 3.35.4
CWE: CWE-89 (SQL 명령에서 사용되는 특수 요소의 부적절한 중화, 'SQL 인젝션')
신고자: Rahul Karne
CNA: VulnCheck
공개일: 2026년 8월 3일
PyAthena는 SELECT 쿼리에서는 신뢰할 수 없는 입력을 올바르게 이스케이프했지만
DELETE 쿼리에서는 잘못 이스케이프했습니다.
Amazon Athena용으로 널리 사용되는 Python DB-API 클라이언트인 PyAthena는 명령문의 시작 키워드에 따라 문자열 이스케이프 루틴을 선택합니다. SELECT, WITH, INSERT, UPDATE, MERGE로 시작하는 명령문은 작은따옴표를 두 번 사용하여 중화하는('') Trino에 올바른 이스케이프를 적용받습니다. 그 외의 모든 명령문, 가장 일반적으로는 DELETE 또는 CREATE TABLE … AS SELECT(CTAS)가 Hive 스타일의 백슬래시 이스케이프(\')를 사용합니다. Athena의 엔진은 Trino이며, Trino는 작은따옴표 문자열 내부의 백슬래시를 일반 문자로 취급하므로 백슬래시 이스케이프는 아무것도 중화하지 못합니다. 이러한 명령문의 문자열 파라미터에 영향을 미칠 수 있는 공격자는 리터럴을 종료하고 임의의 SQL을 주입할 수 있으며, 인증이나 사용자 상호 작용이 필요 없습니다.
따라서 이 결함은 읽기 경로에는 존재하지 않으며, 가장 큰 피해를 줄 수 있는 파괴적인 명령문 유형에 정확히 존재합니다. 이 패키지는 한 달에 2,230만 회 다운로드됩니다.
PyAthena는 Amazon Athena용 서드파티 커뮤니티 클라이언트 라이브러리입니다. AWS 제품이 아니며, 이는 AWS 또는 Athena 자체의 취약점이 아닙니다.
취약한 명령문에 전달되는 문자열 파라미터를 제어하는 공격자는 의도된 문자열 리터럴을 벗어나 명령문의 로직을 변경할 수 있습니다. 가장 직접적이고 확실하게 입증 가능한 영향은 무단 데이터 삭제입니다: DELETE … WHERE token = %(token)s 쿼리에서 missing' OR 1=1 -- 같은 페이로드는 WHERE 조건을 무력화하고 Athena 워크그룹의 IAM 역할이 삭제할 수 있는 모든 행을 삭제합니다(예: Iceberg 테이블의 모든 행). 명령문 유형과 역할의 권한에 따라 공격자는 CTAS 주입을 통해 공격자가 정의한 테이블을 생성할 수도 있으며, 이후 결과 테이블을 읽을 수 있는 경우 역할이 접근할 수 있는 다른 테이블의 데이터를 유출할 수도 있습니다.
모든 영향은 클라이언트가 사용하는 Athena 워크그룹 / IAM 역할의 권한에 의해 제한됩니다. 이는 Athena의 SQL 엔진에 대한 데이터 플레인 주입이며, PyAthena를 실행하는 호스트에서의 코드 실행이나 AWS 자체의 손상을 가져오지 않습니다.
영향을 받는 대상: 기본 DefaultParameterFormatter(클라이언트 측 pyformat / named 파라미터 치환)를 사용하는 PyAthena < 3.35.4 애플리케이션 중 (1) SELECT/WITH/INSERT/UPDATE/MERGE로 시작하지 않는 명령문, 실제로는 DELETE, CTAS, CREATE VIEW, DROP, ALTER를 구성하고, (2) 공격자의 영향을 받는 데이터를 문자열 파라미터로 해당 명령문에 전달하는 경우.
영향을 받지 않는 대상:
3.35.4 이상을 사용하는 모든 사용자.SELECT/WITH/INSERT/UPDATE/MERGE로만 시작하는 애플리케이션. 이러한 명령문은 안전한 따옴표 두 번 이스케이퍼로 라우팅됩니다.| 지표 | 값 | 출처 |
|---|---|---|
| 누적 다운로드 수 | 740.6M | pepy.tech/projects/pyathena |
| 최근 30일 다운로드 수 | 22.3M | pepy.tech |
| 최근 24시간 다운로드 수 | 221.0K | pepy.tech |
| 지속 설치율 | 8.95/초 | pepy.tech |
| 주목할 만한 하위 의존 프로젝트 | dbt-athena는 pyathena.formatter에서 _escape_hive와 _escape_presto를 직접 임포트 | connections_legacy.py#L23-L27 |
DefaultParameterFormatter.format()는 명령문의 시작 키워드만으로 문자열 이스케이프 함수를 선택합니다. 허용 목록(allowlist)에 있는 접두사만 Trino에 올바른 이스케이퍼를 적용받으며, 그 외의 모든 명령문은 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}'"
Amazon Athena의 SQL 엔진은 Trino입니다 (이전 엔진 버전에서는 Presto). Trino에서 작은따옴표 문자열 리터럴 내부의 작은따옴표를 이스케이프하는 유일한 방법은 두 번 사용하는 것('')이며, 백슬래시는 리터럴 문자입니다. 따라서 _escape_hive는 Athena에서 따옴표를 전혀 중화하지 못하며, ... = 'missing\' OR 1=1 -- '을 생성합니다. Trino는 이를 문자열 리터럴 'missing\' 뒤에 OR 1=1 -- '가 이어지는 것으로 파싱합니다. 즉, 공격자가 제어하는 SQL입니다.
이 설계는 실패 시 위험(fail-dangerous)합니다: 안전한 경로를 허용 목록으로 지정하고 그 외의 모든 것은 안전하지 않은 이스케이퍼를 기본값으로 사용합니다. 업스트림 수정은 이를 실패 시 안전(fail-safe) 방식으로 반전시킵니다 (Trino 이스케이퍼를 기본값으로 사용하고 CREATE DATABASE/DROP TABLE/MSCK REPAIR와 같은 진정한 Hive DDL에만 Hive 이스케이프를 사용하며, CTAS와 CREATE VIEW는 Trino로 취급). 또한 앞부분의 SQL 주석을 제거하여 /* … */ DELETE … 접두사가 명령문 유형 감지를 우회하지 못하도록 합니다.
_escape_hive는 검증(sanitization)이 누락된 것이 아니라, 그 자체가 검증입니다. Hive의 문자열 리터럴 문법에 대해 올바르고 잘 구성된 이스케이프 루틴이지만, Trino 문법을 사용하는 엔진에 적용되었습니다. 오염 추적(taint-tracking) 도구는 SQL 인젝션을 이스케이퍼를 통과하지 않고 싱크(sink)에 도달하는 신뢰할 수 없는 데이터로 모델링합니다. 여기서 데이터는 모든 경로에서 이스케이퍼를 통과하며, 그 이스케이퍼는 잘못된 방언(dialect)에 대한 수정 코드이기 때문에 정확히 수정 코드처럼 보입니다.
방언의 정확성은 오염(taint) 속성이 아니므로 어떤 오염 규칙도 이를 평가하지 않습니다. 이 결함은 CodeQL, Semgrep, Snyk, Socket이 단순히 간과한 것이 아니라 구조적으로 보이지 않으며, 이것이 초당 약 9회 설치되는 패키지에서 오랫동안 지속된 이유입니다.
공격자에게 필요한 조건:
pyformat / named paramstyle)를 사용하는 PyAthena < 3.35.4 대상 애플리케이션.SELECT/WITH/INSERT/UPDATE/MERGE로 시작하지 않는 명령문, 실제로는 DELETE 또는 CTAS를 구성하는 코드 경로.숫자 파라미터와 안전한 이스케이퍼로 라우팅되는 모든 명령문은 이 결함으로 악용할 수 없습니다.
PoC는 실제의 수정되지 않은 PyAthena 포맷터(재구성이 아닌 공개된 PyPI 릴리스에서 임포트)를 호출하고 그 출력을 로컬 인메모리 DuckDB 데이터베이스에서 실행합니다. AWS 계정, 자격 증명, 네트워크 접근이 필요 없습니다. 전체 재현은 두 개의 명령으로 충분합니다:
pip install pyathena==3.35.2 duckdb
python poc_pyathena_cna_demo.py --no-pause
소스: poc_pyathena_cna_demo.py
녹화 데모: 데모 보기
DefaultParameterFormatter의 docstring은 SQL 인젝션을 방지하기 위해 파라미터를 이스케이프한다고 명시합니다. PoC는 해당 docstring을 출력한 다음, inspect.getsource를 통해 설치된 패키지에서 직접 _escape_presto, _escape_hive 및 접두사 선택 분기를 출력합니다. 따라서 독자는 권고(advisory)의 말을 믿는 대신 라이브러리 자체 소스의 모순을 직접 확인할 수 있습니다.
DELETE 조건 탈출공격자가 제어하는 파라미터: missing' OR 1=1 --
DELETE FROM sessions WHERE token = 'missing\' OR 1=1 -- '
Trino의 렉서는 작은따옴표 문자열 리터럴 내부의 작은따옴표에 대해 정확히 하나의 이스케이프만 인식합니다: 두 번 사용하는 것('')입니다. 백슬래시는 이스케이프 의미를 갖지 않습니다. 따라서 리터럴은 missing\ 다음의 따옴표에서 종료되며 OR 1=1 --은 SQL로 파싱됩니다. DuckDB도 이 속성을 공유하며, 두 개의 행이 시드된 sessions 테이블에 대한 결과는 다음과 같습니다:
Rows before executing generated SQL: 2
Rows after executing generated SQL: 0
WHERE 조건이 무력화되고 모든 행이 삭제됩니다.
공격자가 제어하는 파라미터: nobody' UNION SELECT secret FROM admin_credentials --
CREATE TABLE leaked AS SELECT name FROM users WHERE name = 'nobody\' UNION SELECT secret FROM admin_credentials -- '
주입된 UNION은 원래 명령문이 전혀 참조하지 않았던 테이블에서 행을 복사합니다. 데모에서는 admin_credentials의 DEMO_SECRET_VALUE가 공격자에게 보이는 leaked 테이블에 저장됩니다. Athena에서는 워크그룹의 IAM 역할이 읽을 수 있는 범위로 제한됩니다.
SELECT와 UPDATE로 라우팅된 동일한 페이로드는 _escape_presto에 도달하여 따옴표 두 번 사용으로 올바르게 중화됩니다:
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
두 페이로드 모두 문자열 리터럴 내부에 머무릅니다. SELECT는 0개의 행을 반환하고 UPDATE는 0개의 행을 변경하며, 탈출이 발생하지 않습니다. 이 대조 실험은 테스트 하네스가 정상이며 결함이 테스트 설정이 아닌 이스케이퍼 선택에 국한된다는 것을 입증합니다.
3.35.4에 대한 동일한 세 가지 페이로드는 모두 따옴표가 두 번 사용된 출력을 생성합니다:
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 -- '
수정 버전은 또한 앞부분의 주석 접두사가 있어도 견딥니다. 그렇지 않으면 명령문 유형 감지를 우회할 수 있습니다:
/* hi */ DELETE FROM sessions WHERE token = 'missing'' OR 1=1 -- '
모든 경우에 페이로드는 데이터로 포함되며 인젝션이 발생하지 않습니다.
PyAthena 3.35.4 이상으로 업그레이드하세요:
pip install --upgrade "pyathena>=3.35.4"
즉시 업그레이드할 수 없는 경우: SELECT/WITH/INSERT/UPDATE/MERGE로 시작하지 않는 모든 명령문에 신뢰할 수 없는 데이터를 파라미터로 전달하지 마세요. 파괴적인 명령문의 경우 서버 측에서 입력을 검증/허용 목록에 포함하거나 클라이언트 측 포맷터에 의존하지 않는 경로를 통해 작업을 수행하세요. 영향을 받는 버전에서는 이스케이퍼 선택을 변경하는 구성 플래그가 없습니다. 업그레이드가 확실한 해결 방법입니다.
이스케이퍼를 직접 임포트하는 프로젝트를 위한 참고 사항. 3.35.4 수정은 DefaultParameterFormatter.format() 내부의 이스케이퍼 선택을 변경합니다. _escape_hive 자체는 변경하지 않으며, 설계상 Hive에는 올바르고 Trino에는 잘못된 상태로 유지됩니다. 따라서 pyathena.formatter에서 _escape_hive 또는 _escape_presto를 임포트하여 자체적으로 명령문 유형 분기를 수행하는 하위 프로젝트는 PyAthena를 업그레이드해도 수정되지 않으며, 동일한 방언 문제에 대해 자체 분기 로직을 감사해야 합니다.
영향을 받는지 확인하는 방법:
pip show pyathena # check the installed version
pip-audit # flags CVE-2026-65321 once it propagates to the advisory feeds
VulnCheck(CNA)는 두 개의 점수를 발표했으며, 둘 다 Critical입니다: 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) 및 CVSS v3.1 = 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
이 공격은 원격이며 인증이 필요 없고 사용자 상호 작용이 없으며(AV:N/PR:N/UI:N), 공격자 측의 특별한 전제 조건이 필요 없습니다(AC:L/AT:N). 취약한 시스템에 대한 영향은 기밀성, 무결성, 가용성 모두에서 높습니다(v4.0에서 VC:H/VI:H/VA:H; v3.1에서 C:H/I:H/A:H): 주입된 DELETE는 데이터를 파괴할 수 있고, 주입된 CTAS/UNION SELECT는 워크그룹의 역할이 접근할 수 있는 데이터를 읽고 복사할 수 있습니다. v4.0 벡터에서 후속 시스템(subsequent-system) 지표는 모두 None입니다(SC:N/SI:N/SA:N). 이 결함은 Athena의 SQL 및 권한 부여 경계에 국한되며 호스트에서의 코드 실행이나 AWS 자체로의 피벗을 가져오지 않습니다. SC/SI/SA:N 세 가지 조합이 바로 v4.0이 최대치인 10.0이 아닌 9.3에 머무는 이유이며, "이것이 전체 시스템 손상을 의미하는가?"에 대한 정직한 답입니다. 그렇지 않습니다. v3.1 점수가 9.8에 도달하는 이유는 이진 범위(scope) 플래그(S:U/S:C)가 v4.0이 세 개의 별도 후속 시스템 지표로 나누는 것을 단일 비트로 압축하기 때문입니다. 두 점수는 모순이 아니라 일관됩니다.
사전에 밝힐 가치가 있는 한 가지 주의 사항: 실제 환경에서의 악용 가능성은 소비 애플리케이션이 신뢰할 수 없는 입력을 비-SELECT 파라미터화 명령문(DELETE/CTAS/DROP/ALTER)으로 라우팅해야 하며, 구체적인 폭발 반경(blast radius)은 Athena 워크그룹의 IAM 권한에 의해 제한됩니다. 기본 점수는 합리적인 최악의 경우를 모델링하며, 최소 권한 배포 환경은 덜 심각한 영향을 받습니다.
| 날짜 | 이벤트 |
|---|---|
| 2026년 7월 19일 | 취약점 식별 |
| 2026년 7월 20일 | 유지관리자에게 신고 |
| 2026년 7월 20일 | 유지관리자 확인 |
| 2026년 7월 31일 | 수정 커밋 |
| 2026년 7월 31일 | 패치 버전 3.35.4 릴리스 |
| 2026년 8월 2일 | VulnCheck가 CVE-2026-65321 배정 |
| 2026년 8월 3일 | 공개 공개 |
보안 연구원이자 IEEE Senior Member인 Rahul Karne이 발견하고 신고했습니다. 그의 연구는 의존성이 높은 오픈소스 패키지의 주입 및 입력 처리 결함에 중점을 두고 있으며, 이전 공개 사례로는 confluent-kafka(다운로드 11.2억 회), datamodel-code-generator(1.85억 회), ElementsKit Elementor Addons WordPress 플러그인(활성 설치 100만 이상)의 CVE가 있습니다.
연락처: [email protected] · GitHub: rahulreddykarne
미디어 문의: [email protected]. 요청 시 고해상도 데모 녹화, PoC 및 추가 기술 세부 정보를 제공합니다.