
Apache Spark 4.2.0 — высокий уровень — удалённо — остаточные привилегии — глобальная для процесса карта Python Data Source
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-глобальная карта с приоритетом первого добавления (first-wins). SPARK-45917 автоматически регистрирует Python Data Sources pyspark_* из sys.path воркера, выполняющего поиск. Изоляция сессий Connect хранит артефакты в SparkFiles/<sessionUUID>/, а registerPython для каждой сессии остаётся в runtimeDataSourceBuilders. Первый поиск всё равно помещает каталог SparkFiles инициирующей сессии в sys.path, сериализует (pickle) каждый DefaultSource и кэширует эти классы в companion-объекте. Последующий Connect SQL (или Hive Thrift SQL) read.format(name) / USING name запускает этот класс от имени Spark UID без отправки Python proto.
Закладчик — это Python-арендатор Connect, которому Python уже разрешён. Жертва — арендатор, работающий только через SQL, на том же драйвере Connect. 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 | Высокий: 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 |
| Аутентификация | Python-арендатор Connect (закладчик) плюс более поздний пользователь Connect / Hive Thrift SQL только с SQL (жертва) |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1:18970. Файл-свидетель. Гаджет companion-карты. |
Удерживать сессию Spark Connect Python на общем драйвере. Разместить pyfiles/pyspark_<name>.py, чей DefaultSource.name() является именем отсутствующего коннектора (delta, iceberg, hudi, ...). Инициировать первый поиск Python Data Source в этой сессии. Любая последующая сессия в той же JVM, которая выполняет read.format(name) или SQL USING name, инстанцирует сериализованный класс от имени Spark UID на воркере планировщика драйвера и на исполнителях.
Это и есть полный набор привилегий. Закладчик уже выполняет Python. Остаточная проблема в том, что арендатор, работающий только через SQL и никогда не отправлявший REGISTER_DATA_SOURCE / Python UDF proto, всё равно исполняет этот класс.
| Метод | Точка сброса | Привилегия |
|---|---|---|
Закладчик: AddArtifacts с именем pyfiles/pyspark_<name>.py, затем первый format(name) / поиск | companion DataSourceManager.dataSourceBuilders (объект DataSourceManager.scala) плюс iter_modules из pyspark_* в lookup_data_sources.py | Кэшировать сериализованный 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. |
| Метод | Sink | Результат |
|---|---|---|
| Позднее размещение второго имени после заполнения карты | та же статическая карта | Закрыто. Побеждает первый. Лаборатория: later-lookup found=False, companion по-прежнему только delta. |
Посессионный registerPython("alpha") | runtimeDataSourceBuilders | Изолировано. Клон копирует только runtime. Лаборатория: runtime-isolated=yes. Чтение alpha жертвой даёт found=False. |
Java-алиас parquet | Java DataSourceRegister ServiceLoader | Закрыто. Лаборатория: java-alias-closed=yes layer='java'. То же для csv/json/orc/jdbc. |
| Метод | Sink | Привилегия |
|---|---|---|
USING жертвы после размещения | Python-воркеры драйвера + исполнителя | Код в масштабе кластера от имени Spark UID. Лаборатория пишет файл-свидетель вместо полезной нагрузки. |
| Тот же путь | падение / зависание воркера | Доступность от имени Spark UID. Не High. High — это выполнение кода. |
Hive Thrift SQL USING на общем SparkSession | та же статическая карта | Тот же остаток, если Thrift и Connect делят JVM драйвера. Не проверялось. |
parquet / csv / json / orc / jdbc. Побеждает ServiceLoader.concatenatePaths отклоняет .. и абсолютные имена).pythonExec, задаваемый клиентом. Планировщик Connect использует серверные PYSPARK_PYTHON / python3.registerPython между сессиями. Карта runtime привязана к экземпляру DataSourceManager.pyspark с одной сессией. Другого арендатора нет.Я зафиксировал apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Двадцать восемь поисков по исходникам. Отправка заданий, выполняющая пользовательский код, — по замыслу. History Jackson, оболочка UI, локальная имперсонация PySpark и путь приёма standalone RPC закрыты на этом этапе. Артефакты Connect изолируют pyfiles под UUID сессии. Runtime registerPython — посессионный. Companion-объект — нет.
SPARK-45917 автоматически регистрирует пакеты pyspark_* из PYTHONPATH. Это предназначено для коннекторов, установленных оператором. Остаток в том, что воркер поиска также делает sys.path.insert для каталога SparkFiles вызвавшей сессии (worker_util.setup_spark_files), а затем пиклит любой найденный pyspark_* DefaultSource в глобальную для процесса карту, которая никогда не обновляется.
Неверный поворот: считать изоляцию registerPython всем барьером между арендаторами. Runtime изолирован. Статика — нет.
cd lab
./run.sh