
Detailed CVE-2026-41490 disclosure with PoC demonstrating unauthenticated SQL injection in Dagster database I/O managers via dynamic partition keys, affecting DuckDB, Snowflake, BigQuery, and DeltaLake integrations.
심각도: 높음 (CVSS 8.x — AV:N/AC:L/PR:L/UI:N + C:H/I:H/A:H)
CWE: CWE-89 — SQL 인젝션
영향받는: dagster 데이터베이스 I/O 관리자 통합 — dagster-duckdb, dagster-snowflake, dagster-gcp (BigQuery), dagster-deltalake, dagster-snowflake-polars (≤ 1.12.20)
권고: GHSA-mjw2-v2hm-wj34
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-41490
크레딧: Romain Deperne
모든 Dagster 데이터베이스 I/O 관리자는 WHERE 절을 파티션 키 값을 하여 구성합니다. 에셋이 을 사용하는 경우, 파티션 키는 GraphQL API()를 통해 런타임에 설정될 수 있습니다. 이 API는 기본 웹서버 배포에서 인증되지 않습니다. 악의적인 파티션 키는 이스케이프되지 않은 상태로 (로드) 및 (정리) 쿼리 모두에 유입되어, 백업 웨어하우스(Snowflake, BigQuery, DuckDB, DeltaLake 등)에 대해 SQL 인젝션을 초래합니다.
DynamicPartitionsDefinitionaddDynamicPartitionSELECTDELETE동일한 헬퍼 함수 _static_where_clause가 다섯 개의 I/O 관리자 패키지에 복사되어 있습니다. 각 패키지는 다음과 같이 수행합니다:
def _static_where_clause(table_partition):
partitions = ", ".join(f"'{partition}'" for partition in table_partition.partitions)
return f"""{table_partition.partition_expr} in ({partitions})"""
partition은 이스케이프 없이 단일 따옴표로 감싸집니다. 문제는 partition이 공격자에 의해 제어될 수 있는지 여부였습니다. 정적 파티션의 경우 개발자가 정의하므로 — 흥미롭지 않습니다. DynamicPartitionsDefinition 의 경우 키는 Dagster의 메타데이터 DB에 저장되며, addDynamicPartition GraphQL 뮤테이션을 통해 런타임에 추가됩니다. 기본 dagster-webserver 배포에서는 GraphQL이 인증되지 않으므로, 네트워크상의 공격자가 파티션 키를 종단간 공급할 수 있습니다.
두 체인 끝을 모두 확인했습니다: GraphQL 뮤테이션은 검증 없이 임의의 키 문자열을 허용하며, 키는 context.asset_partition_keys를 통해 _static_where_clause에 그대로 도달합니다.
addDynamicPartition(... partitionKey: "') UNION SELECT username, password_hash FROM secret_table; --")launchRun(...) targeting that partitionSELECT/DELETE ... WHERE col in ('') UNION SELECT ... ; --')를 구성합니다.| 패키지 | 파일 |
|---|---|
| dagster-duckdb | io_manager.py:340-342 |
| dagster-snowflake | snowflake_io_manager.py:434-436 |
| dagster-gcp (BigQuery) | bigquery/io_manager.py:472-474 |
| dagster-deltalake | io_manager.py:265-267 |
| dagster-snowflake-polars | snowflake_polars_type_handler.py:74 |
로드 및 정리 경로 모두 이를 사용합니다:
query = f"SELECT {col_str} FROM {schema}.{table} WHERE\n" + _partition_where_clause(...) # read
query = f"DELETE FROM {schema}.{table} WHERE\n" + _partition_where_clause(...) # write
파티션 키는 신뢰할 수 있는 개발자 상수로 취급되었지만, DynamicPartitionsDefinition은 이를 런타임에 외부에서 설정 가능한 입력으로 전환합니다. 정적 키에 대해 "안전"했던 f-스트링 보간은 동적 키에 대해 인젝션이 됩니다. 수정: f-스트링 대신 매개변수화된 쿼리 / 적절한 식별자+리터럴 인용 사용.
poc/poc_partition_sqli.py — 양호한 키와 악성 키에 대한 취약한 _static_where_clause 출력을 보여주고, 시드된 데이터베이스에 대해 실제 DuckDB UNION 기반 데이터 유출 + DROP TABLE을 실행하며, 공격자가 보내는 정확한 addDynamicPartition / launchRun GraphQL 페이로드를 출력합니다.
pip install duckdb # minimal; full chain: dagster dagster-duckdb pandas
python3 poc/poc_partition_sqli.py
인증되지 않은 (기본 구성) SQL 인젝션으로 Dagster가 오케스트레이션하는 데이터 웨어하우스를 대상으로 함 — 다른 테이블의 임의 읽기 및 파괴적 쓰기 가능. Dagster는 데이터 플랫폼의 중심에 위치하므로, 이는 스택에서 가장 민감한 저장소에 도달합니다.
GitHub Security Advisory를 통해 책임 있게 공개됨. 수정 후 PoC 게시.