
Prueba de concepto de laboratorio para una falla de autorización en Apache Superset 6.1.0, donde un editor de dashboard con rol Gamma inserta SQL de dataset mediante la importación de dashboard v0 sin versionado, que se ejecuta al actualizar el gráfico.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · superset-g3-v0-import-sql-plant
Un compañero de trabajo con una cuenta normal de editor de paneles puede ocultar su propia consulta de base de datos dentro de un gráfico en el que otras personas ya confían. La próxima vez que alguien abra ese gráfico, la consulta se ejecuta contra la conexión de datos de la empresa. Pueden leer tablas a las que nunca se les dio acceso. No necesitan el permiso adicional de "ejecutar mi propio SQL" que se supone que controla esto. No pueden registrar un nuevo inicio de sesión de almacén de datos y no pueden ejecutar INSERT o DELETE a través de esta ruta del gráfico.
Clase: Privilegio residual Alcance: Remoto
Apache Superset 6.1.0 (c83fb2bb) - Apache Software Foundation
Gamma por defecto tiene can_write en Dashboard, por lo que POST /api/v1/dashboard/import/ está dentro de la matriz. El JSON que no es ZIP no tiene metadata.yaml, por lo que el despachador prueba v1 y luego recurre a v0 sin versión. decode_dashboards reconstituye SqlaTable. import_dataset busca por table_name + schema + database_id (desde params.database_name) y datasource.override(i_datasource) copia export_fields, incluido sql, sin raise_for_access y sin escritura en Dataset. Luego, POST /api/v1/chart/data compila ese SQL como el FROM virtual en la conexión analítica del dataset.
SQL de Gamma sin sql_lab. El PUT REST de Dataset sigue en 403. El ZIP v1 sigue vacío.
| ID | aún sin CVE |
| Clase | Privilegio residual (residual de CVE-2026-23982 / CVE-2024-26016) |
| Alcance | Remoto (HTTP autenticado; Gamma por defecto) |
| CWE | CWE-863, CWE-284 |
| CVSS | Alto: 7.6 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L |
| Producto | Apache Superset |
| Afectado | 6.1.0 (c83fb2bb1dcfac41ac51bcebd82471f4a7180d18); GitHub Latest GA en el momento de la búsqueda |
| Autenticación | Gamma por defecto (can_write en Dashboard). No sin autenticación. No Admin (fuera de alcance). |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Necesita una cuenta Gamma (o cualquier cuenta con escritura en Dashboard) y un dataset que pueda graficar después: el mismo table_name + schema en una Base de datos cuyo database_name esté en el JSON params. La concesión complementaria es sobre el objeto dataset, no sobre cada tabla que nombra el SQL plantado. raise_for_access de chart/data es sobre el objeto datasource (datasource_access / schema / propietario). get_from_clause solo rechaza has_mutation().
| Privilegio | Método / sink | Reconocimiento / integridad / disponibilidad | Probado en laboratorio o inferido |
|---|---|---|---|
| Sobrescribir el SQL virtual de un dataset con nombre coincidente | Gamma POST /api/v1/dashboard/import/ JSON no-ZIP; v0 import_dataset datasource.override copia export_fields incluyendo sql | Integridad (dataset.sql) | Probado en laboratorio. Import 200; dataset.sql = SELECT 'SUPERSET-G3-V0-IMPORT-SQL-PLANT-WITNESS' AS witness |
| Ejecutar ese SQL en la URI analítica del dataset | POST /api/v1/chart/data tras el plant; get_from_clause envuelve el sql almacenado como TextAsFrom | Reconocimiento (SELECT cualquier tabla que el usuario de la URI pueda leer) | Probado en laboratorio. 200; compilado FROM (SELECT 'SUPERSET-G3-V0-IMPORT-SQL-PLANT-WITNESS' AS witness) AS virtual_table |
| Ocultar la consulta en un gráfico que otros usuarios ya autorizados tienen abierto | Mismo plant; propietarios / otros principales con datasource_access ejecutan al refrescar | Integridad + reconocimiento (otras personas ejecutan el plant) | Inferido. El laboratorio Gamma ya tenía el permiso, por lo que se auto-ejecutó |
| Romper paneles que usaban el SQL antiguo | Reemplazar el sql de la tabla física con un SELECT del atacante | Disponibilidad | Inferido |
| Negativo: ejecución en SQL Lab | POST /api/v1/sqllab/execute/ | n/a | Probado en laboratorio. 403 |
| Negativo: sobrescritura REST de Dataset | PUT /api/v1/dataset/{id} con sql | n/a | Probado en laboratorio. 403; sql permaneció vacío (hermano de CVE-2026-23982) |
Negativo: ZIP v1 con metadata.yaml | mismo endpoint de importación, bundle ZIP | n/a | Probado en laboratorio. 200; sql permaneció vacío (clase CVE-2024-26016) |
Cerrado en este remanente: importación no autenticada; registrar una nueva URI de Database (la importación de dataset v0 no crea la conexión; la falta de params.database_name es DatabaseNotFoundError); DML a través de chart/data (has_mutation()); Gamma graficando un dataset que no puede leer (403); un table_name único recién insertado (PVM creado, no adjunto a Gamma). No es RCE. No es Admin. La separación del almacén de producción limita el SELECT a ese usuario de la URI. La URI analítica del laboratorio era el mismo Postgres que el metadatos, por lo que el acceso entre tablas incluyendo ab_user es inferido, no verificado con un segundo oráculo.
Fijé Apache Superset 6.1.0. Admin es equivalente a nivel de SO y está fuera de alcance. Busqué remanentes contra la propia matriz de roles de Apache: Gamma, Alpha, sql_lab, invitado embebido, Public.
La oleada de 6.0.0 ya cerró sqlExpression en chart/data (CVE-2026-23980), SQL REST de Dataset (CVE-2026-23982), y DO $$ / EXPLAIN ANALYZE en SQL Lab (CVE-2026-23984). La autorización de importación para ZIP versionado es CVE-2024-26016. El v0 sin versionar sigue siendo el último en command_versions porque "los archivos no están versionados."
import_ en Dashboard se mapea a escritura. El JSON no-ZIP es {filename: bytes} sin metadata.yaml. El despachador intenta v1, captura IncorrectVersionError, ejecuta v0. decode_dashboards convierte __SqlaTable__ en un modelo vivo. import_dataset busca y sobrescribe. export_fields incluye sql. En tiempo de consulta se trata el SQL almacenado como confiable: get_from_clause después de has_mutation().
Pistas falsas ya registradas: tratar el ZIP v1 como el remanente (sql permaneció vacío); PUT REST de Dataset (403); SQL Lab como Gamma (403); inflar a cualquier objeto Database (v0 no registra una URI); una reverse shell. Teatro. El testigo es el FROM virtual.
python3 superset-g3-v0-import-sql-plant-Abraxas-Labs.py
o:
cd lab
./run.sh