Apache Spark 4.2.0 — высокий уровень — удалённо — запись файла — сессия Connect SET copyFromLocalToFs.allowDestLocal
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-connect-copy-local
Класс: запись файлов (остаток от SPARK-58531; статический только в 4.3.0) Доступ: удалённый
Apache Spark 4.2.0 (32f72996) — Apache Software Foundation
spark.sql.artifact.copyFromLocalToFs.allowDestLocal на этом пине всё ещё buildConf (настраивается в сессии). SPARK-58531 перевёл его в StaticSQLConf в 4.3.0. Сессия Connect может установить тестовый шлюз local-dest, после чего copyFromLocalToFs / AddArtifacts forward_to_fs/<abs> перезаписывает локальный путь драйвера от имени Spark UID.
Сессия Connect выполняет SET, затем forward_to_fs записывает локальные файлы драйвера. Назначение должно быть LocalFileSystem. Не RCE.
| ID | CVE пока нет (остаток от SPARK-58531) |
| Класс | Запись файлов (остаток от SPARK-58531; статический только в 4.3.0) |
| Доступ | Удалённый (Spark Connect Config + AddArtifacts) |
| CWE | CWE-22, CWE-732 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H |
| Продукт | Apache Spark (ASF) |
| Затронуто | Apache Spark 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). В 4.3.0 статический. |
| Аутентификация | Клиент Connect, способный выполнить ConfigRequest.SET и AddArtifacts. По умолчанию в Connect нет PSK. |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1:18920 |
Клиент Spark Connect, имеющий доступ к gRPC (порт привязки по умолчанию 15002, без PSK, если оператор не задал spark.connect.authenticate.token), может отключить тестовый шлюз, с которым был запущен драйвер, а затем перезаписать выбранный локальный путь на драйвере от имени Spark UID.
| Метод | Точка сброса | Привилегия |
|---|---|---|
gRPC Config SET spark.sql.artifact.copyFromLocalToFs.allowDestLocal=true (или SQL SET) | SparkConnectConfigHandler.handleSet -> RuntimeConfig.set -> SQLConf.setConfString | Целостность (конфигурация сессии, подтверждено в лаборатории): переключает шлюз local-dest для сессии. Сам по себе файл не записывает. Не изменяет другие сессии. Лаборатория: SET HTTP 200, modifiable=True. |
gRPC AddArtifacts с именем forward_to_fs + абсолютное назначение, CRC ok | ArtifactManager.uploadArtifactToFs -> LocalFileSystem.copyFromLocalFile(delSrc=false, overwrite=true) | Целостность (перезапись файла, подтверждено в лаборатории): запись/перезапись локального пути драйвера от имени Spark UID, вне artifactRootDirectory/<sessionUUID>/. Лаборатория: назначение /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS, uid 185, содержимое WITNESS. |
| Тот же AddArtifacts с назначением HDFS/S3/ABFS | документированный copyFromLocalToFs | Целостность (кластерная ФС, по замыслу): это не данный остаток. Назначение, не являющееся LocalFileSystem, никогда не обращается к тестовому флагу. |
Подтверждено в лаборатории vs выведено логически: установка ключа buildConf в сессии, затем запись в назначение LocalFileSystem от имени uid 185 вне песочницы артефактов — подтверждено в лаборатории. Перезапись от имени Spark UID пути, доступного процессу на запись (рабочий каталог драйвера, общедоступный на запись каталог для сброса, уже исполняемый скрипт, который оператор позже запустит), — выведено логически. Контроля над RIP нет. Не раздувайте запись файлов до RCE.
Требуется: работающий Spark Connect. Клиент, способный выполнить SET конфигурации SQL сессии и AddArtifacts. Назначение должно быть Hadoop LocalFileSystem. По умолчанию в Connect нет PSK; если оператор задал токен, для остатка всё равно нужен этот общий Bearer. Это обход контроля local-dest, а не обход PSK.
| Поверхность | Лаборатория |
|---|---|
| Копирование без SET | 400 _LEGACY_ERROR_TEMP_3161, назначение отсутствует |
SET старого статического spark.connect.copyFromLocalToFs.allowDestLocal | 400 CANNOT_MODIFY_STATIC_CONFIG |
Имя артефакта .. / абсолютный staging (ArtifactUtils.concatenatePaths) | INVALID_ARTIFACT_PATH |
Артефакты, отличные от forward_to_fs | остаются под artifactRootDirectory/<sessionUUID>/ |
| Назначение HDFS/облако без флага | документированный API, не данный остаток |
Неаутентифицированный SQL / Python UDF / Scala UdfPacket на открытом Connect | отправка задачи по FAQ вендора; DROP |
| SPARK-52122 ML DefaultParamsReader | allowlist + safeMLClassLoader на этом пине |
Ни shell. Ни heap dump. Ни обхода Connect PSK. Назначение всё ещё должно быть LocalFileSystem. Запись выполняется от имени Spark UID, а не root.
uploadArtifactToFs отказывает в назначении LocalFileSystem, если тестовый флаг не включён, с явным комментарием, что иначе пользователь перезапишет произвольный файл на драйвере:
if (fs.isInstanceOf[LocalFileSystem]) {
val allowDestLocalConf =
session.sessionState.conf.getConf(SQLConf.ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL)
.getOrElse(
session.conf.get("spark.connect.copyFromLocalToFs.allowDestLocal").contains("true"))
if (!allowDestLocalConf) {
throw new SparkUnsupportedOperationException("_LEGACY_ERROR_TEMP_3161")
}
}
fs.copyFromLocalFile(false, true, new FSPath(localPath.toString), destFSPath)
Ключ 3.5 статический, по умолчанию false. Замена в 4.0 — buildConf optional:
val ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL =
buildConf("spark.sql.artifact.copyFromLocalToFs.allowDestLocal")
.internal()
...
.booleanConf
.createOptional
buildConf не вызывает registerStaticConfigKey. isModifiable — это containsConfigKey && !isStaticConfigKey. RuntimeConfig.set отклоняет только статические ключи. Internal всё ещё можно установить. После SET getConf(ARTIFACT_...) возвращает Some(true), и запасной вариант getOrElse к старой статической конфигурации пропускается.
SPARK-58531 / PR 57730 «Convert spark.sql.artifact.copyFromLocalToFs.allowDestLocal to a static conf» закрыт 2026-08-05, Fix Version 4.3.0. JIRA: конфигурация «controls whether a caller can write to an arbitrary path on the driver's local filesystem, so it is meant to be set by whoever starts the driver.» 4.2.0 указан в Affects. На этом пине всё ещё runtime-конфигурация.
Уже зафиксированные ошибочные пути: рассмотрение Connect SQL как находки (FAQ); SET старого статического ключа (CANNOT_MODIFY_STATIC_CONFIG); утверждение, что абсолютное назначение Unix игнорирует родителя (оно присоединяется под корнем); раздувание записи файлов до RCE; привязка к 4.3.0 RC из-за того, что JIRA закрыта.
cd lab
./run.sh