Apache Spark 4.2.0 — высокий уровень — удалённое — чтение файлов — неаутентифицированный RPC StreamRequest File join
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-rpc-stream-unauth
Класс: Остаточные привилегии / Чтение файлов Доступ: Удалённый
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2020-9480 закрыл неаутентифицированные RequestSubmitDriver / RegisterApplication на плоскости управления RPC. AbstractAuthRpcHandler ограничивает receive() и receiveStream(). Он не ограничивает getStreamManager(). TransportRequestHandler по-прежнему направляет StreamRequest в этот менеджер до SASL. NettyStreamManager.openStream выполняет new File(dir, fname) без проверки нахождения внутри корневого каталога.
Неаутентифицированный StreamRequest classes/../../path читает файлы Spark-UID. Unix-абсолютные дочерние пути остаются внутри зарегистрированного каталога (лаборатория 404). files/ — это получение из map (404). receive() / receiveStream() остаются 401. Это не RCE из 9480.
| ID | CVE пока нет |
| Класс | Остаточные привилегии / Чтение файлов (неограниченный остаток плоскости данных RPC от CVE-2020-9480) |
| Доступ | Удалённый (неаутентифицированный TCP к драйверу RpcEnv / ESS / BlockManager с spark.authenticate=true) |
| CWE | CWE-862, CWE-22 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Продукт | Apache Spark |
| Затронуто | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). Оператор включил spark.authenticate=true. Чтение файлов хоста требует зарегистрированного каталога (spark.repl.class.outputDir /classes и/или Connect artifacts). |
| Аутентификация | отсутствует на StreamRequest / getStreamManager() |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
TCP к порту Spark RPC, на котором установлен spark.authenticate=true и есть зарегистрированный каталог. Без общего секрета. Без spark.network.crypto.enabled. Отправьте транспортный StreamRequest (тип сообщения 6) с streamId classes/../../<path>. TransportRequestHandler.processStreamRequest никогда не вызывает RpcHandler.receive. Он вызывает getStreamManager().openStream. Обёртка 9480 возвращает реальный менеджер. new File(dir, "../../oracle/secret") канонизируется за пределы корня REPL/артефактов и возвращается как FileSegmentManagedBuffer.
Это чтение файла хоста от имени Spark UID. Это не отправка задания. Это не RequestSubmitDriver. Это не шелл.
| Метод | Точка сброса | Привилегия |
|---|---|---|
Неаутентифицированный StreamRequest classes/../../<path> | NettyStreamManager.openStream new File(dir, fname), затем FileSegmentManagedBuffer | Чтение файлов Spark-UID, которые может открыть драйвер. Доказано в лаборатории: opened /opt/repl-classes/../../oracle/secret, канонический /oracle/secret, тело SPARK-RPC-STREAM-UNAUTH-WITNESS, x-auth-gated=false, x-authenticated=false. |
Неаутентифицированный StreamRequest classes/hello.txt (контроль внутри каталога) | то же соединение, остаётся внутри зарегистрированного каталога | Чтение файла, который действительно находится в /classes. Доказано в лаборатории 200 IN-DIR-OK, без свидетеля. Подтверждает, что файловый сервер работает без SASL даже для содержащегося имени. |
| Метод | Точка сброса | Привилегия |
|---|---|---|
Неаутентифицированный ChunkFetchRequest / StreamRequest для живого ESS streamId | OneForOneStreamManager.getChunk / openStream. checkAuthorization пропускается, когда client.getClientId() == null | Чтение байтов shuffle / disk-RDD другого приложения в полёте. Доступность: слив продвигает curChunk. Требуется живой streamId (случайное начало, затем +1). Гаджет не работал с живым ESS. |
Неаутентифицированный MergedBlockMetaRequest | AbstractAuthRpcHandler.getMergedBlockMetaReqHandler() также не ограничен | Чтение битовых карт чанков merged-shuffle для выбранного appId, когда в ESS включён merged shuffle. Тот же пропуск при null-clientId. Не проверялось. |
| Метод | Точка сброса | Остаётся закрытым |
|---|---|---|
Неаутентифицированный StreamRequest classes//oracle/secret (Unix-абсолютный дочерний путь) | new File(dir, "/oracle/secret") | 404. Открытый путь — /opt/repl-classes/oracle/secret. Unix File конкатенирует; он не игнорирует родителя. Утверждение из охоты, что абсолютный дочерний путь отбрасывает корень, неверно на Unix. |
Неаутентифицированный StreamRequest files//oracle/secret | получение из map files | 404. files/ и jars/ — это поиск по базовому имени, а не соединение. |
Неаутентифицированный receive() / OneWayMessage | AbstractAuthRpcHandler.receive | 401 Unauthenticated call to receive(). x-auth-gated=true. |
Неаутентифицированный receiveStream() / UploadStream | AbstractAuthRpcHandler.receiveStream | 401 Unauthenticated call to receiveStream(). |
Аутентифицированный receiveStream() с секретом лаборатории | тот же шлюз после doAuthChallenge | 200 receiveStream-ok. Контроль того, что секрет по-прежнему открывает путь 9480. |
Доказано в лаборатории vs предположительно: неаутентифицированное чтение файла через .., Unix-абсолютный 404, files/ 404, ограниченные receive / receiveStream — это лаборатория. Слив shuffle в ESS — предположительно.
Смежное закрыто (не преувеличивайте):
RequestSubmitDriver / RegisterApplication через OneWayMessage / UploadStream / RpcRequest. По-прежнему требует общего секрета.spark.authenticate=false по умолчанию (FAQ в SECURITY.md, документированное значение по умолчанию для доверенной сети). Не этот остаток.AppIsolationSuite / SPARK-57889 с ненулевым clientId)./classes или artifacts, поэтому этот обход файлов хоста относится к driver / REPL / Connect, а не к master:7077.spark.authenticate.secret.Я зафиксировал apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8), не master и не 4.3 RC. Двадцать восемь поисков по исходникам. Отправка заданий на открытом кластере — это по замыслу. Загрузка ML-модели — это исполняемый код. Публичный RCE через Jackson History, шелл UI id -Gn, локальная имперсонация PySpark и путь receive в 9480 закрыты на этом срезе.