Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
spark-connect-archive-fragment — Apache Spark 4.2.0 - 高危 - 远程 - 文件写入 - Connect archives hash-fragment dest | Kitploit
工具/GitHubGitHub/abraxas/spark-connect-archive-fragment
漏洞分析漏洞利用Web应用程序漏洞利用安全虚拟化渗透测试红队实验室与实践
GitHubabraxas/spark-connect-archive-fragment

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →

spark-connect-archive-fragment

Apache Spark 4.2.0 - 高危 - 远程 - 文件写入 - Connect archives hash-fragment dest

查看仓库
3天前尚未审核
分享

Abraxas Labs - spark-connect-archive-fragment

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  spark-connect-archive-fragment

spark-connect-archive-fragment

类别: 文件写入 可达性: 远程

Apache Spark 4.2.0 (32f72996) - Apache Software Foundation

Spark Connect AddArtifacts 中形如 archives/<file>#<fragment> 的名称会按井号 / 数字符号字符进行拆分。zip 字节流会经过 concatenatePaths。而 fragment 不会。随后 SparkContext.addFile(..., isArchive=true) 会在 driver 上执行 new File(root, uri.getFragment)、deleteRecursively(dest) 以及 unpack。Executor 会构建相同的 dest。SPARK-33530 将 #name 记录为 SparkFiles 下的目录名。fragment 为 ../<outside> 时即为 dest。

Connect archives/<file>#../<path> 会以 Spark UID 身份在会话 SparkFiles 目录之外解包并执行 deleteRecursively。

ID暂无 CVE
类别文件写入
可达性远程(Spark Connect 客户端;AddArtifacts 且 archive=True)
CWECWE-22
CVSS高危:8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
产品Apache Spark
受影响版本4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) Connect archives/ 井号 fragment dest
认证Connect 客户端。默认 Connect 无 PSK(spark.connect.authenticate.token 未设置)。若启用 PSK / 网络 ACL 则为 PR:L;默认开放 Connect 为 PR:N。
许可证GNU Affero GPL v3.0
实验环境仅 127.0.0.1

我固定了标签 v4.2.0 的 peel 32f72996(2026-07-11,v4.2.0-rc6)。实验环境是一个 Java gadget,复现了固定版本中 StagedArtifact 的井号拆分、ArtifactUtils.concatenatePaths、new File(root, fragment)、deleteRecursively 以及 dest 内 zip 解包。回环 HTTP 位于 127.0.0.1:18930。官方 PySpark 已经会将 URI fragment 复制到归档名称中。

这不是 RCE。走出 dest 的 zip 成员会被拒绝(Hadoop FileUtil.unZip / HADOOP-13649 类)。遗留问题在于 dest 本身。

攻击者能做什么

方法汇聚点权限
Connect AddArtifacts 名称 archives/<file>#../<outside>SparkContext.addFile dest new File(root, uri.getFragment) 然后 Utils.unpack(SparkContext.scala:1891-1900)完整性(实验证明): 以 Spark UID 身份在会话 SparkFiles 之外写入归档成员。实验 dest 规范路径 /lab/oracle/SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS,uid 1000,dest-outside=yes。
同一 dest,在解包之前Utils.deleteRecursively(dest)(SparkContext.scala:1899)可用性(实验证明): dest 金丝雀文件消失。实验 deleteRecursively=yes;dest 下的 canary.txt 未能存活。同级的 /lab/oracle/canary-keep.txt 保留。
在获取归档的每个 executor 上使用同一原语Executor dest new File(root, sourceURI.getFragment) 然后 deleteRecursively + unpack(Executor.scala:1468-1477)完整性 / 可用性(推断): executor 上构建相同的 dest。实验为 driver 侧 gadget。
制品名称 files/../../... 或 archives/../../...ArtifactUtils.concatenatePaths(ArtifactUtils.scala:26-39)已关闭(实验证明): INVALID_ARTIFACT_PATH。
Unix 绝对 fragment /absnew File(root, "/abs")在 Unix 上已关闭(实验证明): 父路径未被忽略;dest 拼接在 root 之下。实验 dest /lab/root/lab/oracle/abs-...,outside=false,abs 金丝雀保留。
本会话 jars/ / classes/ 添加会话 URLClassLoader作为遗留问题已关闭: 本会话 JAR 加载是设计使然。不属于本发现。
跨会话 jar 加载Connect 隔离 spark.sql.artifact.isolation.enabled=true已关闭: 隔离会话读取 addedJars(sessionUUID)。

