Apache Spark 4.2.0 - Élevé - À distance - Écriture de fichier - Session Connect SET copyFromLocalToFs.allowDestLocal
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-connect-copy-local
Article : https://abraxaslabs.tech/research/spark-connect-copy-local
Classe : Écriture de fichier (résidu de SPARK-58531 ; statique uniquement sur 4.3.0) Portée : Distante
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
spark.sql.artifact.copyFromLocalToFs.allowDestLocal est toujours buildConf (modifiable par session) sur ce commit. SPARK-58531 l'a déplacé vers StaticSQLConf dans 4.3.0. Une session Connect peut définir (SET) le garde-fou de destination locale réservé aux tests, puis copyFromLocalToFs / AddArtifacts forward_to_fs/<abs> écrase un chemin local au driver avec l'UID Spark.
Une session Connect fait un SET puis forward_to_fs écrit des fichiers locaux au driver. La destination doit être LocalFileSystem. Pas de RCE.
| ID | pas encore de CVE (résidu de SPARK-58531) |
| Classe | Écriture de fichier (résidu de SPARK-58531 ; statique uniquement sur 4.3.0) |
| Portée | Distante (Spark Connect Config + AddArtifacts) |
| CWE | CWE-22, CWE-732 |
| CVSS | Élevé : 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H |
| Produit | Apache Spark (ASF) |
| Affecté | Apache Spark 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). 4.3.0 est statique. |
| Auth | Client Connect capable de ConfigRequest.SET et AddArtifacts. Par défaut, Connect n'a pas de PSK. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18920 uniquement |
Un client Spark Connect capable d'atteindre gRPC (port d'écoute par défaut 15002, sans PSK sauf si l'opérateur a défini spark.connect.authenticate.token) peut désactiver un garde-fou réservé aux tests avec lequel le driver a été démarré, puis écraser un chemin local choisi sur le driver avec l'UID Spark.
| Méthode | Point d'écriture | Privilège |
|---|---|---|
gRPC Config SET spark.sql.artifact.copyFromLocalToFs.allowDestLocal=true (ou SQL SET) | SparkConnectConfigHandler.handleSet -> RuntimeConfig.set -> SQLConf.setConfString | Intégrité (conf de session, prouvé en lab) : désactive le garde-fou de destination locale par session. N'écrit pas de fichier à lui seul. Ne modifie pas les autres sessions. Lab : SET HTTP 200, modifiable=True. |
gRPC AddArtifacts nom forward_to_fs + destination absolue, CRC ok | ArtifactManager.uploadArtifactToFs -> LocalFileSystem.copyFromLocalFile(delSrc=false, overwrite=true) | Intégrité (écrasement de fichier, prouvé en lab) : écrit/écrase un chemin local au driver avec l'UID Spark, hors de artifactRootDirectory/<sessionUUID>/. Lab : destination /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS, uid 185, contenu WITNESS. |
| Même AddArtifacts vers une destination HDFS/S3/ABFS | copyFromLocalToFs documenté | Intégrité (FS de cluster, par conception) : pas ce résidu. Une destination qui n'est pas LocalFileSystem ne consulte jamais le garde-fou de test. |
Prouvé en lab vs déduit : le SET de session de la clé buildConf, puis l'écriture vers une destination LocalFileSystem avec l'uid 185 hors du bac à sable des artefacts, est prouvé en lab. L'écrasement par l'UID Spark d'un chemin que le processus peut écrire (répertoire de travail du driver, un dépôt accessible en écriture par tous, un script déjà exécutable que l'opérateur lance ensuite) est déduit. Aucun contrôle RIP. Ne pas gonfler l'écriture de fichier en RCE.
Prérequis : Spark Connect en écoute. Client capable de définir (SET) la configuration SQL de session et d'utiliser AddArtifacts. La destination doit être le LocalFileSystem Hadoop. Le Connect par défaut n'a pas de PSK ; si l'opérateur a défini un token, le leftover a toujours besoin de ce Bearer partagé. Il s'agit d'un contournement du contrôle de destination locale, pas d'un contournement du PSK.
| Surface | Lab |
|---|---|
| Copie sans SET | 400 _LEGACY_ERROR_TEMP_3161, destination absente |
SET de l'ancienne configuration statique spark.connect.copyFromLocalToFs.allowDestLocal | 400 CANNOT_MODIFY_STATIC_CONFIG |
Nom d'artefact .. / staging absolu (ArtifactUtils.concatenatePaths) | INVALID_ARTIFACT_PATH |
Artefacts non-forward_to_fs | restent sous artifactRootDirectory/<sessionUUID>/ |
| Destination HDFS/cloud sans le flag | API documentée, pas ce leftover |
SQL non authentifié / UDF Python / UdfPacket Scala sur un Connect ouvert | soumission de job selon la FAQ du fournisseur ; DROP |
| SPARK-52122 ML DefaultParamsReader | allowlist + safeMLClassLoader sur ce pin |
Pas de shell. Pas de heap dump. Pas de contournement du PSK Connect. La destination doit toujours être LocalFileSystem. L'écriture se fait avec l'UID Spark, pas en tant que root.
uploadArtifactToFs refuse une destination LocalFileSystem sauf si le flag de test est activé, avec un commentaire explicite indiquant qu'autrement un utilisateur écrase un fichier arbitraire sur le 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 clé 3.5 est statique, par défaut false. Le remplacement 4.0 est buildConf optionnel :
val ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL =
buildConf("spark.sql.artifact.copyFromLocalToFs.allowDestLocal")
.internal()
...
.booleanConf
.createOptional
buildConf n'appelle pas registerStaticConfigKey. isModifiable vaut containsConfigKey && !isStaticConfigKey. RuntimeConfig.set ne rejette que les clés statiques. Internal reste définissable. Après SET, getConf(ARTIFACT_...) renvoie Some(true) et le repli getOrElse vers l'ancienne conf statique est ignoré.
SPARK-58531 / PR 57730 « Convert spark.sql.artifact.copyFromLocalToFs.allowDestLocal to a static conf » est résolu le 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 est listé dans Affects. Cette version épinglée contient toujours la conf d'exécution.