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
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 este pin. SPARK-58531 lo movió a StaticSQLConf en 4.3.0. Una sesión de Connect puede hacer SET de la puerta de destino local solo para pruebas, y luego 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 | sin CVE aún (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 solo para pruebas con la que se inició el driver, y luego sobrescribir una ruta local elegida en el driver con el UID de Spark.
| Método | Destino | 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 ok | 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 a 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 la bandera de pruebas. |
Probado en laboratorio vs inferido: el SET de sesión de la clave buildConf, y luego la escritura en destino LocalFileSystem como uid 185 fuera del sandbox de artefactos, está probado en laboratorio. La sobrescritura con el UID de Spark de una ruta que el proceso puede escribir (directorio de trabajo del driver, un drop con permisos de escritura para todos, un script ya ejecutable que el operador ejecuta después) es inferido. Sin control de RIP. No infles 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 LocalFileSystem de Hadoop. Connect por defecto no tiene PSK; si el operador configuró un token, el residuo 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 de la antigua clave estática spark.connect.copyFromLocalToFs.allowDestLocal | 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 la bandera | API documentada, no este residuo |
SQL sin autenticación / Python UDF / Scala UdfPacket en Connect abierto | envío de trabajo según 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 es con el UID de Spark, no como root.
uploadArtifactToFs rechaza un destino LocalFileSystem a menos que la bandera de pruebas esté activada, 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 de 3.5 es estática, por defecto false. El reemplazo de 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 claves estáticas. Internal sigue siendo configurable. Después del SET, getConf(ARTIFACT_...) devuelve Some(true) y se omite el fallback getOrElse a la antigua conf estática.
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 listado en Affects. Este pin todavía tiene la conf de runtime.
Caminos equivocados ya registrados: tratar Connect SQL como un hallazgo (FAQ); SET de la antigua clave estática (CANNOT_MODIFY_STATIC_CONFIG); afirmar que un destino absoluto de Unix ignora el padre (se une bajo root); inflar la escritura de archivos a RCE; fijar el RC de 4.3.0 porque el JIRA está resuelto.
cd lab
./run.sh