实验证明 vs 推断: 井号 fragment dest 位于 SparkFiles 之外、dest 金丝雀被删除、同级金丝雀保留、files/ 与 archives/ 名称拼接被拒绝、Unix 绝对 fragment 拼接在 root 之下、uid 1000、dest 内 zip 解包:实验证明。executor 获取同一 dest:推断(相同构造函数)。覆盖 driver classpath 上后续加载的类:推断链,未演示,未夸大为 RCE。

前提条件: 一个能够以 archive=True 执行 AddArtifacts 的 Connect 客户端。官方 PySpark spark.addArtifact(..., archive=True) 已经会将非空 URI fragment 放入名称中(python/pyspark/sql/connect/client/artifact.py:223-236)。默认 Connect 无 PSK。

保持关闭的相邻攻击面

攻击面实验
制品名称 .. / 绝对路径经由 concatenatePathsfiles-concat=rejected INVALID_ARTIFACT_PATH
archives/ 名称遍历 archives/../../...同样 INVALID_ARTIFACT_PATH
Unix new File(root, "/abs")dest 位于 root 之下;abs oracle 金丝雀保留
受限 dest 下的 zip 成员 zip-slipgadget 拒绝逃逸 dest 的成员(HADOOP-13649 类)。不属于本遗留问题。
files/ 路径拼接(无井号拆分)不取 fragment;名称仍走 concatenatePaths
跨会话 jar 隔离Connect 默认开启
开放 Connect 上的未认证 SQL / Python设计使然的作业提交(SECURITY.md FAQ)。不属于本遗留问题。
作业提交 / ML 模型加载设计使然

我是如何发现的

StagedArtifact 仅拆分以 archives 加服务器 File.separator 开头的名称,且仅按 # 拆分:

val splits = name.split("#")
if (splits.length == 2) {
  (splits(0), Some(splits(1)))
}

splits(0) 通过 concatenatePaths 暂存。splits(1) 保留为 fragment。ArtifactManager 将该 fragment 附加到会话文件服务器 URI 并调用 SparkContext.addArchive。dest 为 new File(root, uri.getFragment)。SPARK-33530 测试了 sc.addArchive(s"${jarFile.getAbsolutePath}#foo") 作为 SparkFiles 下的目录名。

首轮排查曾声称 Unix 绝对 fragment 会忽略 root(java.io.File 对绝对子路径忽略父路径)。在 Unix 上,new File(root, "/abs") 会去掉前导斜杠并拼接在 root 之下。父路径仅在 Windows 上被忽略。此固定版本上的高危遍历是 #../。

实验环境

cd lab
./run.sh

回环 127.0.0.1:18930。Compose 项目 spark-connect-archive-fragment。镜像 eclipse-temurin:21-jdk-jammy,运行时 uid 1000。见证文件 SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS。最后一行:

SUCCESS SPARK-CONNECT-ARCHIVE-FRAGMENT how=gadget dest-outside=yes deleteRecursively=yes files-concat=rejected uid=1000 SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS

CONTROL:archives/payload.zip 解包到 /lab/root 之下。ATTACK:archives/payload-frag.zip#../oracle/SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS dest 规范路径 /lab/oracle/...,金丝雀被删除,pwned.txt 以 uid 1000 写入。Unix 绝对 fragment 拼接在 root 之下。栈在 EXIT 时拆除。

修复方案

将归档 fragment 通过与制品名称相同的 stay-under-root 检查(concatenatePaths,或拒绝 .. / 绝对路径)。保持 SPARK-33530 的 #name 作为 SparkFiles 下的单一目录组件。不要对未通过该检查的 dest 执行 deleteRecursively。Executor 使用相同的构造函数;两者都要修复。

在此之前,不要将 Spark Connect 暴露给任何你尚未信任其拥有 Spark UID 文件系统访问权限的人。在 spark.connect.authenticate.token 上设置 PSK 可将门槛提高到令牌持有者。它不会净化 fragment。

参考资料

下载工具