
Apache Spark 4.2.0 - Élevé - À distance - Résidu de privilèges - table de sources de données Python globale au processus
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
Classe : Résidu de privilège Portée : Distante
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders est une map JVM-globale où le premier arrivé gagne. SPARK-45917 enregistre automatiquement les sources de données Python pyspark_* depuis le sys.path du worker de lookup. L'isolation des sessions Connect maintient les artefacts sous SparkFiles/<sessionUUID>/, et le registerPython par session reste sur runtimeDataSourceBuilders. Le premier lookup place tout de même le répertoire SparkFiles de la session déclencheuse sur le sys.path, sérialise (pickle) chaque DefaultSource, et met en cache ces classes dans l'objet compagnon. Les read.format(name) / USING name ultérieurs en Connect SQL (ou Hive Thrift SQL) exécutent cette classe en tant qu'UID Spark sans envoyer de proto Python.
Le planteur est un tenant Python Connect qui est déjà autorisé à utiliser Python. La victime est un tenant SQL uniquement sur le même driver Connect. Les alias Java (parquet, csv, json, orc, jdbc) restent sur le chemin Java ServiceLoader. Les plantations ultérieures après que la map est remplie ne rafraîchissent pas. AC:H correspond au premier lookup plus un nom qui n'est pas un alias Java.
| ID | pas encore de CVE |
| Classe | Résidu de privilège (source de données Python inter-sessions en tant qu'UID Spark ; pas une soumission de job non authentifiée) |
| Portée | Distante (driver Spark Connect partagé) |
| CWE | CWE-269, CWE-502 |
| CVSS | Élevé : 8.8 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Produit | Apache Spark |
| Affecté | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) registre statique des sources de données Python |
| Auth | Tenant Python Connect (planteur) plus un utilisateur ultérieur SQL uniquement Connect / Hive Thrift SQL (victime) |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18970 uniquement. Fichier témoin. Gadget de la map compagnon. |
Maintenir une session Python Spark Connect sur un driver partagé. Planter pyfiles/pyspark_<name>.py dont le DefaultSource.name() est un nom court de connecteur absent (delta, iceberg, hudi, ...). Déclencher le premier lookup de source de données Python sur cette session. Toute session ultérieure dans la même JVM qui fait read.format(name) ou SQL USING name instancie la classe sérialisée en tant qu'UID Spark sur le worker planificateur du driver et sur les exécuteurs.
C'est l'ensemble complet des privilèges. Le planteur exécute déjà Python. Le résidu est qu'un tenant SQL uniquement, qui n'a jamais envoyé de proto REGISTER_DATA_SOURCE / Python UDF, exécute tout de même cette classe.
| Méthode | Sink | Privilège |
|---|---|---|
Planteur : AddArtifacts nom pyfiles/pyspark_<name>.py puis premier format(name) / lookup | compagnon DataSourceManager.dataSourceBuilders (objet DataSourceManager.scala) plus lookup_data_sources.py iter_modules de pyspark_* | Met en cache le DefaultSource sérialisé de la session déclencheuse à l'échelle de la JVM. Lab : layer='static' trigger='attacker' first-wins=yes. |
Victime : SQL USING <name> / spark.read.format(name) sur une session qui n'a jamais planté | PythonDataSourceV2 createDataSourceInPython | Exécute cette classe en tant qu'UID Spark. Lab : planted_by='attacker' witness_written=True static-cross-session=yes. Le fichier oracle contient SPARK-PYTHON-DS-STATIC-WITNESS. |
| Méthode | Sink | Résultat |
|---|---|---|
| Plantation ultérieure d'un second nom après que la map est remplie | même map statique | Fermé. Le premier arrivé gagne. Lab : later-lookup found=False, le compagnon contient toujours uniquement delta. |
registerPython("alpha") par session | runtimeDataSourceBuilders | Isolé. Le clone copie uniquement le runtime. Lab : runtime-isolated=yes. La lecture de alpha par la victime est found=False. |
Alias Java parquet | Java DataSourceRegister ServiceLoader | Fermé. Lab : java-alias-closed=yes layer='java'. Idem pour csv/json/orc/jdbc. |
| Méthode | Sink | Privilège |
|---|---|---|
Victime USING après plantation | workers Python du driver + exécuteur | Code à l'échelle du cluster en tant qu'UID Spark. Le lab écrit un fichier témoin au lieu d'une charge utile. |
| Même chemin | crash / spin du worker | Disponibilité en tant qu'UID Spark. Pas le niveau Élevé. Le niveau Élevé est l'exécution de code. |
Hive Thrift SQL USING sur une SparkSession partagée | même map statique | Même résidu si Thrift et Connect partagent la JVM du driver. Non exercé. |
parquet / csv / json / orc / jdbc. ServiceLoader gagne.concatenatePaths rejette .. et les noms absolus).pythonExec fourni par le client. Le planificateur Connect utilise PYSPARK_PYTHON / python3 du serveur.registerPython. La map runtime est par instance de DataSourceManager.pyspark local mono-utilisateur avec une seule session. Il n'y a pas d'autre tenant.J'ai épinglé apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Vingt-huit recherches dans le code source. La soumission de job exécutant du code utilisateur est par conception. L'historique Jackson, le shell UI, l'usurpation locale PySpark, et le chemin de réception RPC standalone sont fermés sur cette analyse. Les artefacts Connect isolent les pyfiles sous un UUID de session. Le registerPython runtime est par session. L'objet compagnon ne l'est pas.
SPARK-45917 enregistre automatiquement les paquets pyspark_* depuis PYTHONPATH. C'est prévu pour les connecteurs installés par l'opérateur. Le résidu est que le worker de lookup fait aussi un sys.path.insert du répertoire SparkFiles de la session déclencheuse (worker_util.setup_spark_files), puis sérialise tout DefaultSource pyspark_* qu'il trouve dans une map globale au processus qui ne se rafraîchit jamais.
Fausse piste : traiter l'isolation de registerPython comme la barrière complète entre tenants. Le runtime est isolé. Le statique ne l'est pas.
cd lab
./run.sh