Apache Spark 4.2.0 - Alta - Remota - Escritura de archivos - Sesión Connect SET copyFromLocalToFs.allowDestLocal
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-connect-copy-local
Análisis: https://abraxaslabs.tech/research/spark-connect-copy-local
Clase: Escritura de archivos (residuo de SPARK-58531; estático solo en 4.3.0) Alcance: Remoto
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
spark.sql.artifact.copyFromLocalToFs.allowDestLocal sigue siendo buildConf (configurable por sesión) en esta versión fijada. SPARK-58531 lo movió a StaticSQLConf en 4.3.0. Una sesión de Connect puede establecer mediante SET la puerta de destino local exclusiva para pruebas y, a continuación, copyFromLocalToFs / AddArtifacts forward_to_fs/<abs> sobrescribe una ruta local del driver con el UID de Spark.
La sesión de Connect hace SET y luego forward_to_fs escribe archivos locales del driver. El destino debe ser LocalFileSystem. No es RCE.
| ID | aún sin CVE (residuo de SPARK-58531) |
| Clase | Escritura de archivos (residuo de SPARK-58531; estático solo en 4.3.0) |
| Alcance | Remoto (Spark Connect Config + AddArtifacts) |
| CWE | CWE-22, CWE-732 |
| 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 (ASF) |
| Afectado | Apache Spark 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). 4.3.0 es estático. |
| Autenticación | Cliente Connect que pueda ConfigRequest.SET y AddArtifacts. Connect por defecto no tiene PSK. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1:18920 |
Un cliente de Spark Connect que pueda alcanzar gRPC (puerto de escucha por defecto 15002, sin PSK a menos que el operador haya configurado spark.connect.authenticate.token) puede desactivar una puerta exclusiva para pruebas con la que se inició el driver y, a continuación, sobrescribir una ruta local elegida en el driver con el UID de Spark.
| Método | Sumidero | Privilegio |
|---|---|---|
gRPC Config SET spark.sql.artifact.copyFromLocalToFs.allowDestLocal=true (o SQL SET) | SparkConnectConfigHandler.handleSet -> RuntimeConfig.set -> SQLConf.setConfString | Integridad (conf. de sesión, probado en laboratorio): desactiva la puerta de destino local por sesión. No escribe un archivo por sí mismo. No cambia otras sesiones. Laboratorio: SET HTTP 200, modifiable=True. |
gRPC AddArtifacts nombre forward_to_fs + destino absoluto, CRC correcto | ArtifactManager.uploadArtifactToFs -> LocalFileSystem.copyFromLocalFile(delSrc=false, overwrite=true) | Integridad (sobrescritura de archivo, probado en laboratorio): escribe/sobrescribe una ruta local del driver con el UID de Spark, fuera de artifactRootDirectory/<sessionUUID>/. Laboratorio: destino /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS, uid 185, contenido WITNESS. |
| El mismo AddArtifacts hacia un destino HDFS/S3/ABFS | copyFromLocalToFs documentado | Integridad (FS de clúster, por diseño): no es este residuo. Un destino que no sea LocalFileSystem nunca consulta el flag de pruebas. |
Probado en laboratorio vs inferido: el SET de sesión de la clave buildConf, seguido de la escritura en un destino LocalFileSystem como uid 185 fuera del sandbox de artefactos, está probado en laboratorio. La sobrescritura con el UID de Spark de una ruta en la que el proceso puede escribir (directorio de trabajo del driver, un directorio de depósito con permisos de escritura para todos, un script ya ejecutable que el operador ejecute después) es inferido. Sin control de RIP. No hay que inflar la escritura de archivos a RCE.
Requiere: Spark Connect escuchando. Cliente que pueda hacer SET de la conf SQL de sesión y AddArtifacts. El destino debe ser Hadoop LocalFileSystem. Connect por defecto no tiene PSK; si el operador configuró un token, el remanente aún necesita ese Bearer compartido. Es un bypass del control de destino local, no un bypass de PSK.
| Superficie | Laboratorio |
|---|---|
| Copia sin SET | 400 _LEGACY_ERROR_TEMP_3161, destino ausente |
SET del antiguo spark.connect.copyFromLocalToFs.allowDestLocal estático | 400 CANNOT_MODIFY_STATIC_CONFIG |
Nombre de artefacto .. / staging absoluto (ArtifactUtils.concatenatePaths) | INVALID_ARTIFACT_PATH |
Artefactos que no son forward_to_fs | permanecen bajo artifactRootDirectory/<sessionUUID>/ |
| Destino HDFS/cloud sin el flag | API documentada, no este remanente |
SQL no autenticado / UDF de Python / UdfPacket de Scala en Connect abierto | envío de job del FAQ del proveedor; DROP |
| SPARK-52122 ML DefaultParamsReader | allowlist + safeMLClassLoader en este pin |
Sin shell. Sin volcado de heap. Sin omisión de PSK de Connect. El destino aún tiene que ser LocalFileSystem. La escritura se hace como el UID de Spark, no como root.
uploadArtifactToFs rechaza un destino LocalFileSystem a menos que el flag de pruebas esté activado, con un comentario explícito de que, de lo contrario, un usuario sobrescribe un archivo arbitrario en el driver:
if (fs.isInstanceOf[LocalFileSystem]) {
val allowDestLocalConf =
session.sessionState.conf.getConf(SQLConf.ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL)
.getOrElse(
session.conf.get("spark.connect.copyFromLocalToFs.allowDestLocal").contains("true"))
if (!allowDestLocalConf) {
throw new SparkUnsupportedOperationException("_LEGACY_ERROR_TEMP_3161")
}
}
fs.copyFromLocalFile(false, true, new FSPath(localPath.toString), destFSPath)
La clave 3.5 es estática, por defecto false. El reemplazo en 4.0 es buildConf opcional:
val ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL =
buildConf("spark.sql.artifact.copyFromLocalToFs.allowDestLocal")
.internal()
...
.booleanConf
.createOptional
buildConf no llama a registerStaticConfigKey. isModifiable es containsConfigKey && !isStaticConfigKey. RuntimeConfig.set solo rechaza las claves estáticas. La interna sigue siendo configurable. Tras SET, getConf(ARTIFACT_...) devuelve Some(true) y se omite el fallback de getOrElse a la conf estática antigua.
SPARK-58531 / PR 57730 "Convert spark.sql.artifact.copyFromLocalToFs.allowDestLocal to a static conf" está resuelto el 2026-08-05, Fix Version 4.3.0. JIRA: la conf "controls whether a caller can write to an arbitrary path on the driver's local filesystem, so it is meant to be set by whoever starts the driver." 4.2.0 aparece en Affects. Este pin todavía tiene la conf de runtime.