abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
解説記事: https://abraxaslabs.tech/research/spark-python-ds-static
分類: 権限の残留 到達範囲: リモート
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders は JVM グローバルな先勝ちマップである。SPARK-45917 は、ルックアップワーカーの sys.path から pyspark_* Python Data Source を自動登録する。Connect のセッション分離により、アーティファクトは SparkFiles/<sessionUUID>/ 配下に保たれ、セッションごとの registerPython は runtimeDataSourceBuilders 上に留まる。しかし最初のルックアップは依然としてトリガーしたセッションの SparkFiles ディレクトリを sys.path に追加し、すべての DefaultSource を pickle 化し、それらのクラスをコンパニオンオブジェクトにキャッシュする。その後の Connect SQL(または Hive Thrift SQL)の read.format(name) / USING name は、Python proto を送信することなく、そのクラスを Spark UID として実行する。
仕掛け役は、すでに Python の実行を許可されている Connect Python テナントである。被害者は、同じ Connect ドライバー上の SQL 専用テナントである。Java エイリアス(parquet、csv、json、orc、jdbc)は Java の ServiceLoader パスに留まる。マップが埋まった後の仕掛けは更新されない。AC:H は最初のルックアップと、Java エイリアスではない名前によるものである。
| ID | CVE 未割当 |
| 分類 | 権限の残留(クロスセッションの Python Data Source を Spark UID として実行。未認証のジョブ投入ではない) |
| 到達範囲 | リモート(共有 Spark Connect ドライバー) |
| CWE | CWE-269, CWE-502 |
| CVSS | High: 8.8 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| 製品 | Apache Spark |
| 影響範囲 | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) Python Data Source 静的レジストリ |
| 認証 | Connect Python テナント(仕掛け役)に加え、後から SQL 専用の Connect / Hive Thrift SQL ユーザー(被害者) |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1:18970 のみ。ウィットネスファイル。コンパニオンマップのガジェット。 |
共有ドライバー上で Spark Connect Python セッションを保持する。DefaultSource.name() が存在しないコネクタの短縮名(delta、iceberg、hudi など)である pyfiles/pyspark_<name>.py を仕掛ける。そのセッションで最初の Python Data Source ルックアップをトリガーする。同じ JVM 内の後続のすべてのセッションが read.format(name) または SQL の USING name を実行すると、pickle 化されたクラスを Spark UID として、ドライバーのプランナーワーカー上およびエグゼキュータ上でインスタンス化する。
これが権限の全集合である。仕掛け役はすでに Python を実行している。残留しているのは、REGISTER_DATA_SOURCE / Python UDF proto を一度も送信していない SQL 専用テナントが、それでもそのクラスを実行してしまうという点である。
| 手法 | シンク | 権限 |
|---|---|---|
仕掛け役: AddArtifacts で pyfiles/pyspark_<name>.py を指定し、最初の format(name) / ルックアップを実行 | コンパニオンの DataSourceManager.dataSourceBuilders(DataSourceManager.scala オブジェクト)に加え、lookup_data_sources.py の pyspark_* に対する iter_modules | トリガーしたセッションの pickle 化された DefaultSource を JVM 全体にキャッシュする。ラボ: layer='static' trigger='attacker' first-wins=yes。 |
被害者: 一度も仕掛けていないセッションで SQL USING <name> / spark.read.format(name) を実行 | PythonDataSourceV2 の createDataSourceInPython | そのクラスを Spark UID として実行する。ラボ: planted_by='attacker' witness_written=True static-cross-session=yes。オラクルファイルには SPARK-PYTHON-DS-STATIC-WITNESS が格納される。 |
| 手法 | シンク | 結果 |
|---|---|---|
| マップが埋まった後に別名で2つ目のプラントを実行 | 同一の静的マップ | クローズ。先勝ち。ラボ: later-lookup found=False、コンパニオンは依然として delta のみ。 |
セッションごとの registerPython("alpha") | runtimeDataSourceBuilders | 分離。クローンはランタイムのみをコピー。ラボ: runtime-isolated=yes。alpha の被害者読み取りは found=False。 |
Javaエイリアス parquet | Java DataSourceRegister ServiceLoader | クローズ。ラボ: java-alias-closed=yes layer='java'。csv/json/orc/jdbc も同様。 |
| 手法 | シンク | 権限 |
|---|---|---|
プラント後の被害者 USING | ドライバ + エグゼキュータのPythonワーカー | Spark UIDとしてのクラスタ全体のコード。ラボはペイロードの代わりに証人ファイルを書き込む。 |
| 同一パス | ワーカーのクラッシュ / スピン | Spark UIDとしての可用性。Highではない。Highはコード実行である。 |
共有 SparkSession 上のHive Thrift SQL USING | 同一の静的マップ | ThriftとConnectがドライバJVMを共有する場合、同じ残留。未検証。 |
parquet / csv / json / orc / jdbc ハイジャック。ServiceLoaderが勝つ。concatenatePaths は .. と絶対名を拒否する)。pythonExec。Connectプランナーはサーバーの PYSPARK_PYTHON / python3 を使用する。registerPython のクロスセッションリーク。ランタイムマップは DataSourceManager インスタンスごと。pyspark。他のテナントが存在しない。apache/spark v4.2.0(32f7299601108917fb01920a54e084595b7b3bf8)をピン留めした。28回のソース探索。ユーザーコードを実行するジョブ送信は設計によるもの。History Jackson、UIシェル、PySparkローカル偽装、スタンドアロンRPC受信パスはこの剥離ではクローズされている。ConnectアーティファクトはpyfilesをセッションUUIDの下で分離する。ランタイムの registerPython はセッションごと。コンパニオンオブジェクトはそうではない。
SPARK-45917はPYTHONPATHから pyspark_* パッケージを自動登録する。これはオペレータがインストールしたコネクタ向けに意図されたもの。残留とは、ルックアップワーカーがトリガー元セッションのSparkFilesディレクトリを sys.path.insert し(worker_util.setup_spark_files)、見つけた pyspark_* DefaultSource をリフレッシュされないプロセスグローバルマップにピクル化することである。
誤った方向: registerPython の分離をテナントバーの全体とみなすこと。ランタイムは分離されている。静的はそうではない。
cd lab
./run.sh
ループバック 127.0.0.1:18970。Compose プロジェクト spark-python-ds-static。ウィットネス SPARK-PYTHON-DS-STATIC-WITNESS。object DataSourceManager の Python ガジェットに加え、セッションごとのランタイムマップ。pyspark_delta.py は攻撃者の SparkFiles にのみ配置。攻撃者のルックアップがコンパニオンを埋める。後からの iceberg の配置はリフレッシュしない。被害者による delta の read がオラクルを書き込む。registerPython("alpha") はリークしない。Java の parquet は Java のまま。
最終行:
SUCCESS SPARK-PYTHON-DS-STATIC static-cross-session=yes runtime-isolated=yes first-wins=yes java-alias-closed=yes SPARK-PYTHON-DS-STATIC-WITNESS
静的マップを埋める際に、セッションの SparkFiles をルックアップワーカーの sys.path に配置しないでください。オペレータの PYTHONPATH パッケージのみをロードしてください(設計どおり SPARK-45917)。あるいは、アーティファクトが既にそうであるように、静的マップをセッション / JobArtifactSet UUID でキー付けしてください。先勝ちの空埋めは、ドライバの存続期間中、別のテナントのクラスを固定すべきではありません。
それまでは、Python テナントと、ドライバの Python を既に信頼しているとは言えない SQL のみのテナントの間で Connect ドライバを共有しないでください。
32f7299601108917fb01920a54e084595b7b3bf8)DataSourceManager.scala のコンパニオン dataSourceBuilders / initialStaticDataSourceBuilderslookup_data_sources.py の pyspark_* DefaultSourceworker_util.py の setup_spark_filesPythonDataSourceV2.scalaAbraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]