
Apache Spark 4.2.0 - Alto - Remoto - Privilegio residuo - mappa globale del processo Python Data Source
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
Write-up: https://abraxaslabs.tech/research/spark-python-ds-static
Classe: Privilegio residuo Portata: Remota
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders è una mappa JVM-globale first-wins. SPARK-45917 registra automaticamente le Python Data Source pyspark_* dal sys.path del worker di lookup. L'isolamento delle sessioni Connect mantiene gli artefatti sotto SparkFiles/<sessionUUID>/, e registerPython per-sessione resta su runtimeDataSourceBuilders. Il primo lookup mette comunque la directory SparkFiles della sessione che lo attiva sul sys.path, serializza (pickle) ogni DefaultSource e memorizza quelle classi nella companion object. Le successive read.format(name) / USING name di Connect SQL (o Hive Thrift SQL) eseguono quella classe come Spark UID senza inviare proto Python.
Chi pianta è un tenant Python Connect a cui è già consentito usare Python. La vittima è un tenant solo-SQL sullo stesso driver Connect. Gli alias Java (parquet, csv, json, orc, jdbc) restano sul percorso Java ServiceLoader. I piantamenti successivi, una volta riempita la mappa, non vengono aggiornati. AC:H corrisponde al primo lookup più un nome che non è un alias Java.
| ID | nessuna CVE al momento |
| Classe | Privilegio residuo (Python Data Source cross-sessione come Spark UID; non invio di job non autenticato) |
| Portata | Remota (driver Spark Connect condiviso) |
| 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 |
| Prodotto | Apache Spark |
| Affetto | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) registro statico delle Python Data Source |
| Auth | Tenant Python Connect (chi pianta) più un successivo utente solo-SQL Connect / Hive Thrift SQL (vittima) |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1:18970. File testimone. Gadget della mappa companion. |
Mantenere una sessione Python Spark Connect su un driver condiviso. Piantare pyfiles/pyspark_<name>.py il cui DefaultSource.name() è un nome breve di connettore assente (delta, iceberg, hudi, ...). Attivare il primo lookup di Python Data Source su quella sessione. Ogni sessione successiva nello stesso JVM che esegue read.format(name) o SQL USING name istanzia la classe serializzata come Spark UID sul worker del planner del driver e sugli executor.
Questo è l'intero insieme di privilegi. Chi pianta esegue già Python. Il residuo è che un tenant solo-SQL, che non ha mai inviato REGISTER_DATA_SOURCE / proto Python UDF, esegue comunque quella classe.
| Metodo | Sink | Privilegio |
|---|---|---|
Chi pianta: AddArtifacts con nome pyfiles/pyspark_<name>.py poi primo format(name) / lookup | companion DataSourceManager.dataSourceBuilders (object DataSourceManager.scala) più iter_modules di pyspark_* in lookup_data_sources.py | Memorizza il DefaultSource serializzato della sessione che lo attiva a livello JVM. Lab: layer='static' trigger='attacker' first-wins=yes. |
Vittima: SQL USING <name> / spark.read.format(name) su una sessione che non ha mai piantato | PythonDataSourceV2 createDataSourceInPython | Esegue quella classe come Spark UID. Lab: planted_by='attacker' witness_written=True static-cross-session=yes. Il file oracolo contiene SPARK-PYTHON-DS-STATIC-WITNESS. |
| Metodo | Sink | Risultato |
|---|---|---|
| Pianta successiva di un secondo nome dopo che la mappa è stata riempita | stessa mappa statica | Chiuso. Vince il primo. Lab: later-lookup found=False, il companion è ancora solo delta. |
registerPython("alpha") per sessione | runtimeDataSourceBuilders | Isolato. Il clone copia solo il runtime. Lab: runtime-isolated=yes. La lettura di alpha da parte della vittima è found=False. |
Alias Java parquet | ServiceLoader di DataSourceRegister Java | Chiuso. Lab: java-alias-closed=yes layer='java'. Lo stesso per csv/json/orc/jdbc. |
| Metodo | Sink | Privilegio |
|---|---|---|
USING della vittima dopo la pianta | worker Python del driver + executor | Codice a livello di cluster come UID Spark. Il lab scrive un file testimone invece di un payload. |
| Stesso percorso | crash / spin del worker | Disponibilità come UID Spark. Non il livello High. Il livello High è l'esecuzione di codice. |
Hive Thrift SQL USING su una SparkSession condivisa | stessa mappa statica | Stesso residuo se Thrift e Connect condividono la JVM del driver. Non esercitato. |
parquet / csv / json / orc / jdbc. Vince il ServiceLoader.concatenatePaths rifiuta .. e nomi assoluti).pythonExec fornito dal client. Il planner di Connect usa PYSPARK_PYTHON / python3 del server.registerPython. La mappa runtime è per istanza di DataSourceManager.pyspark locale monoutente con una sola sessione. Non c'è altro tenant.Ho fissato apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Ventotto ricerche nel codice sorgente. L'invio di job che esegue codice utente è per progettazione. History Jackson, la shell della UI, l'impersonazione locale di PySpark e il percorso di ricezione RPC standalone sono chiusi in questa analisi. Gli artefatti di Connect isolano i pyfiles sotto un UUID di sessione. registerPython a runtime è per sessione. L'oggetto companion no.
SPARK-45917 registra automaticamente i pacchetti pyspark_* da PYTHONPATH. Questo è intenzionale per i connettori installati dall'operatore. Il residuo è che il worker di lookup esegue anche sys.path.insert della directory SparkFiles della sessione che ha attivato, (worker_util.setup_spark_files), poi serializza con pickle qualunque DefaultSource pyspark_* trovi in una mappa globale del processo che non si aggiorna mai.
Falsa pista: considerare l'isolamento di registerPython come l'intera barriera tra tenant. Il runtime è isolato. Lo statico no.
cd lab
./run.sh