Apache Spark 4.2.0 - Alta - Remota - Escritura de archivos - Connect archives hash-fragment dest
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-connect-archive-fragment
Clase: Escritura de archivos Alcance: Remoto
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
Los nombres de AddArtifacts de Spark Connect con la forma archives/<file>#<fragment> se dividen por el carácter hash / signo de número. Los bytes del zip pasan por concatenatePaths. El fragmento no. SparkContext.addFile(..., isArchive=true) luego hace new File(root, uri.getFragment), deleteRecursively(dest) y unpack en el driver. Los ejecutores construyen el mismo dest. SPARK-33530 documentó #name como un nombre de directorio bajo SparkFiles. Un fragmento de ../<outside> es el dest.
Connect archives/<file>#../<path> descomprime y hace deleteRecursively fuera del directorio SparkFiles de la sesión como el UID de Spark.
| ID | sin CVE aún |
| Clase | Escritura de archivos |
| Alcance | Remoto (cliente Spark Connect; AddArtifacts con archive=True) |
| CWE | CWE-22 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H |
| Producto | Apache Spark |
| Afectado | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) Connect archives/ dest de fragmento hash |
| Auth | Cliente Connect. Connect por defecto no tiene PSK (spark.connect.authenticate.token sin establecer). PR:L si hay PSK / ACL de red; PR:N en Connect abierto por defecto. |
| Licencia | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Fijé el tag v4.2.0 peel 32f72996 (2026-07-11, v4.2.0-rc6). El lab es un gadget Java del split de hash de StagedArtifact fijado, ArtifactUtils.concatenatePaths, new File(root, fragment), deleteRecursively y descompresión de zip contenida en dest. HTTP en loopback sobre 127.0.0.1:18930. El PySpark oficial ya copia un fragmento de URI en el nombre del archivo.
Esto no es RCE. Los miembros del zip que salen de dest son rechazados (clase Hadoop FileUtil.unZip / HADOOP-13649). Lo que queda es el propio dest.
| Método | Sink | Privilegio |
|---|---|---|
Connect AddArtifacts nombre archives/<file>#../<outside> | SparkContext.addFile dest new File(root, uri.getFragment) luego Utils.unpack (SparkContext.scala:1891-1900) | Integridad (probado en lab): escribe miembros del archivo como el UID de Spark fuera de SparkFiles de la sesión. Dest del lab canónico /lab/oracle/SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS, uid 1000, dest-outside=yes. |
| Mismo dest, antes de unpack | Utils.deleteRecursively(dest) (SparkContext.scala:1899) | Disponibilidad (probado en lab): canary de dest eliminado. Lab deleteRecursively=yes; canary.txt bajo dest no sobrevivió. Hermano /lab/oracle/canary-keep.txt conservado. |
| Misma primitiva en cada ejecutor que obtiene el archivo | Dest de Executor new File(root, sourceURI.getFragment) luego deleteRecursively + unpack (Executor.scala:1468-1477) | Integridad / disponibilidad (inferido): misma construcción de dest en los ejecutores. El lab es el gadget del lado del driver. |
Nombre de artefacto files/../../... o archives/../../... | ArtifactUtils.concatenatePaths (ArtifactUtils.scala:26-39) | Cerrado (probado en lab): INVALID_ARTIFACT_PATH. |
Fragmento absoluto Unix /abs | new File(root, "/abs") | Cerrado en Unix (probado en lab): el padre no se ignora; dest se une bajo root. Dest del lab /lab/root/lab/oracle/abs-..., outside=false, canary abs conservado. |
Añadir jars/ / classes/ de la propia sesión | URLClassLoader de la sesión | Cerrado como remanente: la carga de JAR de la propia sesión es por diseño. No es este hallazgo. |
| Carga de jar entre sesiones | Aislamiento de Connect spark.sql.artifact.isolation.enabled=true | Cerrado: las sesiones aisladas leen addedJars(sessionUUID). |
Probado en lab vs inferido: dest de fragmento hash fuera de SparkFiles, canary de dest eliminado, canary hermano conservado, concatenación de nombres files/ y archives/ rechazada, fragmento absoluto Unix se une bajo root, uid 1000, descompresión de zip contenida en dest: lab. Obtención por el ejecutor del mismo dest: inferido (mismo constructor). Sobrescritura de una clase cargada posteriormente en el classpath del driver: cadena inferida, no demostrada, no inflada a RCE.
Requiere: un cliente Connect que pueda AddArtifacts con archive=True. El PySpark oficial spark.addArtifact(..., archive=True) ya coloca un fragmento de URI no vacío en el nombre (python/pyspark/sql/connect/client/artifact.py:223-236). Connect por defecto no tiene PSK.
| Superficie | Lab |
|---|---|
Nombre de artefacto .. / absoluto a través de concatenatePaths | files-concat=rejected INVALID_ARTIFACT_PATH |
Recorrido del nombre archives/ archives/../../... | mismo INVALID_ARTIFACT_PATH |
Unix new File(root, "/abs") | dest bajo root; canary abs oracle conservado |
| Zip-slip de miembro de zip bajo un dest contenido | el gadget rechaza miembros que escapan de dest (clase HADOOP-13649). No es este remanente. |
Concatenación de ruta files/ (sin split de hash) | el fragmento no se toma; el nombre sigue siendo concatenatePaths |
| Aislamiento de jar entre sesiones | activado por defecto para Connect |
| SQL / Python sin autenticación en Connect abierto | envío de trabajos por diseño (SECURITY.md FAQ). No es este remanente. |
| Envío de trabajos / carga de modelo ML | por diseño |
StagedArtifact divide solo los nombres que comienzan con archives más el File.separator del servidor, y solo por #:
val splits = name.split("#")
if (splits.length == 2) {
(splits(0), Some(splits(1)))
}
splits(0) se prepara vía concatenatePaths. splits(1) se conserva como fragment. ArtifactManager adjunta ese fragmento a la URI del file-server de la sesión y llama a SparkContext.addArchive. Dest es new File(root, uri.getFragment). Las pruebas de SPARK-33530 usan sc.addArchive(s"${jarFile.getAbsolutePath}#foo") como un nombre de directorio bajo SparkFiles.
La primera pasada de búsqueda afirmó que un fragmento absoluto Unix ignora root (ignorar el padre en java.io.File para hijos absolutos). En Unix, new File(root, "/abs") elimina la barra inicial y se une bajo root. El padre solo se ignora en Windows. El recorrido Alto en este pin es #../.
cd lab
./run.sh
Loopback 127.0.0.1:18930. Proyecto Compose spark-connect-archive-fragment. Imagen eclipse-temurin:21-jdk-jammy, uid de runtime 1000. Witness SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS. Última línea:
SUCCESS SPARK-CONNECT-ARCHIVE-FRAGMENT how=gadget dest-outside=yes deleteRecursively=yes files-concat=rejected uid=1000 SPARK-CONNECT-ARCHIVE-FRAGMENT-WITNESS