
Apache Spark 4.2.0 - Élevé - À distance - Lecture de fichier - RPC StreamRequest File join non authentifié
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-rpc-stream-unauth
Classe : Reliquat de privilège / Lecture de fichier Portée : Distante
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2020-9480 a fermé RequestSubmitDriver / RegisterApplication non authentifiés sur le plan de contrôle RPC. AbstractAuthRpcHandler filtre receive() et receiveStream(). Il ne filtre pas getStreamManager(). TransportRequestHandler distribue toujours StreamRequest vers ce gestionnaire avant SASL. NettyStreamManager.openStream fait new File(dir, fname) sans vérification de confinement à la racine.
Un StreamRequest non authentifié classes/../../path lit des fichiers Spark-UID. Les enfants absolus Unix restent sous le répertoire enregistré (labo 404). files/ est un accès map (404). receive() / receiveStream() restent 401. Ce n'est pas la RCE 9480.
| ID | pas encore de CVE |
| Classe | Reliquat de privilège / Lecture de fichier (reliquat non filtré du plan de données RPC de CVE-2020-9480) |
| Portée | Distante (TCP non authentifié vers un driver RpcEnv / ESS / BlockManager avec spark.authenticate=true) |
| CWE | CWE-862, CWE-22 |
| CVSS | Élevé : 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Produit | Apache Spark |
| Affecté | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). spark.authenticate=true activé par l'opérateur. La lecture de fichier hôte nécessite un répertoire enregistré (spark.repl.class.outputDir /classes et/ou les artifacts de Connect). |
| Auth | aucune sur StreamRequest / getStreamManager() |
| Licence | GNU Affero GPL v3.0 |
| Labo | 127.0.0.1 uniquement |
TCP vers un port RPC Spark qui a spark.authenticate=true et un répertoire enregistré. Aucun secret partagé. Pas de spark.network.crypto.enabled. Envoyer un StreamRequest de transport (type de message 6) avec streamId classes/../../<path>. TransportRequestHandler.processStreamRequest n'appelle jamais RpcHandler.receive. Il appelle getStreamManager().openStream. Le wrapper 9480 renvoie le vrai gestionnaire. new File(dir, "../../oracle/secret") se canonicalise en dehors de la racine REPL/artifact et revient sous forme de FileSegmentManagedBuffer.
C'est une lecture de fichier hôte en tant qu'UID Spark. Ce n'est pas une soumission de job. Ce n'est pas RequestSubmitDriver. Ce n'est pas un shell.
| Méthode | Point d'aboutissement | Privilège |
|---|---|---|
StreamRequest non auth classes/../../<path> | NettyStreamManager.openStream new File(dir, fname) puis FileSegmentManagedBuffer | Lire des fichiers Spark-UID que le driver peut ouvrir. Prouvé en labo : opened /opt/repl-classes/../../oracle/secret, canonique /oracle/secret, corps SPARK-RPC-STREAM-UNAUTH-WITNESS, x-auth-gated=false, x-authenticated=false. |
StreamRequest non auth classes/hello.txt (contrôle dans le répertoire) | même jointure, reste sous le répertoire enregistré | Lire un fichier qui se trouve réellement sous /classes. Prouvé en labo 200 IN-DIR-OK, aucun témoin. Confirme que le serveur de fichiers fonctionne sans SASL même pour un nom contenu. |
| Méthode | Point d'aboutissement | Privilège |
|---|---|---|
ChunkFetchRequest / StreamRequest non auth d'un streamId ESS actif | OneForOneStreamManager.getChunk / openStream. checkAuthorization est ignoré quand client.getClientId() == null | Lire les octets de shuffle / disk-RDD en vol d'une autre application. Disponibilité : le drain avance curChunk. Nécessite un streamId actif (départ aléatoire, puis +1). Le gadget ne parlait pas à un ESS actif. |
MergedBlockMetaRequest non auth | AbstractAuthRpcHandler.getMergedBlockMetaReqHandler() également non filtré | Lire les bitmaps de chunks de shuffle fusionnés pour un appId choisi quand l'ESS a le shuffle fusionné. Même contournement du clientId nul. Non exercé. |
| Méthode | Point d'aboutissement | Reste fermé |
|---|---|---|
StreamRequest non auth classes//oracle/secret (enfant absolu Unix) | new File(dir, "/oracle/secret") | 404. Le chemin ouvert est /opt/repl-classes/oracle/secret. File Unix concatène ; il n'ignore pas le parent. L'affirmation de la chasse selon laquelle un enfant absolu abandonne la racine est fausse sur Unix. |
StreamRequest non auth files//oracle/secret | accès map files | 404. files/ et jars/ sont des recherches par nom de base, pas des jointures. |
receive() / OneWayMessage non auth | AbstractAuthRpcHandler.receive | 401 Unauthenticated call to receive(). x-auth-gated=true. |
receiveStream() / UploadStream non auth | AbstractAuthRpcHandler.receiveStream | 401 Unauthenticated call to receiveStream(). |
receiveStream() auth avec le secret du labo | même filtre après doAuthChallenge | 200 receiveStream-ok. Contrôle que le secret ouvre toujours le chemin 9480. |
Prouvé en labo vs déduit : la lecture de fichier .. non auth, l'absolu Unix 404, files/ 404, receive / receiveStream filtrés sont du labo. Le drain de shuffle ESS est déduit.
Fermé adjacent (ne pas gonfler) :
RequestSubmitDriver / RegisterApplication non auth via OneWayMessage / UploadStream / RpcRequest. Nécessite toujours le secret partagé.spark.authenticate=false (FAQ SECURITY.md, défaut documenté de réseau de confiance). Pas ce reliquat.AppIsolationSuite / SPARK-57889 clientId non nul)./classes ni artifacts enregistrés, donc ce parcours de fichier hôte concerne le driver / REPL / Connect, pas master:7077.spark.authenticate.secret.J'ai épinglé apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8), pas master et pas une RC 4.3. Vingt-huit chasses dans le code source. La soumission de job sur un cluster ouvert est by design. Le chargement de modèle ML est du code exécutable. La RCE publique Jackson History, le shell UI id -Gn, l'usurpation locale PySpark et le chemin receive de 9480 sont fermés sur cette couche.