
Apache Spark 4.2.0 - 高 - リモート - ファイル書き込み - History appStorePath の App ID がサニタイズされていない
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-history-store-path
クラス: ファイル書き込み (設定によるゲートあり) 到達範囲: リモート
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
HistoryServerDiskManager.appStorePath は new File(appStoreDir, appId + extension) をサニタイズなしで実行します。Utils.sanitizeDirName はイベントログのファイル名専用です。JSON の App ID は asText() です。リスティングは event.appId.orNull を格納します。Lease.commit はその文字列を使用します。App ID ../ は spark.history.store.path の外に .rdb を書き込みます。デフォルトの未設定 store.path はインメモリ (書き込みなし) です。内容はストアファイルであり、RCE ではありません。
| ID | CVE 未割当 |
| クラス | ファイル書き込み (設定によるゲートあり History ディスクストア) |
| 到達範囲 | リモート (イベントログライターと、spark.history.store.path が設定された History。UI ACL はデフォルトで無効) |
| CWE | CWE-22 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N (spark.history.store.path が設定されている場合) |
| 製品 | Apache Spark History Server |
| 影響を受けるバージョン | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) |
| 認証 | spark.history.fs.logDirectory (デフォルト file:/tmp/spark-events) の書き込み権限を持つ者。History に spark.history.store.path が設定されている必要あり |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1:18990 のみ。引用された join のガジェット。store.path の外への証拠 .rdb。 |
被害者モデルは CVE-2025-54920 と同じです。History Server が攻撃者の書き込み可能なログディレクトリを指しています。Jackson はもはや HiveConnection クラスのガジェットを構築しません。ディスクストアのパスは依然としてイベント JSON の App ID を信頼しています。
| 手法 | シンク | 権限 | ラボ vs 推測 |
|---|---|---|---|
JSON の App ID が ../<name> (または ../../oracle/...) であるイベントログを設置。スキャンを待つ。アプリ UI / getAppUI / loadDiskStore / createDiskStore をロードする GET | HistoryServerDiskManager.appStorePath その後 Lease.commit renameTo | 完全性: History UID として {store.path}/apps の外に .rdb (デフォルト RocksDB) または .ldb (LevelDB) パスを作成。ガジェット上でラボ実証済み: escaped=yes、ファイル /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb に証拠が含まれる。内容はストアファイルであり、攻撃者が選択したホストバイトコードでもシェルでもない。 | ラボ実証済み (エスケープされたパスへの証拠バイトのガジェット書き込み)。RocksDB ディレクトリの完全な History renameTo は同じ join。 |
| エスケープ先が既にディレクトリである場合の同じ App ID | Lease.commit if (dst.isDirectory()) deleteStore(dst) その後 Utils.deleteRecursively | 可用性: 名前が既にバックエンドサフィックスで終わる既存ディレクトリを削除し、リースされたストアで置き換える。 | 推測 (HistoryServerDiskManager.scala の commit をピン留め)。ガジェットはファイルを書き込み、deleteRecursively を呼び出さなかった。 |
デフォルトの未設定 spark.history.store.path | LOCAL_STORE_DIR createOptional; diskManager = storePath.map | クローズ。インメモリリスティング。HistoryServerDiskManager なし。ラボ実証済み gated=yes written=no。 | ラボ実証済み |
App ID に最初に Utils.sanitizeDirName を適用 | イベントログファイル名ヘルパー | apps/ 内に留まる。/ と : は - になる。ラボ実証済みのネガティブ。 | ラボ実証済み |
store.path 設定済みで App ID local-control を制御 | 同じ join | {store.path}/apps/local-control.rdb を書き込む。apps/ 内に留まる。 | ラボ実証済み |
隣接するクローズ済み項目: デフォルトの未設定ストア (インメモリ)。Jackson sparkEventFromJson は依然として SparkListenerEvent を要求 (CVE-2025-54920)。URL getAppUI フォールバック eventlog_v2_ + nameForAppAndAttempt はサニタイズ済み。リスティング KV キーはファイルシステムパスではない。HybridStore / kvstore マッパーには Id.CLASS がない。コンパクションはデフォルトで無効。クリーナーはデフォルトで無効。オープンクラスタでのジョブ投入 (SECURITY.md FAQ)。ML モデルロード (docs/ml-security.md)。この残存項目はホスト RCE ではない。
Spark 4.2.0 をピン留めしました。master でも 4.3 RC でもありません。28 回の調査。公開されている History Jackson RCE はこのピールではクローズされています。イベントログライターは既に Utils.sanitizeDirName を呼び出すため、ログファイルに / を含めることはできません。ディスクキャッシュのファイル名は生の App ID を使用します。
private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
new File(appStoreDir, fileName)
}
appStoreDir は {spark.history.store.path}/apps です。Unix の new File(parent, "../../oracle/name.rdb") は parent の外に解決されます。JsonProtocol.applicationStartFromJson は json.get("App ID").asText() を取得します。AppListingListener.onApplicationStart は event.appId.orNull を格納します。lease.commit(appId, attempt.info.attemptId) がその文字列を appStorePath に渡します。
既に記録済みの誤った方向性: デフォルトの未設定 store.path を書き込みと見なす (実際はインメモリ)。Unix の new File(dir, "/abs") が parent を無視すると期待する (実際は無視しない。残存は ..)。サフィックスが .rdb だから RCE と主張する。join がオラクルであるのに完全な History Server を起動する。sanitizeDirName がこのパスをカバーすると見なす。
python3 spark-history-store-path-Abraxas-Labs.py
または
cd lab
./run.sh
127.0.0.1:18990 上の HistoryServerDiskManager.appStorePath のガジェット。ピン留めした抜粋は lab/pin/ 以下。完全な History Server は起動しません。ループバックにバインドしてください。
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
new File の前に appStorePath 内で appId と attemptId に対して Utils.sanitizeDirName を実行する (または .. / / / NUL を拒否する)。join 後、dst.getCanonicalFile() が appStoreDir.getCanonicalFile() の下に留まることを要求する。spark.history.store.path を未設定にすることが安全なデフォルトのままです。
32f7299601108917fb01920a54e084595b7b3bf8)HistoryServerDiskManager.scala appStorePath / Lease.commitFsHistoryProvider.scala diskManager / loadDiskStore / onApplicationStartJsonProtocol.scala applicationStartFromJsonUtils.scala sanitizeDirNameHistory.scala LOCAL_STORE_DIR (オプション)GNU Affero GPL v3.0。LICENSE を参照。ループバックラボのみ。無保証。