
Apache Spark 4.2.0 - Hoch - Remote - Dateischreiben - History appStorePath nicht bereinigte App-ID
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-history-store-path
Klasse: Dateischreiben (konfigurationsgesteuert) Reichweite: Remote
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
HistoryServerDiskManager.appStorePath führt new File(appStoreDir, appId + extension) ohne Bereinigung aus. Utils.sanitizeDirName ist nur für Event-Log-Dateinamen gedacht. Die JSON-App ID ist asText(). Das Listing speichert event.appId.orNull. Lease.commit verwendet diesen String. Eine App-ID ../ schreibt eine .rdb außerhalb von spark.history.store.path. Der standardmäßig nicht gesetzte store.path ist In-Memory (kein Schreiben). Der Inhalt ist eine Store-Datei, kein RCE.
| ID | noch keine CVE |
| Klasse | Dateischreiben (konfigurationsgesteuerter History-Disk-Store) |
| Reichweite | Remote (Event-Log-Schreiber plus History mit gesetztem spark.history.store.path; UI-ACLs standardmäßig aus) |
| CWE | CWE-22 |
| CVSS | Hoch: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N, wenn spark.history.store.path gesetzt ist |
| Produkt | Apache Spark History Server |
| Betroffen | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) |
| Auth | Schreiber von spark.history.fs.logDirectory (Standard file:/tmp/spark-events); History muss spark.history.store.path gesetzt haben |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1:18990. Gadget des zitierten Joins. Witness-.rdb außerhalb von store.path. |
Dasselbe Opfermodell wie bei CVE-2025-54920: Der History Server zeigt auf ein Log-Verzeichnis, in das der Angreifer schreiben kann. Jackson konstruiert keine Gadgets der Klasse HiveConnection mehr. Der Disk-Store-Pfad vertraut weiterhin der App-ID aus dem Event-JSON.
| Methode | Sink | Berechtigung | Lab vs. abgeleitet |
|---|---|---|---|
Ein Event-Log platzieren, dessen JSON-App ID ../<name> (oder ../../oracle/...) ist. Auf den Scan warten. GET, das die App-UI lädt / getAppUI / loadDiskStore / createDiskStore | HistoryServerDiskManager.appStorePath, dann Lease.commit renameTo | Integrität: einen .rdb-Pfad (Standard RocksDB) oder .ldb-Pfad (LevelDB) außerhalb von {store.path}/apps als History-UID anlegen. Im Lab am Gadget bewiesen: escaped=yes, Datei /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb enthält den Witness. Der Inhalt ist eine Store-Datei, kein vom Angreifer gewählter Host-Bytecode, keine Shell. | Im Lab bewiesen (Gadget-Schreiben von Witness-Bytes am ausgelaufenen Pfad). Das vollständige History-renameTo eines RocksDB-Verzeichnisses ist derselbe Join. |
| Dieselbe App-ID, wenn das ausgelaufene Ziel bereits ein Verzeichnis ist | Lease.commit if (dst.isDirectory()) deleteStore(dst), dann Utils.deleteRecursively | Verfügbarkeit: ein bestehendes Verzeichnis löschen, dessen Name bereits mit dem Backend-Suffix endet, und es dann durch den geleasten Store ersetzen. | Abgeleitet (Pin HistoryServerDiskManager.scala commit). Das Gadget schrieb eine Datei, rief kein deleteRecursively auf. |
Standardmäßig nicht gesetztes spark.history.store.path | LOCAL_STORE_DIR createOptional; diskManager = storePath.map | Geschlossen. In-Memory-Listing. Kein HistoryServerDiskManager. Im Lab bewiesen gated=yes written=no. | Im Lab bewiesen |
Zuerst Utils.sanitizeDirName auf die App-ID anwenden | Event-Log-Dateinamen-Helfer | Bleibt innerhalb von apps/. / und : werden zu -. Im Lab bewiesenes Negativ. | Im Lab bewiesen |
App-ID local-control mit gesetztem store.path kontrollieren | derselbe Join | Schreibt {store.path}/apps/local-control.rdb. Bleibt innerhalb von apps/. | Im Lab bewiesen |
Angrenzend geschlossen: Standardmäßig nicht gesetzter Store (In-Memory). Jackson sparkEventFromJson erfordert weiterhin SparkListenerEvent (CVE-2025-54920). URL-getAppUI-Fallback eventlog_v2_ + nameForAppAndAttempt ist bereinigt. Der Listing-KV-Key ist kein Dateisystempfad. HybridStore / kvstore-Mapper hat kein Id.CLASS. History-HTTP akzeptiert keinen Upload-Pfad. Compaction standardmäßig aus. Cleaner standardmäßig aus. Job-Submit auf einem offenen Cluster (SECURITY.md FAQ). ML-Modell-Laden (docs/ml-security.md). Dieser Rest ist kein Host-RCE.
Ich habe Spark 4.2.0 gepinnt, nicht master, nicht ein 4.3 RC. Achtundzwanzig Jagden. Das öffentliche History-Jackson-RCE ist auf diesem Peel geschlossen. Event-Log-Schreiber rufen bereits Utils.sanitizeDirName auf, sodass die Log-Datei kein / enthalten kann. Der Dateiname des Disk-Cache verwendet die rohe App-ID.
private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
new File(appStoreDir, fileName)
}
appStoreDir ist {spark.history.store.path}/apps. Unix new File(parent, "../../oracle/name.rdb") löst außerhalb des Parents auf. JsonProtocol.applicationStartFromJson nimmt json.get("App ID").asText(). AppListingListener.onApplicationStart speichert event.appId.orNull. lease.commit(appId, attempt.info.attemptId) speist diesen String in appStorePath ein.
Bereits festgehaltene Irrwege: den standardmäßig nicht gesetzten store.path als Schreiben zu behandeln (er ist In-Memory); zu erwarten, dass Unix new File(dir, "/abs") den Parent ignoriert (tut es nicht; der Rest ist ..); RCE zu behaupten, weil das Suffix .rdb ist; einen vollständigen History Server zu booten, obwohl der Join das Orakel ist; sanitizeDirName als diesen Pfad abdeckend zu behandeln.
python3 spark-history-store-path-Abraxas-Labs.py
oder
cd lab
./run.sh
Gadget von HistoryServerDiskManager.appStorePath auf 127.0.0.1:18990. Pin-Auszüge unter lab/pin/. Der vollständige History Server wird nicht gebootet. Binde ihn an Loopback.
SUCCESS SPARK-HISTORY-STORE-PATH how=gadget escaped=yes gated=yes outside=/work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb SPARK-HISTORY-STORE-PATH-WITNESS
Führe Utils.sanitizeDirName (oder lehne .. / / / NUL ab) auf appId und attemptId innerhalb von appStorePath vor new File aus. Verlange nach dem Join, dass dst.getCanonicalFile() unter appStoreDir.getCanonicalFile() bleibt. Ein nicht gesetztes spark.history.store.path bleibt der sichere Standard.