
Apache Spark 4.2.0 - Alta - Remota - Privilegio residual - mapa global de procesos de Python Data Source
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
Clase: Privilegio residual Alcance: Remoto
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders es un mapa global de la JVM en el que gana el primero. SPARK-45917 autorregistra los orígenes de datos de Python pyspark_* desde el sys.path del worker de búsqueda. El aislamiento de sesión de Connect mantiene los artefactos bajo SparkFiles/<sessionUUID>/, y el registerPython por sesión permanece en runtimeDataSourceBuilders. La primera búsqueda aún coloca el directorio SparkFiles de la sesión que la desencadena en sys.path, serializa cada DefaultSource y almacena en caché esas clases en el objeto compañero. Las posteriores consultas SQL de Connect (o SQL de Hive Thrift) read.format(name) / USING name ejecutan esa clase como el UID de Spark sin enviar el proto de Python.
El plantador es un inquilino de Connect Python al que ya se le permite Python. La víctima es un inquilino solo de SQL en el mismo driver de Connect. Los alias de Java (parquet, csv, json, orc, jdbc) permanecen en la ruta del ServiceLoader de Java. Las plantaciones posteriores una vez llenado el mapa no se refrescan. AC:H es la primera búsqueda más un nombre que no es un alias de Java.
| ID | aún sin CVE |
| Clase | Privilegio residual (origen de datos de Python entre sesiones como UID de Spark; no es envío de trabajos sin autenticación) |
| Alcance | Remoto (driver de Spark Connect compartido) |
| CWE | CWE-269, CWE-502 |
| CVSS | Alto: 8.8 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Producto | Apache Spark |
| Afectado | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) registro estático de orígenes de datos de Python |
| Autenticación | Inquilino de Connect Python (plantador) más un usuario posterior solo de SQL de Connect / SQL de Hive Thrift (víctima) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1:18970. Archivo testigo. Gadget del mapa compañero. |
Mantener una sesión de Spark Connect Python en un driver compartido. Plantar pyfiles/pyspark_<name>.py cuyo DefaultSource.name() sea un nombre corto de conector ausente (delta, iceberg, hudi, ...). Desencadenar la primera búsqueda de origen de datos de Python en esa sesión. Cualquier sesión posterior en la misma JVM que haga read.format(name) o SQL USING name instancia la clase serializada como el UID de Spark en el worker planificador del driver y en los ejecutores.
Ese es el conjunto completo de privilegios. El plantador ya ejecuta Python. Lo residual es que un inquilino solo de SQL, que nunca envió REGISTER_DATA_SOURCE / proto de UDF de Python, aún ejecuta esa clase.
| Método | Sumidero | Privilegio |
|---|---|---|
Plantador: AddArtifacts con nombre pyfiles/pyspark_<name>.py y luego el primer format(name) / búsqueda | compañero DataSourceManager.dataSourceBuilders (objeto DataSourceManager.scala) más lookup_data_sources.py iter_modules de pyspark_* | Almacenar en caché el DefaultSource serializado de la sesión que lo desencadena en toda la JVM. Laboratorio: layer='static' trigger='attacker' first-wins=yes. |
Víctima: SQL USING <name> / spark.read.format(name) en una sesión que nunca plantó | PythonDataSourceV2 createDataSourceInPython | Ejecutar esa clase como UID de Spark. Laboratorio: planted_by='attacker' witness_written=True static-cross-session=yes. El archivo oráculo contiene SPARK-PYTHON-DS-STATIC-WITNESS. |
| Método | Sumidero | Resultado |
|---|---|---|
| Plantación posterior de un segundo nombre una vez llenado el mapa | el mismo mapa estático | Cerrado. Gana el primero. Laboratorio: later-lookup found=False, el compañero sigue siendo solo delta. |
registerPython("alpha") por sesión | runtimeDataSourceBuilders | Aislado. El clon copia solo el runtime. Laboratorio: runtime-isolated=yes. La lectura de alpha por la víctima es found=False. |
Alias de Java parquet | DataSourceRegister ServiceLoader de Java | Cerrado. Laboratorio: java-alias-closed=yes layer='java'. Igual para csv/json/orc/jdbc. |
| Método | Sumidero | Privilegio |
|---|---|---|
USING de la víctima tras la plantación | workers de Python del driver + ejecutor | Código en todo el clúster como UID de Spark. El laboratorio escribe un archivo testigo en lugar de un payload. |
| La misma ruta | caída / giro del worker | Disponibilidad como UID de Spark. No es el Alto. El Alto es la ejecución de código. |
SQL de Hive Thrift USING en un SparkSession compartido | el mismo mapa estático | El mismo residual si Thrift y Connect comparten la JVM del driver. No ejercitado. |
parquet / csv / json / orc / jdbc de Java. Gana ServiceLoader.concatenatePaths rechaza .. y nombres absolutos).pythonExec proporcionado por el cliente. El planificador de Connect usa PYSPARK_PYTHON / python3 del servidor.registerPython. El mapa de runtime es por instancia de DataSourceManager.pyspark local monousuario con una sola sesión. No hay otro inquilino.Fijé apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Veintiocho búsquedas en el código fuente. El envío de trabajos que ejecuta código de usuario es por diseño. History Jackson, el shell de la UI, la suplantación local de PySpark y la ruta de recepción RPC independiente están cerrados en este análisis. Los artefactos de Connect aíslan los pyfiles bajo un UUID de sesión. El registerPython de runtime es por sesión. El objeto compañero no lo es.
SPARK-45917 autorregistra paquetes pyspark_* desde PYTHONPATH. Eso está pensado para conectores instalados por el operador. Lo residual es que el worker de búsqueda también hace sys.path.insert del directorio SparkFiles de la sesión que lo desencadena (worker_util.setup_spark_files), y luego serializa cualquier DefaultSource de pyspark_* que encuentre en un mapa global del proceso que nunca se refresca.
Mal camino: tratar el aislamiento de registerPython como toda la barrera entre inquilinos. El runtime está aislado. El estático no.
cd lab
./run.sh