Voltar para todas as publicações
Publicado em · por Renaud Deraison

A página web chamada localhost

Em 17 de agosto, a CISA adicionou a CVE-2025-62593 do Ray ao seu catálogo de Vulnerabilidades Exploradas Conhecidas. É execução remota de código na própria máquina do desenvolvedor, entregue por uma página web que ele visitou, alcançando um serviço que só escutava em 127.0.0.1. Agentes de código vêm enchendo essa interface de servidores sem autenticação o ano inteiro. O Bromure Agentic Coding coloca esses servidores onde nenhuma aba de navegador consegue discar.

Seu servidor de desenvolvimento, seu notebook e seu painel local cada um ligam uma porta sem autenticação a 127.0.0.1, no raciocínio de que só quem está ao teclado consegue alcançá-la. Uma página web também consegue. A política de navegador que está no caminho se desfaz em uns sessenta segundos.

Em 17 de agosto, a CISA adicionou uma vulnerabilidade ao seu catálogo de Vulnerabilidades Exploradas Conhecidas e deu às agências federais até 20 de agosto para tratá-la. O The Hacker News escreveu sobre isso na manhã seguinte. A entrada é a CVE-2025-62593, uma falha de injeção de código no Ray, o framework de computação distribuída que boa parte da indústria usa para treinar e servir modelos.

O código roda no laptop do próprio desenvolvedor. Alguém tinha o Ray rodando localmente para o seu trabalho, abriu uma página web, e a página executou um comando por ele.

Três fatos comuns, mal arranjados

O aviso dá nota 9,4 e descreve uma cadeia sem nenhum passo exótico dentro.

A API de Jobs do Ray recebe um comando de shell. Esse é o produto: você faz POST em /api/jobs/ com um entrypoint e o cluster roda o comando. O painel escuta na porta 8265, e nem ele nem os endpoints de jobs exigem autenticação. Foi uma decisão deliberada dos mantenedores, cuja posição é que segurança e isolamento precisam ser impostos fora do cluster Ray. Você o coloca em algum lugar que só chamadores confiáveis alcançam, e a interface de loopback de um laptop soa como um lugar assim.

O Ray até tinha uma guarda contra navegadores. Ele checava se o cabeçalho User-Agent começava com Mozilla e recusava essas requisições, na suposição de que um navegador não pode mentir sobre o próprio User-Agent. No Firefox e no Safari, a API Fetch deixa a página definir esse cabeçalho como bem entender.

Sobra a política de mesma origem, o único controle entre uma página na internet e um serviço na sua máquina. O DNS rebinding a desmonta. O atacante serve a página a partir de um domínio cujos registros DNS ele controla, com um time-to-live muito curto. A página carrega e então resolve de novo esse mesmo domínio para 127.0.0.1. O navegador continua tratando como a mesma origem, mesmo hostname e mesma porta, enquanto toda requisição agora vai para a sua máquina. A prova de conceito publicada usa singularity, um framework de rebinding que é público há anos.

Encadeie tudo isso e uma página maliciosa, ou um anúncio contaminado numa página em que você confiava, posta um job na sua instância Ray com um comando de shell dentro. O Chrome está protegido por um bug sem relação; o Firefox e o Safari não. Avi Lumelsky, da Oligo, teorizou o bypass do User-Agent; Jonathan Leitschuh, então na Socket, construiu a cadeia de rebinding e a divulgação. O Ray 2.52.0 corrige a falha e adiciona autenticação por token opcional, desligada por padrão.

Como uma página vira um cliente do seu laptop1 · você carrega a páginaevil.example → 203.0.113.7TTL: 1 segundo2 · o registro mudaevil.example → 127.0.0.1mesma origem, novo destino3 · a guarda é um cabeçalhofetch(url, headers: UA)Firefox e Safari permitem4 · POST /api/jobs/127.0.0.1:8265entrypoint = shellNO QUE CADA CAMADA ESTAVA CONFIANDORay“a rede ao nosso redor é confiável”a checagem de User-Agent“um navegador não mente sobre isso”política de mesma origem“um hostname é um endereço só”Só a terceira é um controle de segurança, e o rebinding é mais antigo que a maioria das ferramentas que se apoiam nele.
A cadeia de rebinding. Cada passo é um comportamento documentado de uma ferramenta bem mantida: um TTL de DNS curto, um cabeçalho Fetch que a especificação permite a uma página definir, e um endpoint de API cujo propósito inteiro é rodar comandos.

