
Apache Spark 4.2.0 - 高 - リモート - 権限の残存 - proxy-user defaultExtraClassPath
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-proxyuser-classpath
解説記事: https://abraxaslabs.tech/research/spark-proxyuser-classpath
分類: 権限の取り残し (CVE-2023-22946 / SPARK-41958 の取り残し) 到達範囲: リモート
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2023-22946 / SPARK-41958 は依然として spark.submit.proxyUser.allowCustomClasspathInClusterMode をデフォルトで false にしており、--proxy-user によるクラスタ送信時には依然として childClasspath.clear() を実行している。これは公開されている YARN の --jars 経路である。その後 Spark 4.0 で spark.driver.defaultExtraClassPath が追加された。ランチャーはこのキーを常に spark-submit の JVM -cp の先頭に付加し、クラスタモードでも同様である。SPARK-41958 はこれを一切検知しない。
Livy スタイルのテナントは、ゲートウェイの spark-submit JVM 内でステージングされた jar を依然としてロードできる。Hadoop の UserGroupInformation.doAs はその Unix UID を変更しない。
| ID | CVE 未割当 |
| 分類 | 権限の取り残し (CVE-2023-22946 / SPARK-41958 の取り残し) |
| 到達範囲 | リモート (ゲートウェイ経由の Livy スタイル --proxy-user ジョブ送信) |
| CWE | CWE-269, CWE-427 |
| CVSS | High: 8.8 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| 製品 | Apache Spark |
| 影響を受けるバージョン | Apache Spark 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) |
| 認証 | --conf とステージングされた --jars を spark-submit --proxy-user ... --deploy-mode cluster に転送する Livy / Kyuubi / Oozie スタイルのゲートウェイの認証済みテナント |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1:18950 のみ。証跡ファイル。引用したランチャー / 送信経路のガジェット。 |
スーパーユーザー UID で spark-submit --proxy-user <tenant> --deploy-mode cluster を実行するマルチテナント Spark ゲートウェイ (Livy、Kyuubi、Oozie など) のテナントアカウントを保持する。ゲートウェイ JVM が開ける jar をステージングする。--conf spark.driver.defaultExtraClassPath=<staged jar> を送信する。SPARK-41958 のゲートは false のままでよい。構築される spark-submit -cp は依然としてその jar で始まる。その中のクラスはゲートウェイの Unix UID としてロードされる。
--conf spark.submit.proxyUser.allowCustomClasspathInClusterMode=true は第 2 の経路である。.internal() はドキュメント専用であり、--conf は依然としてキーを書き込み、YARN クラスタは --jars を childClasspath に残す。公開されている CVE-2023-22946 は、clear() がすでにカバーしている YARN のデフォルト --jars 経路のままである。この取り残しはゲートされていない defaultExtraClassPath である。
クリーンな送信後の Hadoop のなりすましは、YARN AM / k8s ドライバにとって依然としてプロキシユーザーである。この取り残しはゲートウェイ JVM のみに関わる。
| 手法 | シンク | 偵察 | 完全性 | 可用性 | 来歴 |
|---|---|---|---|---|---|
Livy 形式の submit --proxy-user + yarn/k8s/standalone クラスタ + --conf spark.driver.defaultExtraClassPath=<ステージング済みローカル jar>、SPARK-41958 フラグは false のまま | SparkSubmitCommandBuilder.buildSparkSubmitCommand SparkSubmitCommandBuilder.java:317-327 -cp の先頭エントリ | ゲートウェイ UID が読めるファイルを一覧/読み取り(keytab ディレクトリ、spark-defaults、ローカルステージング)。後続 RPC の Hadoop アイデンティティは依然としてプロキシユーザー。 | spark-submit JVM 内でテナントのクラスをロードする。Unix UID はゲートウェイ(Hadoop プロキシスーパーユーザー)のまま。ラボ: defaultExtraClassPath-on-cp=yes childClasspath-cleared=yes lab.WitnessMain が SPARK-PROXYUSER-CLASSPATH-WITNESS を書き込んだ。公開 CVE クラスからの推測: その UID としての Hadoop Configuration リソースオーバーレイ / FileSystem 実装ルックアップ。 | オーバーレイが例外を投げると submit JVM がクラッシュする。クラスではない。 | ラボで実証済みのロード + 構築された -cp としての witness 書き込み。Hadoop オーバーレイは推測。Livy/Kyuubi の --conf パススルーは推測(それらのツリーはこのピンではない)。 |
同じ submit で --conf spark.submit.proxyUser.allowCustomClasspathInClusterMode=true に加えて --jars / プライマリリソース | SparkSubmit.prepareSubmitEnvironment SparkSubmit.scala:333-336,738-743,917-924 続いて runMain :980-987 | 手法 A と同じ偵察を、システムローダーではなく子 URLClassLoader 上で行う。 | addJarToClasspath が実行されるよう --jars を childClasspath に残す。ラボ: conf-flag-keeps-jars=yes。 | 同上。 | ラボで実証済みの childClasspath 保持。フラグが false のままのときに clear() がカバーする公開 YARN --jars パスの再提出ではない。 |
ラボ実証済み vs 推測: 構築された -cp はクラスタモードで defaultExtraClassPath を含む。childClasspath.clear() はデフォルトフラグでも依然として実行される。spark.driver.extraClassPath はクラスタでは省略される。フラグ false の --jars はクリアされる。その -cp 上の witness クラスがオラクルを書き込む。Hadoop 設定クラスの実行は公開 CVE-2023-22946 のメカニズムであり、ここでは再実行していない。Livy/Kyuubi が --conf を転送することは、それらのプロジェクトが文書化しているアーキテクチャである。
| 手法 | シンク | クローズのまま |
|---|---|---|
クラスタモードでの --driver-class-path / spark.driver.extraClassPath | SparkSubmitCommandBuilder.java:318-319 | spark-submit の -cp には追加されない。ラボ: extraClassPath-omitted=yes。クラスタドライバに渡され、YARN/K8s がプロキシユーザーとして起動する。 |
デフォルトクラスタ + --proxy-user + --jars でフラグを反転せず defaultExtraClassPath もなし | SparkSubmit.scala:917-918 childClasspath.clear() | YARN/standalone/k8s クラスタの子クラスパスが空にされる。ラボ: 対照ウォークで childClasspath-cleared=yes。公開 CVE-2023-22946 はこのパスのまま。 |
ゲートウェイ上の --proxy-user クライアントモードドライバ | docs/security.md の Proxy User セクション | 文書化済み: クライアントモードドライバ JVM はスーパーユーザー。ベンダーは CVE ではないとしている。Livy/Kyuubi はクラスタモードを強制するよう指示されている。 |
K8s クラスタモードドライバコンテナ(spark.kubernetes.submitInDriver=true)がクラスパスに . を追加 | SparkSubmit.scala:420-431 SPARK-43540 | ドライバポッド内の 2 回目の spark-submit であり、Livy ホストではない。ポッド UID はコンテナユーザー。 |
クラスタモードでの spark.driver.extraJavaOptions | SparkSubmitCommandBuilder.java:341-357 | spark-submit JVM には適用されない。SPARK_SUBMIT_OPTS はゲートウェイの環境変数であり、テナントの --conf ではない。 |
| 送信されたアプリの YARN AM / k8s ドライバ | クリーンな submit 後の Hadoop なりすまし | クラスタ起動では依然としてプロキシユーザー。この残存物はゲートウェイ JVM のみ。 |
| 未認証のジョブ送信 / Connect SQL / ML モデルロード | SECURITY.md FAQ / docs/ml-security.md | 設計によるもの。この残存物ではない。 |
Spark 4.2.0(32f7299601108917fb01920a54e084595b7b3bf8)を固定し、SparkSubmit.submit / prepareSubmitEnvironment / runMain、SPARK-41958 フラグ、およびランチャーの -cp ビルダーを追跡しました。公開されている Medium の記事は依然として --jars パスに依拠しています。デフォルトの allowCustomClasspathInClusterMode は false(package.scala:2849-2854)であり、クラスターモード + proxy-user では依然として childClasspath がクリアされます。--conf は依然としてその内部キーを書き込みます(SparkSubmitArguments.scala:474-476)。別途、Spark 4.0.0 の spark.driver.defaultExtraClassPath は常に spark-submit の -cp に含まれ、クラスターモードでも同様です:
boolean isClientMode = isClientMode(config);
String extraClassPath = isClientMode ? config.get(SparkLauncher.DRIVER_EXTRA_CLASSPATH) : null;
String defaultExtraClassPath = config.get(SparkLauncher.DRIVER_DEFAULT_EXTRA_CLASS_PATH);
if (extraClassPath == null || extraClassPath.trim().isEmpty()) {
extraClassPath = defaultExtraClassPath;
} else {
extraClassPath += File.pathSeparator + defaultExtraClassPath;
}
List<String> cmd = buildJavaCommand(extraClassPath);
4.0.0 のキーは .internal() です(package.scala:80-85)。--conf は依然としてこれを書き込みます(SparkSubmitCommandBuilder.java:572-576)。SPARK-41958 はこのパスを決してゲートしませんでした。SparkSubmit が起動する前に、jar はすでにシステムクラスローダー上にあります。