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 在 4.3.0 中将其移至 StaticSQLConf。Connect 会话可以 SET 这个仅用于测试的本地目标门控,然后 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 | 高危: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 为静态配置。 |
| 认证 | 能够执行 ConfigRequest.SET 和 AddArtifacts 的 Connect 客户端。默认 Connect 无 PSK。 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18920 |
能够访问 gRPC(默认绑定端口 15002,除非运维人员设置了 spark.connect.authenticate.token,否则无 PSK)的 Spark Connect 客户端,可以关闭驱动启动时设置的仅用于测试的门控,然后以 Spark UID 覆盖驱动上选定的本地路径。
| 方法 | 汇聚点 | 权限 |
|---|---|---|
gRPC Config SET spark.sql.artifact.copyFromLocalToFs.allowDestLocal=true(或 SQL SET) | SparkConnectConfigHandler.handleSet -> RuntimeConfig.set -> SQLConf.setConfString | 完整性(会话配置,实验已验证): 翻转每会话的本地目标门控。本身不写入文件。不影响其他会话。实验:SET 返回 HTTP 200,modifiable=True。 |
gRPC AddArtifacts 名称 forward_to_fs + 绝对目标路径,CRC 正确 | 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 的目标从不查询该测试标志。 |
实验验证与推断: 会话 SET buildConf 键,然后以 uid 185 在工件沙箱之外写入 LocalFileSystem 目标,是实验验证的。以 Spark UID 覆盖进程可写的路径(驱动工作目录、全局可写的投放目录、运维人员稍后执行的已可执行脚本)是推断的。无 RIP 控制。不要将文件写入夸大为 RCE。
前提条件: Spark Connect 正在监听。客户端能够 SET 会话 SQL 配置和 AddArtifacts。目标必须是 Hadoop LocalFileSystem。默认 Connect 无 PSK;如果运维人员设置了令牌,该遗留问题仍需要该共享 Bearer。它是对本地目标控制的绕过,不是 PSK 绕过。
| 攻击面 | 实验 |
|---|---|
| 不 SET 直接复制 | 400 _LEGACY_ERROR_TEMP_3161,目标不存在 |
SET 旧的静态 spark.connect.copyFromLocalToFs.allowDestLocal | 400 CANNOT_MODIFY_STATIC_CONFIG |
工件名称 .. / 绝对暂存路径(ArtifactUtils.concatenatePaths) | INVALID_ARTIFACT_PATH |
非 forward_to_fs 工件 | 保持在 artifactRootDirectory/<sessionUUID>/ 下 |
| 无标志的 HDFS/云目标 | 文档化 API,不是本遗留问题 |
开放 Connect 上的未认证 SQL / Python UDF / Scala UdfPacket | 厂商 FAQ 作业提交;DROP |
| SPARK-52122 ML DefaultParamsReader | 此版本上的允许列表 + safeMLClassLoader |
无 shell。无堆转储。无 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 可选:
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 解决,修复版本 4.3.0。JIRA:该配置“控制调用者是否可以写入驱动本地文件系统上的任意路径,因此它应由启动驱动的人设置。”4.2.0 被列为受影响版本。此版本仍具有运行时配置。
已记录的弯路:将 Connect SQL 视为发现(FAQ);SET 旧的静态键(CANNOT_MODIFY_STATIC_CONFIG);声称 Unix 绝对目标会忽略父路径(它会在根下拼接);将文件写入夸大为 RCE;因为 JIRA 已解决而锁定 4.3.0 RC。
cd lab
./run.sh
回环 127.0.0.1:18920。会话配置门控和 LocalFileSystem 目标复制的利用工具(版本 v4.2.0 32f72996)。服务器 UID 185。无 SET 对照:400 _LEGACY_ERROR_TEMP_3161,目标不存在。SET 旧静态键对照:CANNOT_MODIFY_STATIC_CONFIG。会话 SET buildConf 键后复制:目标 /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS 位于 /opt/spark/work/artifacts/<sessionUUID>/ 之外,uid 185,内容 WITNESS。
health pin=v4.2.0 commit=32f7299601108917fb01920a54e084595b7b3bf8
server uid=185 artifact_root=/opt/spark/work/artifacts/<sessionUUID>
control-no-set status=400 condition=_LEGACY_ERROR_TEMP_3161
control-no-set dest-absent=yes
control-static-set status=400 error_class=CANNOT_MODIFY_STATIC_CONFIG
session-set status=200 modifiable=True
copy-after-set status=200 dest=/oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS
oracle dest=/oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS uid=185 outside-sandbox=yes content=SPARK-CONNECT-COPY-LOCAL-WITNESS
SUCCESS SPARK-CONNECT-COPY-LOCAL SPARK-CONNECT-COPY-LOCAL-WITNESS
将 spark.sql.artifact.copyFromLocalToFs.allowDestLocal 移至 StaticSQLConf,使会话无法 SET 它。这就是 4.3.0 上的 SPARK-58531 / PR 57730。在运维人员能够运行 4.3.0 之前:不要在无令牌和网络 ACL 的情况下暴露 Connect,也不要因为驱动启动时未设置该测试标志就认为它是关闭的。