
Apache Spark 4.2.0 - Hoch - Remote - Dateilesen - unauth RPC StreamRequest File join
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-rpc-stream-unauth
Klasse: Privilegien-Überrest / Dateilesen Reichweite: Remote
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2020-9480 hat unauthentifiziertes RequestSubmitDriver / RegisterApplication auf der RPC-Kontroll-Ebene geschlossen. AbstractAuthRpcHandler schützt receive() und receiveStream(). Es schützt nicht getStreamManager(). TransportRequestHandler leitet StreamRequest weiterhin vor SASL an diesen Manager weiter. NettyStreamManager.openStream führt new File(dir, fname) ohne Prüfung auf Verbleib im Wurzelverzeichnis aus.
Unauthentifiziertes StreamRequest classes/../../path liest Spark-UID-Dateien. Unix-absolute Kindpfade bleiben unter dem registrierten Verzeichnis (Lab 404). files/ ist ein Map-Get (404). receive() / receiveStream() bleiben 401. Dies ist keine 9480-RCE.
| ID | noch keine CVE |
| Klasse | Privilegien-Überrest / Dateilesen (ungeschützter RPC-Datenebenen-Überrest von CVE-2020-9480) |
| Reichweite | Remote (unauthentifiziertes TCP zu einem Driver-RpcEnv / ESS / BlockManager mit spark.authenticate=true) |
| CWE | CWE-862, CWE-22 |
| CVSS | Hoch: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Produkt | Apache Spark |
| Betroffen | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Vom Betreiber aktiviertes spark.authenticate=true. Host-Dateilesen erfordert ein registriertes Verzeichnis (spark.repl.class.outputDir /classes und/oder Connect artifacts). |
| Auth | keine bei StreamRequest / getStreamManager() |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
TCP zu einem Spark-RPC-Port, der spark.authenticate=true und ein registriertes Verzeichnis hat. Kein gemeinsames Geheimnis. Kein spark.network.crypto.enabled. Senden eines Transport-StreamRequest (Nachrichtentyp 6) mit streamId classes/../../<path>. TransportRequestHandler.processStreamRequest ruft niemals RpcHandler.receive auf. Es ruft getStreamManager().openStream auf. Der 9480-Wrapper gibt den echten Manager zurück. new File(dir, "../../oracle/secret") kanonisiert außerhalb des REPL-/Artefakt-Wurzelverzeichnisses und kommt als FileSegmentManagedBuffer zurück.
Das ist ein Host-Dateilesen als die Spark-UID. Es ist kein Job-Submit. Es ist kein RequestSubmitDriver. Es ist keine Shell.
| Methode | Senke | Privileg |
|---|---|---|
Unauth StreamRequest classes/../../<path> | NettyStreamManager.openStream new File(dir, fname) dann FileSegmentManagedBuffer | Liest Spark-UID-Dateien, die der Driver öffnen kann. Im Lab nachgewiesen: opened /opt/repl-classes/../../oracle/secret, kanonisch /oracle/secret, Body SPARK-RPC-STREAM-UNAUTH-WITNESS, x-auth-gated=false, x-authenticated=false. |
Unauth StreamRequest classes/hello.txt (In-Dir-Kontrolle) | gleicher Join, bleibt unter dem registrierten Verzeichnis | Liest eine Datei, die tatsächlich unter /classes liegt. Im Lab nachgewiesen 200 IN-DIR-OK, kein Witness. Bestätigt, dass der Dateiserver auch für einen enthaltenen Namen ohne SASL funktioniert. |
| Methode | Senke | Privileg |
|---|---|---|
Unauth ChunkFetchRequest / StreamRequest einer Live-ESS-streamId | OneForOneStreamManager.getChunk / openStream. checkAuthorization wird übersprungen, wenn client.getClientId() == null | Liest In-Flight-Shuffle-/Disk-RDD-Bytes einer anderen App. Verfügbarkeit: Drain rückt curChunk vor. Erfordert eine Live-streamId (zufälliger Start, dann +1). Gadget sprach kein Live-ESS. |
Unauth MergedBlockMetaRequest | AbstractAuthRpcHandler.getMergedBlockMetaReqHandler() ebenfalls ungeschützt | Liest Merged-Shuffle-Chunk-Bitmaps für eine gewählte appId, wenn ESS Merged Shuffle hat. Gleicher Null-clientId-Skip. Nicht ausgeführt. |
| Methode | Senke | Bleibt geschlossen |
|---|---|---|
Unauth StreamRequest classes//oracle/secret (Unix-absolutes Kind) | new File(dir, "/oracle/secret") | 404. Geöffneter Pfad ist /opt/repl-classes/oracle/secret. Unix File konkateniert; es ignoriert das Elternverzeichnis nicht. Die Hunt-Behauptung, dass ein absolutes Kind die Wurzel verwirft, ist unter Unix falsch. |
Unauth StreamRequest files//oracle/secret | files-Map-Get | 404. files/ und jars/ sind Basename-Lookups, keine Joins. |
Unauth receive() / OneWayMessage | AbstractAuthRpcHandler.receive | 401 Unauthenticated call to receive(). x-auth-gated=true. |
Unauth receiveStream() / UploadStream | AbstractAuthRpcHandler.receiveStream | 401 Unauthenticated call to receiveStream(). |
Authed receiveStream() mit dem Lab-Geheimnis | gleicher Gate nach doAuthChallenge | 200 receiveStream-ok. Kontrolle, dass das Geheimnis weiterhin den 9480-Pfad öffnet. |
Im Lab nachgewiesen vs. abgeleitet: Unauth-..-Dateilesen, Unix-absolut 404, files/ 404, geschütztes receive / receiveStream sind Lab. ESS-Shuffle-Drain ist abgeleitet.
Angrenzend geschlossen (nicht aufblähen):
RequestSubmitDriver / RegisterApplication via OneWayMessage / UploadStream / RpcRequest. Benötigt weiterhin das gemeinsame Geheimnis.spark.authenticate=false Unauth-Shuffle und Job-Submit (SECURITY.md FAQ, dokumentierter Trusted-Network-Standard). Nicht dieser Überrest.AppIsolationSuite / SPARK-57889 Nicht-Null-clientId)./classes oder artifacts registriert, daher ist dieser Host-Datei-Walk Driver / REPL / Connect, nicht master:7077.spark.authenticate.secret.Ich habe apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) gepinnt, nicht master und nicht ein 4.3 RC. Achtundzwanzig Source-Hunts. Job-Submit auf einem offenen Cluster ist by design. ML-Modell-Laden ist ausführbarer Code. Die öffentliche Jackson-History-RCE, UI-id -Gn-Shell, PySpark-Local-Impersonation und der 9480-Receive-Pfad sind bei diesem Peel geschlossen.
Der RPC-Auth-Hunt sollte eine Re-File-Prüfung von 9480 sein. AuthServerBootstrap umhüllt weiterhin den Handler. Unauth receive wirft weiterhin. Dann las ich getStreamManager(). Drei Zeilen. Es gibt den Delegate zurück. TransportRequestHandler.handle leitet StreamRequest um receive herum. Standard-StreamManager.checkAuthorization ist ein No-Op. SPARK-57889 fügte eine String-ID-Prüfung bei StreamRequest hinzu und behielt den Null-clientId-Skip bei, den ESS für "Auth aus" verwendet, was auch "diese Verbindung hat sich nie authentifiziert" bedeutet.