abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-history-store-path
技术分析:https://abraxaslabs.tech/research/spark-history-store-path
类别: 文件写入(受配置限制) 可达性: 远程
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
HistoryServerDiskManager.appStorePath 执行 new File(appStoreDir, appId + extension) 时未做任何清理。Utils.sanitizeDirName 仅用于事件日志文件名。JSON 中的 App ID 通过 asText() 获取。列表操作存储 event.appId.orNull。Lease.commit 使用该字符串。App ID 为 ../ 时会将一个 .rdb 文件写到 spark.history.store.path 之外。默认未设置的 store.path 为内存模式(不写入)。写入内容为存储文件,而非 RCE。
| ID | 暂无 CVE |
| 类别 | 文件写入(受配置限制的 History 磁盘存储) |
| 可达性 | 远程(事件日志写入者,加上设置了 spark.history.store.path 的 History;UI ACL 默认关闭) |
| CWE | CWE-22 |
| CVSS | 高危:7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N(当设置了 spark.history.store.path 时) |
| 产品 | Apache Spark History Server |
| 受影响版本 | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) |
| 认证 | spark.history.fs.logDirectory(默认 file:/tmp/spark-events)的写入者;History 必须设置 spark.history.store.path |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18990。所引用 join 的利用链。在 store.path 之外生成见证 .rdb。 |
与 CVE-2025-54920 相同的受害者模型:History Server 指向攻击者可写入的日志目录。Jackson 不再构造 HiveConnection 类利用链。磁盘存储路径仍然信任事件 JSON 中的 App ID。
| 方法 | 汇聚点 | 权限 | 实验验证 vs 推断 |
|---|---|---|---|
植入一个事件日志,其 JSON App ID 为 ../<name>(或 ../../oracle/...)。等待扫描。GET 请求加载应用 UI / getAppUI / loadDiskStore / createDiskStore | HistoryServerDiskManager.appStorePath 然后 Lease.commit renameTo | 完整性: 以 History UID 在 {store.path}/apps 之外创建 .rdb(默认 RocksDB)或 .ldb(LevelDB)路径。在 gadget 上实验验证:escaped=yes,文件 /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb 包含见证内容。内容为存储文件,而非攻击者选择的宿主字节码,也不是 shell。 | 实验验证(gadget 在逃逸路径写入见证字节)。完整 History 对 RocksDB 目录的 renameTo 是相同的拼接。 |
| 当逃逸目标已是目录时使用相同的 App ID | Lease.commit if (dst.isDirectory()) deleteStore(dst) 然后 Utils.deleteRecursively | 可用性: 删除一个名称已以后端后缀结尾的现有目录,然后用租用的存储替换它。 | 推断(依据 HistoryServerDiskManager.scala 提交)。Gadget 写入了一个文件,未调用 deleteRecursively。 |
默认未设置 spark.history.store.path | LOCAL_STORE_DIR createOptional;diskManager = storePath.map | 已关闭。内存列表。无 HistoryServerDiskManager。实验验证 gated=yes written=no。 | 实验验证 |
先对 App ID 应用 Utils.sanitizeDirName | 事件日志文件名辅助函数 | 保持在 apps/ 内。/ 和 : 变为 -。实验验证为否定。 | 实验验证 |
在设置 store.path 的情况下控制 App ID local-control | 相同的拼接 | 写入 {store.path}/apps/local-control.rdb。保持在 apps/ 内。 | 实验验证 |
相邻已关闭项: 默认未设置存储(内存)。Jackson sparkEventFromJson 仍要求 SparkListenerEvent(CVE-2025-54920)。URL getAppUI 回退 eventlog_v2_ + nameForAppAndAttempt 已被净化。列表 KV 键不是文件系统路径。HybridStore / kvstore 映射器没有 Id.CLASS。History HTTP 不接受上传路径。压缩默认关闭。清理器默认关闭。开放集群上的作业提交(SECURITY.md FAQ)。ML 模型加载(docs/ml-security.md)。此遗留问题不是宿主 RCE。
我固定使用 Spark 4.2.0,不是 master,也不是 4.3 RC。二十八次搜寻。公开的 History Jackson RCE 在此版本上已关闭。事件日志写入器已调用 Utils.sanitizeDirName,因此日志文件不能包含 /。磁盘缓存文件名使用原始 App ID。
private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
new File(appStoreDir, fileName)
}
appStoreDir 为 {spark.history.store.path}/apps。Unix 下 new File(parent, "../../oracle/name.rdb") 会解析到父目录之外。JsonProtocol.applicationStartFromJson 调用 json.get("App ID").asText()。AppListingListener.onApplicationStart 存储 event.appId.orNull。lease.commit(appId, attempt.info.attemptId) 将该字符串传入 appStorePath。
已记录的误区:将默认未设置的 store.path 视为写入(实际为内存中);期望 Unix 下 new File(dir, "/abs") 忽略父目录(实际不会;残留为 ..);因后缀为 .rdb 而声称存在 RCE;在 join 即为 oracle 时启动完整的 History Server;认为 sanitizeDirName 覆盖了此路径。
python3 spark-history-store-path-Abraxas-Labs.py
或
cd lab
./run.sh
127.0.0.1:18990 上 HistoryServerDiskManager.appStorePath 的 Gadget。将摘录固定到 lab/pin/ 下。未启动完整的 History Server。将其绑定到回环地址。
SUCCESS SPARK-HISTORY-STORE-PATH how=gadget escaped=yes gated=yes outside=/work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb SPARK-HISTORY-STORE-PATH-WITNESS
在 appStorePath 中、调用 new File 之前,对 appId 和 attemptId 运行 Utils.sanitizeDirName(或拒绝 .. / / / NUL)。拼接路径后,要求 dst.getCanonicalFile() 仍位于 appStoreDir.getCanonicalFile() 之下。未设置 spark.history.store.path 时仍保持安全默认值。
32f7299601108917fb01920a54e084595b7b3bf8)HistoryServerDiskManager.scala 中的 appStorePath / Lease.commitFsHistoryProvider.scala 中的 diskManager / loadDiskStore / onApplicationStartJsonProtocol.scala 中的 applicationStartFromJsonUtils.scala 中的 sanitizeDirNameHistory.scala 中的 LOCAL_STORE_DIR(可选)GNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境使用。不提供任何担保。