
DuckDB, Snowflake, BigQuery और DeltaLake एकीकरणों को प्रभावित करने वाले, गतिशील विभाजन कुंजियों के माध्यम से Dagster डेटाबेस I/O प्रबंधकों में अनधिप्रमाणित SQL इंजेक्शन को प्रदर्शित करने वाला PoC सहित CVE-2026-41490 का विस्तृत खुलासा।
गंभीरता: उच्च (CVSS 8.3) CWE: CWE-89 — SQL इंजेक्शन प्रभावित: Dagster core <= 1.13.0 और एकीकरण पैकेज <= 0.29.0 में ठीक किया गया: Dagster core 1.13.1 और एकीकरण पैकेज 0.29.1 सलाह: GHSA-mjw2-v2hm-wj34 NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-41490 श्रेय: Romain Deperne
प्रत्येक Dagster डेटाबेस I/O प्रबंधक अपने WHERE क्लॉज़ का निर्माण पार्टीशन कुंजी मानों को सीधे SQL में
f-string-इंटरपोलेट करके करता है। जब कोई एसेट DynamicPartitionsDefinition का उपयोग करता है, तो पार्टीशन कुंजियाँ
रनटाइम पर GraphQL API (addDynamicPartition) के माध्यम से सेट की जा सकती हैं — जो डिफ़ॉल्ट webserver
परिनियोजन में अनप्रमाणित है। एक दुर्भावनापूर्ण पार्टीशन कुंजी बिना एस्केप किए SELECT (लोड) और
DELETE (क्लीनअप) दोनों क्वेरीज़ में प्रवाहित होती है, जिससे अंतर्निहित वेयरहाउस (Snowflake, BigQuery,
DuckDB, DeltaLake, …) के विरुद्ध SQL इंजेक्शन होता है।
एक ही सहायक फ़ंक्शन, _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 एकल उद्धरण चिह्नों में लपेटा गया है और इसमें कोई escaping नहीं है। प्रश्न यह था कि क्या partition कभी
हमलावर-नियंत्रित होता है। static partitions के लिए यह डेवलपर-परिभाषित होता है — दिलचस्प नहीं।
DynamicPartitionsDefinition के लिए keys Dagster के metadata DB में संग्रहीत होती हैं और runtime पर
addDynamicPartition GraphQL mutation के माध्यम से जोड़ी जाती हैं। डिफ़ॉल्ट dagster-webserver deployment में GraphQL
अप्रमाणित (unauthenticated) है, इसलिए नेटवर्क पर मौजूद हमलावर end-to-end partition key प्रदान करता है।
मैंने श्रृंखला के दोनों सिरों की पुष्टि की: GraphQL mutation बिना किसी
validation के मनमाने key strings स्वीकार करता है, और key context.asset_partition_keys के माध्यम से शब्दशः _static_where_clause तक पहुँचती है।
addDynamicPartition(... partitionKey: "') UNION SELECT username, password_hash FROM secret_table; --")launchRun(...)SELECT/DELETE ... WHERE col in ('') UNION SELECT ... ; --') बनाता है| Package | File |
|---|---|
| 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 |
load और cleanup दोनों paths इसे उपभोग करते हैं:
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
Partition keys को विश्वसनीय डेवलपर स्थिरांक माना जाता था, लेकिन DynamicPartitionsDefinition उन्हें रनटाइम, बाहरी रूप से सेट करने योग्य इनपुट में बदल देता है। f-string इंटरपोलेशन जो स्थिर keys के लिए "सुरक्षित" था, गतिशील keys के लिए इंजेक्शन बन जाता है। ठीक करें: f-strings के बजाय parameterized queries / उचित identifier+literal quoting।
poc/poc_partition_sqli.py — सामान्य बनाम दुर्भावनापूर्ण keys के लिए कमजोर _static_where_clause आउटपुट दिखाता है, एक seeded डेटाबेस के विरुद्ध लाइव DuckDB UNION-आधारित exfiltration + DROP TABLE चलाता है, और वे सटीक addDynamicPartition / launchRun GraphQL payloads प्रिंट करता है जो एक हमलावर भेजता है।
pip install duckdb # minimal; full chain: dagster dagster-duckdb pandas
python3 poc/poc_partition_sqli.py
डेटा वेयरहाउस के विरुद्ध अनप्रमाणित (डिफ़ॉल्ट कॉन्फ़िग) SQL इंजेक्शन, जिसे Dagster ऑर्केस्ट्रेट करता है — अन्य तालिकाओं का मनमाना पठन और विनाशकारी राइट। Dagster डेटा प्लेटफ़ॉर्म के केंद्र में स्थित है, इसलिए यह स्टैक के सबसे संवेदनशील स्टोर तक पहुँचता है।
GitHub Security Advisory के माध्यम से ज़िम्मेदारीपूर्वक प्रकट किया गया। PoC फ़िक्स के बाद प्रकाशित।