
Apache Spark 4.2.0 - Alta - Remoto - Leitura de arquivo - RPC StreamRequest File join não autenticado
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-rpc-stream-unauth
Classe: Privilégio remanescente / Leitura de arquivo Alcance: Remoto
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
CVE-2020-9480 fechou RequestSubmitDriver / RegisterApplication não autenticados no plano de controle RPC. AbstractAuthRpcHandler protege receive() e receiveStream(). Ele não protege getStreamManager(). TransportRequestHandler ainda despacha StreamRequest para esse gerenciador antes do SASL. NettyStreamManager.openStream faz new File(dir, fname) sem verificação de permanência na raiz.
StreamRequest não autenticado classes/../../path lê arquivos Spark-UID. Filhos absolutos Unix permanecem sob o diretório registrado (lab 404). files/ é um get de mapa (404). receive() / receiveStream() permanecem 401. Isto não é RCE do 9480.
| ID | sem CVE ainda |
| Classe | Privilégio remanescente / Leitura de arquivo (remanescente do plano de dados RPC sem proteção do CVE-2020-9480) |
| Alcance | Remoto (TCP não autenticado para um driver RpcEnv / ESS / BlockManager com spark.authenticate=true) |
| CWE | CWE-862, CWE-22 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Produto | Apache Spark |
| Afetado | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). spark.authenticate=true habilitado pelo operador. A leitura de arquivo do host precisa de um diretório registrado (spark.repl.class.outputDir /classes e/ou artifacts do Connect). |
| Autenticação | nenhuma em StreamRequest / getStreamManager() |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
TCP para uma porta RPC do Spark que tenha spark.authenticate=true e um diretório registrado. Sem segredo compartilhado. Sem spark.network.crypto.enabled. Envie um StreamRequest de transporte (tipo de mensagem 6) com streamId classes/../../<path>. TransportRequestHandler.processStreamRequest nunca chama RpcHandler.receive. Ele chama getStreamManager().openStream. O wrapper do 9480 retorna o gerenciador real. new File(dir, "../../oracle/secret") canonicaliza para fora da raiz do REPL/artifact e retorna como FileSegmentManagedBuffer.
Isso é uma leitura de arquivo do host como o UID do Spark. Não é submissão de job. Não é RequestSubmitDriver. Não é um shell.
| Método | Sink | Privilégio |
|---|---|---|
StreamRequest não autenticado classes/../../<path> | NettyStreamManager.openStream new File(dir, fname) e então FileSegmentManagedBuffer | Lê arquivos Spark-UID que o driver consegue abrir. Comprovado em lab: opened /opt/repl-classes/../../oracle/secret, canônico /oracle/secret, corpo SPARK-RPC-STREAM-UNAUTH-WITNESS, x-auth-gated=false, x-authenticated=false. |
StreamRequest não autenticado classes/hello.txt (controle dentro do diretório) | mesma junção, permanece sob o diretório registrado | Lê um arquivo que está de fato sob /classes. Comprovado em lab 200 IN-DIR-OK, sem witness. Confirma que o servidor de arquivos funciona sem SASL mesmo para um nome contido. |
| Método | Sink | Privilégio |
|---|---|---|
ChunkFetchRequest / StreamRequest não autenticado de um streamId ESS ativo | OneForOneStreamManager.getChunk / openStream. checkAuthorization é ignorado quando client.getClientId() == null | Lê bytes de shuffle / disk-RDD em trânsito de outro app. Disponibilidade: o drain avança curChunk. Precisa de um streamId ativo (início aleatório, depois +1). O gadget não falava com ESS ativo. |
MergedBlockMetaRequest não autenticado | AbstractAuthRpcHandler.getMergedBlockMetaReqHandler() também sem proteção | Lê bitmaps de chunk de shuffle mesclado para um appId escolhido quando o ESS tem merged shuffle. Mesmo skip de clientId nulo. Não exercitado. |
| Método | Sink | Permanece fechado |
|---|---|---|
StreamRequest não autenticado classes//oracle/secret (filho absoluto Unix) | new File(dir, "/oracle/secret") | 404. O caminho aberto é /opt/repl-classes/oracle/secret. O File do Unix concatena; ele não ignora o pai. A alegação de caça de que um filho absoluto descarta a raiz está errada no Unix. |
StreamRequest não autenticado files//oracle/secret | get de mapa files | 404. files/ e jars/ são buscas por basename, não junções. |
receive() / OneWayMessage não autenticado | AbstractAuthRpcHandler.receive | 401 Unauthenticated call to receive(). x-auth-gated=true. |
receiveStream() / UploadStream não autenticado | AbstractAuthRpcHandler.receiveStream | 401 Unauthenticated call to receiveStream(). |
receiveStream() autenticado com o segredo do lab | mesmo gate após doAuthChallenge | 200 receiveStream-ok. Controle de que o segredo ainda abre o caminho do 9480. |
Comprovado em lab vs inferido: leitura de arquivo .. não autenticada, absoluto Unix 404, files/ 404, receive / receiveStream protegidos são de lab. O drain de shuffle do ESS é inferido.
Fechado adjacente (não inflar):
RequestSubmitDriver / RegisterApplication não autenticados via OneWayMessage / UploadStream / RpcRequest. Ainda precisa do segredo compartilhado.spark.authenticate=false padrão (FAQ do SECURITY.md, padrão documentado de rede confiável). Não é este remanescente.AppIsolationSuite / SPARK-57889 clientId não nulo)./classes ou artifacts registrados, então esta caminhada de arquivo do host é driver / REPL / Connect, não master:7077.spark.authenticate.secret.Eu fixei apache/spark v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8), não master e não um RC 4.3. Vinte e oito caças no código-fonte. Submissão de job em um cluster aberto é por design. Carregamento de modelo ML é código executável. O RCE público do Jackson History, o shell id -Gn da UI, a personificação local do PySpark e o caminho de receive do 9480 estão fechados nesta camada.