
Detaillierte Offenlegung von CVE-2026-41490 mit PoC, der eine nicht authentifizierte SQL-Injection in Dagster-Datenbank-I/O-Managern über dynamische Partitionsschlüssel demonstriert und die Integrationen DuckDB, Snowflake, BigQuery und DeltaLake betrifft.
Schweregrad: Hoch (CVSS 8.3) CWE: CWE-89 — SQL-Injection Betroffen: Dagster core <= 1.13.0 und Integrationspakete <= 0.29.0 Behoben in: Dagster core 1.13.1 und Integrationspakete 0.29.1 Sicherheitshinweis: GHSA-mjw2-v2hm-wj34 NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-41490 Danksagung: Romain Deperne
Jeder Dagster-Datenbank-I/O-Manager erstellt seine WHERE-Klausel, indem er Partitionsschlüsselwerte
per f-string direkt in SQL interpoliert. Wenn ein Asset DynamicPartitionsDefinition verwendet,
können Partitionsschlüssel zur Laufzeit über die GraphQL-API (addDynamicPartition) gesetzt werden —
die in der Standard-Webserver-Bereitstellung nicht authentifiziert ist. Ein bösartiger
Partitionsschlüssel fließt unescaped sowohl in SELECT- (Laden) als auch in DELETE-Abfragen
(Bereinigung) ein, was zu SQL-Injection gegen das zugrunde liegende Warehouse (Snowflake, BigQuery,
DuckDB, DeltaLake, …) führt.
Derselbe Helfer, _static_where_clause, ist über fünf I/O-Manager-Pakete hinweg kopiert. Jeder
davon macht Folgendes:
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 ist in einfache Anführungszeichen eingeschlossen, ohne Escaping. Die Frage war, ob partition jemals
angreiferkontrolliert ist. Bei statischen Partitionen ist es entwicklerdefiniert — nicht interessant. Bei
DynamicPartitionsDefinition werden die Schlüssel in der Metadaten-DB von Dagster gespeichert und zur Laufzeit
über die GraphQL-Mutation addDynamicPartition hinzugefügt. In der Standardbereitstellung von dagster-webserver ist GraphQL
unauthentifiziert, sodass ein Angreifer im Netzwerk den Partitionsschlüssel von Anfang bis Ende liefert.
Ich habe beide Enden der Kette bestätigt: Die GraphQL-Mutation akzeptiert beliebige Schlüsselzeichenfolgen ohne
Validierung, und der Schlüssel erreicht _static_where_clause unverändert über context.asset_partition_keys.
addDynamicPartition(... partitionKey: "') UNION SELECT username, password_hash FROM secret_table; --")launchRun(...) auf diese Partition abzielendSELECT/DELETE ... WHERE col in ('') UNION SELECT ... ; --')| Paket | Datei |
|---|---|
| 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 |
Sowohl der Ladepfad als auch der Bereinigungspfad nutzen ihn:
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
Partitionsschlüssel wurden als vertrauenswürdige Entwicklerkonstanten behandelt, aber DynamicPartitionsDefinition macht sie zu einer zur Laufzeit von außen setzbaren Eingabe. Die f-string-Interpolation, die für statische Schlüssel „sicher“ war, wird für dynamische Schlüssel zur Injection. Fix: parametrisierte Abfragen / korrektes Quoting von Identifiern und Literalen statt f-strings.
poc/poc_partition_sqli.py — zeigt die verwundbare Ausgabe von _static_where_clause für harmlose vs. bösartige Schlüssel, führt eine Live-DuckDB-Exfiltration auf UNION-Basis + DROP TABLE gegen eine vorbereitete Datenbank aus und gibt die exakten addDynamicPartition / launchRun-GraphQL-Payloads aus, die ein Angreifer sendet.
pip install duckdb # minimal; full chain: dagster dagster-duckdb pandas
python3 poc/poc_partition_sqli.py
Nicht authentifizierte (Standardkonfiguration) SQL-Injection gegen das Data Warehouse, das Dagster orchestriert – beliebiges Lesen anderer Tabellen und destruktive Schreibvorgänge. Dagster steht im Zentrum von Datenplattformen, daher reicht dies bis zum sensibelsten Speicher im Stack.
Verantwortungsvoll über GitHub Security Advisory offengelegt. PoC nach dem Fix veröffentlicht.