Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
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.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
spark-history-store-path — Apache Spark 4.2.0 - Alta - Remota - Escritura de archivos - History appStorePath App ID no saneado | Kitploit
Herramientas/GitHubGitHub/abraxas/spark-history-store-path
Análisis EstáticoAnálisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónLabs y Práctica
GitHub
abraxas/spark-history-store-path

spark-history-store-path

Apache Spark 4.2.0 - Alta - Remota - Escritura de archivos - History appStorePath App ID no saneado

Ver Repositorio
hace 2 díasAú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

Abraxas Labs - spark-history-store-path

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  spark-history-store-path

spark-history-store-path

Clase: Escritura de archivos (condicionada por configuración) Alcance: Remoto

Apache Spark 4.2.0 (32f72996) - Apache Software Foundation

HistoryServerDiskManager.appStorePath hace new File(appStoreDir, appId + extension) sin sanitizar. Utils.sanitizeDirName es solo para nombres de archivo del registro de eventos. El App ID del JSON es asText(). El listado almacena event.appId.orNull. Lease.commit usa esa cadena. El App ID ../ escribe un .rdb fuera de spark.history.store.path. El valor predeterminado no establecido de store.path es en memoria (sin escritura). El contenido es un archivo de almacén, no RCE.

IDsin CVE todavía
ClaseEscritura de archivos (almacén en disco del History condicionado por configuración)
AlcanceRemoto (escritor del registro de eventos más History con spark.history.store.path establecido; ACLs de la UI desactivadas por defecto)
CWECWE-22
CVSSAlto: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N cuando spark.history.store.path está establecido
ProductoApache Spark History Server
Afectado4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8)
Autenticaciónescritor de spark.history.fs.logDirectory (por defecto file:/tmp/spark-events); History debe tener spark.history.store.path establecido
LicenciaGNU Affero GPL v3.0
Laboratorio127.0.0.1:18990 únicamente. Gadget del join citado. Testigo .rdb fuera de store.path.

Qué puede hacer un atacante

Mismo modelo de víctima que CVE-2025-54920: History Server apuntando a un directorio de registros en el que el atacante puede escribir. Jackson ya no construye gadgets de la clase HiveConnection. La ruta del almacén en disco todavía confía en el App ID del JSON de eventos.

MétodoSinkPrivilegioLaboratorio vs inferido
Plantar un registro de eventos cuyo App ID en JSON sea ../<name> (o ../../oracle/...). Esperar el escaneo. GET que carga la UI de la app / getAppUI / loadDiskStore / createDiskStoreHistoryServerDiskManager.appStorePath luego Lease.commit renameToIntegridad: crear una ruta .rdb (RocksDB por defecto) o .ldb (LevelDB) fuera de {store.path}/apps como el UID de History. Probado en laboratorio sobre el gadget: escaped=yes, el archivo /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb contiene el testigo. El contenido es un archivo de almacén, no bytecode del host elegido por el atacante, no un shell.Probado en laboratorio (escritura del gadget de bytes testigo en la ruta escapada). El renameTo completo de History de un directorio RocksDB es el mismo join.
Mismo App ID cuando el destino escapado ya es un directorioLease.commit if (dst.isDirectory()) deleteStore(dst) luego Utils.deleteRecursivelyDisponibilidad: eliminar un directorio existente cuyo nombre ya termina en el sufijo del backend, luego reemplazarlo con el almacén arrendado.Inferido (fijar HistoryServerDiskManager.scala commit). El gadget escribió un archivo, no llamó a deleteRecursively.
spark.history.store.path predeterminado no establecidoLOCAL_STORE_DIR createOptional; diskManager = storePath.mapCerrado. Listado en memoria. Sin HistoryServerDiskManager. Probado en laboratorio gated=yes written=no.Probado en laboratorio
Aplicar Utils.sanitizeDirName al App ID primerohelper del nombre de archivo del registro de eventosPermanece dentro de apps/. / y : se convierten en -. Probado en laboratorio negativo.Probado en laboratorio
Controlar el App ID local-control con store.path establecidomismo joinEscribe {store.path}/apps/local-control.rdb. Permanece dentro de apps/.Probado en laboratorio

Adyacente cerrado: almacén predeterminado no establecido (en memoria). Jackson sparkEventFromJson todavía requiere SparkListenerEvent (CVE-2025-54920). El fallback de URL getAppUI eventlog_v2_ + nameForAppAndAttempt sí está sanitizado. La clave KV del listado no es una ruta del sistema de archivos. El mapper de HybridStore / kvstore no tiene Id.CLASS. History HTTP no acepta una ruta de carga. Compactación desactivada por defecto. Cleaner desactivado por defecto. Envío de trabajos en un clúster abierto (SECURITY.md FAQ). Carga de modelos ML (docs/ml-security.md). Este remanente no es RCE del host.

Cómo lo encontré

Fijé Spark 4.2.0, no master, no un RC de 4.3. Veintiocho búsquedas. El RCE público de Jackson en History está cerrado en este peel. Los escritores del registro de eventos ya llaman a Utils.sanitizeDirName por lo que el archivo de registro no puede contener /. El nombre del archivo de caché en disco usa el App ID sin procesar.

private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
  val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
  new File(appStoreDir, fileName)
}

appStoreDir es {spark.history.store.path}/apps. En Unix new File(parent, "../../oracle/name.rdb") resuelve fuera del padre. JsonProtocol.applicationStartFromJson toma json.get("App ID").asText(). AppListingListener.onApplicationStart almacena event.appId.orNull. lease.commit(appId, attempt.info.attemptId) alimenta esa cadena a appStorePath.

Caminos equivocados ya registrados: tratar el store.path predeterminado no establecido como una escritura (está en memoria); esperar que new File(dir, "/abs") de Unix ignore el padre (no lo hace; el remanente es ..); afirmar RCE porque el sufijo es .rdb; arrancar un History Server completo cuando el join es el oráculo; tratar sanitizeDirName como si cubriera esta ruta.

Laboratorio

python3 spark-history-store-path-Abraxas-Labs.py

o

cd lab
./run.sh

Gadget de HistoryServerDiskManager.appStorePath en 127.0.0.1:18990. Extractos fijados bajo lab/pin/. No se arranca el History Server completo. Enlázalo a loopback.

SUCCESS SPARK-HISTORY-STORE-PATH how=gadget escaped=yes gated=yes outside=/work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb SPARK-HISTORY-STORE-PATH-WITNESS

La solución

Ejecutar Utils.sanitizeDirName (o rechazar .. / / / NUL) sobre appId y attemptId dentro de appStorePath antes de new File. Tras el join, exigir que dst.getCanonicalFile() permanezca bajo appStoreDir.getCanonicalFile(). No establecer spark.history.store.path sigue siendo el valor predeterminado seguro.

Referencias

Descargar herramienta