Os atacantes chegaram antes do aviso. A botnet RondoDox pegou a falha dois dias antes da divulgação pública, e a pesquisa ShadowRay 2.0 da Oligo acompanha uma campanha que transforma implantações do Ray em uma botnet autopropagante: clusters comprometidos escaneiam em busca de mais instâncias Ray e as infectam, um cron job puxa novas cargas de repositórios controlados pelo atacante a cada quinze minutos, e a carga minera Monero, abre shells reversos e leva o que encontrar. A Oligo contou mais de 230.000 servidores Ray expostos, um aumento de dez vezes desde o primeiro relatório em 2024. Um único cluster comprometido entregou 240 GB de código-fonte, modelos e conjuntos de dados.

O que está escutando na sua máquina

Deixe o Ray de lado e conte outra coisa: os servidores HTTP sem autenticação ligados à sua interface de loopback neste momento, e quantos deles você conseguiria nomear sem olhar.

Rode lsof -iTCP -sTCP:LISTEN -P depois de uma semana de trabalho e a lista sai mais longa do que você imagina. Vite na 5173. Um serviço FastAPI sob uvicorn --reload na 8000. Jupyter na 8888, mlflow ui na 5000, a porta publicada de um contêiner Postgres, um servidor MCP na 3000, uma porta de debug deixada aberta por um teste que quebrou. Nenhum deles pergunta quem é você. Os autores fizeram a mesma suposição que os mantenedores do Ray fizeram, e ela se sustenta até uma aba em outra janela começar a fazer requisições.

Os agentes mudaram o volume. Um agente trabalhando num ticket sobe serviços como parte natural do ofício. Ele roda o servidor de desenvolvimento para conferir a própria mudança, sobe um banco de dados para rodar a migração, inicia o notebook para olhar os dados, lança a API para bater num endpoint. Faz isso várias vezes por tarde, em vários workspaces, e não limpa nada, porque nada manda limpar. Os processos sobrevivem à tarefa. Na quinta-feira a interface carrega uma dúzia de serviços que você não iniciou e não conseguiria listar de memória, cada um se ligando sem autenticação porque o guia de início rápido do framework diz que em localhost está tudo bem.

A posição do Ray se lê de outro jeito numa máquina rodando um agente de código: segurança e isolamento precisam ser impostos fora da coisa que você iniciou. Num laptop onde o agente e o navegador dividem um único kernel, “fora” não tem onde ficar.

Num workspace Bromure, a 8265 não está no seu Mac

O Bromure Agentic Coding roda cada workspace na sua própria VM Ubuntu sobre o framework Virtualization da Apple. Essa é a resposta inteira a esse ataque, e ela se sustenta sem ninguém aplicar patch nenhum.

Quando o agente roda ray start --head, o painel se liga a 127.0.0.1:8265 dentro da VM. A interface de loopback do seu Mac é outra interface. Um fetch() com rebinding a partir do Safari no host resolve evil.example para 127.0.0.1, conecta no loopback do macOS e não obtém nada, porque o serviço que a página veio procurar está um kernel adiante. A cadeia publicada mira 127.0.0.1, o endereço para o qual o singularity faz rebinding e o endereço que o aviso nomeia, e num workspace Bromure esse endereço não tem nada nele.

Vale o mesmo para a segunda metade do aviso, a parte sobre usar um navegador como intermediário para alcançar instâncias Ray dentro de uma rede corporativa que ninguém colocou na internet. Workspaces rodam em modo NAT por padrão, e o manual é específico sobre o que isso compra: as VMs são alcançáveis a partir do seu Mac mas não ficam expostas na sua LAN física, e conexões de entrada vindas de outro lugar não são possíveis a menos que você publique um serviço de propósito. Publicar é um túnel rápido da Cloudflare por serviço, que você inicia apertando um botão.

E você ganha o inventário. O cartão Portas em escuta do painel do workspace consulta ss -tulnpH dentro do guest e lista cada socket alcançável de fora como o IP-da-VM:porta ao qual você se conectaria, com o nome do processo que o segura. O cartão esconde sockets só de loopback, já que nada fora da VM os alcança. A mesma lista sai de bromure-cli vm <id> -L. Essa lista é o censo das sobras do agente, e o macOS não mantém lista nenhuma para você.

CONVENCIONAL: um loopback, compartilhado por tudoseu Mac · 127.0.0.1:8265 ray · :5173 vite · :8888 jupyter · :3000 mcp:8000 uvicorn · :5000 mlflow · :5432 postgresiniciados pelo agente, sobrevivendo à tarefa, nenhum autenticadouma aba em outra janelafaz rebinding para 127.0.0.1e alcança tudo issoBROMURE: o loopback do agente não é o seu loopbackVM do workspace · o próprio 127.0.0.1:8265 ray · :5173 vite · :8888 jupyterNAT: fora da sua LAN físicasockets alcançáveis listados no painelseu Mac · 127.0.0.1(nada que o agente iniciou)o endereço que o exploit discaa mesma aba rebindadaconecta e encontrauma interface vazia
O mesmo comando, dois arranjos. Convencionalmente, tudo o que o agente inicia entra na pilha que uma página web consegue endereçar. No Bromure, os serviços do agente vivem atrás de uma fronteira de rede, e o que é alcançável é uma lista que você pode ler.

