abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-python-ds-static
クラス: 特権の残留 到達範囲: リモート
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
DataSourceManager.dataSourceBuilders は JVM グローバルな先勝ちマップである。SPARK-45917 はルックアップワーカーの sys.path から pyspark_* Python データソースを自動登録する。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 データソースを 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 データソース静的レジストリ |
| 認証 | 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 データソースルックアップをトリガーする。同じ 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 を、決してリフレッシュされないプロセスグローバルマップに pickle 化することである。
誤った方向: 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]