O salto depois do primeiro

O ShadowRay 2.0 vale a leitura pelo que um nó comprometido faz em seguida, porque é dessa parte que o isolamento tem de dar conta. Ele escaneia em busca de outras instâncias Ray e as infecta. Ele instala um cron job que busca novas instruções a cada quinze minutos. Ele avança sobre quaisquer credenciais e conjuntos de dados que estejam na máquina.

Cada uma dessas coisas é uma conexão de saída, e um workspace Bromure confronta conexões de saída com regras que você escreveu. O editor Conexões de saída no painel Guardrails guarda um conjunto de regras no estilo pf: permitir ou negar, por host, IP ou CIDR, protocolo e porta, avaliado de cima para baixo com a primeira correspondência vencendo, mais um controle para o tráfego que não casa com nada.

allow web  api.github.com:443
allow tcp  registry.npmjs.org:443
deny  any  10.0.0.0/8
deny  any  192.168.0.0/16
default deny

Duas camadas aplicam esse mesmo conjunto de regras. O switch virtual avalia cada fluxo pelo IP de destino e pelos hostnames que aprendeu bisbilhotando as respostas de DNS do próprio guest, de modo que uma regra escrita contra um nome continua casando quando o endereço se move por baixo dela. O proxy MITM avalia as mesmas regras por SNI do TLS, e para regras web por método HTTP, então você pode escrever allow web api.example.com GET,HEAD. O Bromure injeta um reset numa conexão TCP negada, então o connect falha em vez de ficar pendurado.

Toda essa aplicação roda no seu Mac, fora da VM. A interceptação transparente vem ligada por padrão e desvia o HTTP e o HTTPS do guest para o Bromure mesmo se algo lá dentro apagar HTTP_PROXY e HTTPS_PROXY. Um agente que leu a página web errada, ou uma carga que entrou junto com um pacote, fica do lado errado das regras que precisaria editar. O Bromure escreve cada decisão em Janela → Linha do Tempo de Segurança como uma linha de Firewall: host, porta, permitido ou bloqueado, numa única tabela cronológica ao lado das decisões de credencial, cadeia de suprimentos e injeção de prompt.

O cron de quinze minutos tem o mesmo problema na direção oposta. Um workspace são três camadas de armazenamento, e duas delas são suas para descartar: Apagar home… devolve /home/ubuntu ao estado logo após o clone, Redefinir para a base… reclona o disco de sistema do workspace a partir da imagem base, e a imagem base fica somente leitura em tempo de execução. Uma crontab no diretório home não sobrevive a nenhuma das duas.

localhost era uma convenção

Os mantenedores do Ray não foram descuidados. Eles escreveram um framework de computação que roda comandos, disseram isso na documentação, e mandaram você colocar uma fronteira em volta. As pessoas que o rodam num laptop tomaram a interface de loopback como essa fronteira, que é a conclusão para a qual servidores de desenvolvimento, notebooks e painéis locais vêm convidando há vinte anos. Aquilo era uma convenção, não um controle. Sustentava-se enquanto as únicas coisas na sua máquina eram coisas que você mesmo iniciou, e deixou de se sustentar quando os navegadores ganharam um motor de script.

Um agente alarga a fresta toda tarde, um npm run dev de cada vez. Cuidado não a fecha, porque cuidado exige saber o que está escutando, e a lista muda a cada comando que o agente roda.

Pare de deixar os dois dividirem uma interface. Ponha o trabalho do agente na sua própria máquina, onde as portas que ele abre respondem ao seu Mac e a mais ninguém, onde os sockets alcançáveis são uma lista que você lê em vez de uma suposição que você herdou, e onde as conexões que saem são confrontadas com regras que o workspace não consegue editar. Uma página que faz rebinding para 127.0.0.1 então encontra uma interface vazia e leva uma recusa.


Fontes: CISA, “CISA Adds One Known Exploited Vulnerability to Catalog” (17 de agosto de 2026) · The Hacker News, “CISA Flags Actively Exploited Ray Flaw That Can Trigger Browser-Based RCE” (18 de agosto de 2026) · Aviso do GitHub GHSA-q279-jhrf-cc6v (CVE-2025-62593) · Oligo Security, “ShadowRay 2.0”