A sandbox abriu a porta
A CVE-2026-65105 é uma falha de rede no NVIDIA NemoClaw, uma ferramenta cujo propósito inteiro é rodar um agente de IA dentro de uma sandbox. A sandbox é um contêiner Docker, contêineres não conseguem alcançar a interface de loopback do host, então o NemoClaw passou a iniciar o servidor de modelo local em todas as interfaces — o que desligou justamente a única verificação que teria impedido uma página web de falar com ele. O que uma página visitada levou foi o chat template do modelo, a camada que renderiza todo prompt de sistema antes de o modelo o ler. No Bromure Agentic Coding o motor local escuta no loopback, numa porta que o convidado nunca fica sabendo, e o convidado o alcança por vsock em vez de pela rede.
Foi a contenção que abriu o buraco. Uma sandbox que não consegue alcançar a interface de loopback da sua máquina empurra o servidor de modelo para todas as interfaces, e a partir daí uma aba do navegador também o alcança.
Você está rodando seu agente de codificação do jeito cuidadoso. Ele vive numa sandbox. O modelo com que ele pensa roda na sua própria máquina, então nenhum prompt sai do prédio. Você fez as duas coisas que todo mundo manda fazer.
Aí você abre uma aba do navegador, e a partir desse momento seu agente começa a escrever código com um defeito dentro que ele deixa de mencionar.
Isso é a CVE-2026-65105, divulgada em 25 de agosto por Elad Luz e Ofek Itach, da Oasis Security, cuja análise chama o caso de sequestro de agente de passagem. Eles levaram o achado ao PSIRT da NVIDIA antes de publicar, e a história saiu no mesmo dia em The Hacker News, SiliconANGLE e CSO Online. A NVIDIA publicou um comunicado e uma correção.
O produto é o NemoClaw, a ferramenta da NVIDIA para implantar um agente OpenClaw dentro de uma sandbox OpenShell com um backend Ollama local para inferência. Contenção é o argumento de venda.
O contêiner não conseguia alcançar o loopback
O Ollama escuta na porta 11434 e, por padrão, liga-se a 127.0.0.1 — a interface
de loopback, alcançável apenas a partir da própria máquina.
Sandboxes OpenShell são contêineres Docker. Um contêiner tem seu próprio
namespace de rede, então 127.0.0.1 dentro do contêiner é o contêiner, não o
host. O agente na sandbox, portanto, não consegue alcançar um servidor de modelo
ligado ao loopback do host.
O NemoClaw resolveu isso com uma variável de ambiente. Ele inicia o Ollama com
OLLAMA_HOST=0.0.0.0:11434: todas as interfaces da máquina, incluindo a que dá
para o wifi do escritório. O instalador continuava imprimindo Using Ollama on localhost:11434 enquanto o socket respondia ao mundo.
Então vem o passo que transforma uma escolha de configuração em vulnerabilidade.
A API do Ollama não tem autenticação, então ela carrega duas guardas no lugar. A
primeira é uma verificação de CORS no cabeçalho Origin. A segunda é uma lista
de permissões no cabeçalho Host que aceita localhost, o nome de host da
própria máquina, e nomes terminados em .localhost, .local ou .internal.
A segunda guarda contém uma exceção. O Ollama olha para o endereço em que está ligado e, se esse endereço não for loopback, ele pula a verificação de Host por completo. Dá para ver por que alguém escreveu isso: um operador que liga uma interface pública quer clientes remotos, e uma lista de Host feita para loopback mandaria todos eles embora. O resultado é que a configuração que expõe a porta é a mesma configuração que desarma a verificação que a protege.
Sobra a verificação de CORS, e o DNS rebinding atravessa CORS por construção. O
atacante serve a página a partir de um domínio que controla, na porta 11434, e
depois faz esse domínio resolver para 127.0.0.1. Origin e Host agora são o
mesmo nome escolhido pelo atacante, que o Ollama lê como mesma origem.
Cobrimos o mecanismo de rebinding em detalhe na semana passada,
então tome como dado: uma página que você visita vira cliente de um serviço na
sua máquina, e isso leva cerca de um minuto.
O rebinding é o caminho de entrada interessante. É também o mais difícil. Um
servidor em 0.0.0.0 responde a todos os outros dispositivos do segmento de rede
sem truque nenhum de navegador: o notebook do visitante, a smart TV, a máquina
comprometida duas mesas adiante.
Eles não queriam execução de código
Alcançar a API é a metade chata. O que a Oasis fez depois de chegar lá é a parte nova.
O movimento óbvio é gravar um prompt de sistema malicioso dentro do modelo. Isso
falha aqui: o OpenClaw manda o próprio prompt de sistema no array messages a
cada requisição, e o prompt do cliente vence o que está guardado no modelo.
Então os pesquisadores desceram uma camada.
O endpoint /api/create do Ollama aceita um campo template. Um chat
template é um template Go que transforma o array estruturado messages — papéis,
conteúdos, definições de ferramentas — no único bloco corrido de texto que o
modelo de fato lê. Ele roda no momento da inferência, depois de o cliente ter
entregue suas mensagens, e toca em todas elas.
O ataque são três requisições. Ler o template real com /api/show. Manter a
renderização das ferramentas e os tokens especiais exatamente como estão, e mudar
apenas como as mensagens de sistema são renderizadas, anexando uma instrução
da sua escolha. Gravar de volta com /api/create.
O cliente continua enviando seu próprio prompt de sistema caprichado. O template embrulha esse prompt no do atacante, toda vez, antes que o modelo veja um token dele. Nas palavras de Elad Luz, citadas pelo CSO Online, a instrução fica “uma camada abaixo de qualquer coisa que um guardrail ou um operador consiga ver”.
A edição pousa na definição do modelo em disco, então sobrevive à conversa, ao reset e ao reinício. Os metadados do modelo, seu tamanho e as capacidades listadas continuam iguais, e nada parece diferente numa lista de modelos instalados. Todo consumidor daquele modelo recebe a instrução: a CLI que você cutuca à mão e o agente rodando enquanto você almoça.
A Oasis lista para que serve uma instrução dessas. Nada disso é dramático o bastante para ser notado.
Escrever o bug dentro
Produzir código com um defeito sutil — daqueles que passam bem numa revisão, porque foram escritos para passar bem numa revisão.
Ficar calado
Nunca apontar uma preocupação de segurança. Um agente que avisava de algo e parou de avisar é difícil de perceber.
Guiar a escolha
Recomendar um pacote específico, uma URL específica, uma configuração específica. Você pediu uma sugestão; recebeu uma.
Mandar tudo para casa
Onde o agente tem acesso de rede, despachar o conteúdo das conversas e dos arquivos para um endpoint escolhido pelo atacante.
O resto da API sem autenticação é elenco de apoio: /api/generate e /api/chat
para rodar inferência na GPU dos outros, /api/pull para encher um disco,
/api/delete para destruir os modelos, /api/push para publicar sob a conta
ollama.com da vítima, e /api/me para descobrir o nome de host, a chave pública
e o usuário logado de quem você acabou de alcançar.
A sandbox nunca foi o raio de alcance
A Oasis é cuidadosa sobre o que a sandbox fez e não fez. A sandbox OpenShell funciona: ela isola o sistema de arquivos, a rede e os processos, e mantém o endpoint limpo. Um agente dentro de uma organização justifica seu lugar segurando controle de versão, CI/CD, APIs internas, contas de nuvem, chat e uma pilha de servidores MCP. Tome o agente e você comanda esse acesso. O resumo da própria análise é que o verdadeiro raio de alcance segue os recursos que o agente está autorizado a alcançar, não a fronteira da sua sandbox.
Que é a pergunta que um contêiner nunca responde por você, e aqui ela vem com uma reviravolta. A camada de contenção foi o que criou a exposição. O namespace de rede do Docker fez seu trabalho. Contorná-lo colocou o servidor de modelo no wifi.
Onde o modelo local mora num profile
O Bromure Agentic Coding também roda inferência local. Os agentes de um profile podem ser apontados para um modelo no dispositivo em vez de um provedor de nuvem, que é exatamente o recurso que o NemoClaw oferece. O encanamento por baixo é construído ao contrário, e cada passo dele é uma resposta direta a algo lá de cima.
O motor escuta no loopback, e nunca em 0.0.0.0. O servidor de modelo no
dispositivo roda no Mac, em 127.0.0.1, numa porta que o kernel entrega no
lançamento em vez de um número fixo. A porta dinâmica existe por um motivo sem
graça: para que o Bromure nunca brigue com o que já ocupa a tradicional 11434
naquele Mac, Ollama e LM Studio inclusos. Também significa que não existe porta
conhecida para uma página mirar.
O convidado não o alcança por uma rede. Dentro da VM, o agente disca
127.0.0.1:11434, o endereço que ele espera. Essa conexão não chega perto de uma
rota. Uma ponte na VM emenda o fluxo TCP no vsock, o transporte de socket do
virtio, na porta 8446, e a ponta do canal no host o entrega ao motor em loopback.
Um canal vsock é um cano entre um hipervisor e um convidado. Ele não tem endereço
IP, nome de host nem registro DNS, então não há o que religar, não há o que
escanear, e não há como um navegador discar. O convidado nunca fica sabendo em
que porta o motor está.
Nada na sua LAN alcança a VM. Em Resources → Network, o modo padrão é NAT: a saída funciona, e nada na sua rede local alcança a VM. Isso fecha a metade do achado do NemoClaw que não precisa de rebinding nenhum, o dispositivo no mesmo wifi discando a porta direto, e fecha sem que você mude uma configuração.
Não há definição de modelo dentro da VM para reescrever. Agentes convidados
são fixados a um único identificador de modelo, bromure-local, e o endereçam em
bromure.llm, um nome de host sintético sem registro DNS por trás. O proxy dentro
da VM intercepta esse nome e o encaminha ao host. Qual modelo responde é um
mapeamento que você mantém no host, no painel Local Models do profile, e
mudá-lo é um remapeamento do lado do host: nenhuma reconfiguração dentro da VM,
nenhum reinício de agente. O template, os pesos e o roteamento vivem todos no
Mac. Nada no convidado tem um equivalente a /api/create, porque nada no
convidado tem o modelo.
A inferência local passa pelo mesmo pipeline que a de nuvem. Como esse tráfego atravessa o proxy do Bromure em vez de ir direto a uma porta, ele recebe o mesmo tratamento de uma chamada à Anthropic ou à OpenAI: o mesmo trace, o mesmo log e a mesma detecção de prompt injection. O painel Prompt Injection pontua o conteúdo de arquivos, páginas web e saídas de ferramentas que o agente lê, com um modelo PromptGuard local, e pontua os arquivos CLAUDE.md, AGENTS.md e GROK.md que ele carrega como autoridade, com um classificador ModernBERT ajustado mais um scanner determinístico para payloads em Unicode invisível. As detecções vão para o Security Log, ou pausam a requisição, ou devolvem um 451 seco, conforme qual das três respostas você escolheu.
Agora suponha que o modelo está mentindo para você
Leve um argumento de arquitetura longe o bastante e alguém deveria testá-lo. Então entregue a vitória ao atacante pelo caminho que preferir: um template envenenado, um dia ruim num provedor de modelos, um prompt injection que passou. Seu agente agora trabalha para outra pessoa e a saída dele continua boa de ler. Passe aqueles quatro payloads por um profile.
Guiar a escolha, e a escolha ainda ser julgada. Esse payload é o que mais se
apoia na autoridade do modelo, e é o que bate mais na parede. O Bromure avalia
cada busca de pacote no proxy do host antes de o agente ver a resposta, em npm,
PyPI, Cargo, RubyGems, Maven, NuGet, Go modules e Packagist, e o proxy não
pergunta quem sugeriu o pacote. Um portão de idade de dois dias vem ligado por
padrão, o que dá conta da maior parte do que um typosquat recém-publicado precisa.
Ligue a consulta OSV, aponte o filtro de pacotes para socket.dev ou Delpi, e tire
os scripts de instalação, e aí uma recomendação tem de sobreviver a quatro
opiniões nas quais o modelo não tem voz. Os arquivos .npmrc e pip.conf dentro
da VM podem apertar essas configurações. Não podem afrouxá-las.
Mandar tudo para casa, por um fio que você controla. Exfiltração precisa de
saída. Guardrails → Outbound connections é uma tabela de regras no estilo pf —
uma ação, um protocolo (tcp, udp, web, any), um host ou CIDR, uma lista de
portas, e para web uma lista de métodos HTTP — avaliada de cima para baixo, a
primeira correspondência vence, com Unmatched traffic em Allow ou Deny. Ponha
em Deny, liste os hosts de que seu trabalho precisa, e o endpoint do atacante não
está entre eles. A aplicação acontece no switch virtual e na camada SNI do host,
então nada que o convidado faça com o próprio roteamento muda o veredito.
Escrever o bug dentro, e então tentar publicá-lo. Guardrails também carrega um
modo para cada serviço que o agente alcança: Block destructive ou
Read-only em GitHub, GitLab, Bitbucket, AWS, Kubernetes, DigitalOcean,
registries de contêiner e cada endpoint de banco configurado. Em modo somente
leitura um git push é classificado como escrita e bloqueado; um fetch é sempre
permitido. O commit com backdoor fica na VM.
E o acesso que ele herda é falso. O ponto da Oasis sobre raio de alcance é
aquele em que um profile gasta mais esforço. As credenciais na VM são iscas:
marcadores brm_… como variáveis de ambiente, um ~/.kube/config sintético com
certificados de cliente descartáveis, um blob base64 falso em
~/.docker/config.json, e requisições AWS reassinadas no host, de modo que
qualquer coisa que contorne o proxy recebe InvalidSignatureException em vez de
acesso. Chaves SSH privadas
nunca estão na VM — o host assina
através de um agente por profile. Ligue Require approval to use para uma dada
credencial e cada substituição vira um diálogo no host com uma concessão limitada
no tempo: cinco minutos, uma hora, o resto da sessão. O compartilhamento de pastas
é limitado a oito diretórios que você escolhe a dedo, então o resto do seu Mac não
está lá para ser lido.
E você enxerga tudo isso. A janela Security Log (Window → Security Log…) é uma única tabela cronológica no host: vereditos de pacotes, cada destino que a VM abriu e se foi permitido ou negado, trocas de credenciais, detecções de injeção. Código convidado não consegue editá-la, porque código convidado não consegue alcançá-la. Quando o dia acaba, Erase home… e Reset to base… em Resources → Storage levam o profile de volta ao estado logo após a clonagem.
Ligue isso
Se você roda o NemoClaw, aplique a correção da NVIDIA — ela devolve o Ollama ao
loopback e põe um proxy com token na frente de tudo que legitimamente precisa
sair. O The Hacker News observa que o caminho Windows e WSL não está totalmente
coberto por essa versão, então confira sua plataforma no comunicado em vez de
supor. Depois vá encontrar todos os outros servidores de modelo na sua máquina e
pergunte a eles em que endereço estão ligados. A resposta é 0.0.0.0 mais vezes
do que as pessoas esperam, porque contêineres não param de pedir isso.
Num profile, as configurações que valem dois minutos são a lista curta de sempre. Guardrails → Outbound connections com Unmatched traffic em Deny. Credentials → Require approval to use em qualquer coisa que possa gastar dinheiro ou apagar dados. Supply Chain → OSV vulnerability check e filtragem socket.dev ou Delpi se você tiver chave, por cima do portão de idade que já vem ligado. Prompt Injection → os dois detectores, para que o tráfego ao seu modelo local seja lido com o mesmo cuidado que o tráfego a um modelo de nuvem.
Continue rodando o modelo na sua própria máquina. Essa escolha compra prompts que ficam locais, e vale a pena. Ela não diz nada sobre quem mais tem permissão para falar com o servidor que os guarda, o que é uma pergunta sobre um socket, e um socket merece resposta melhor do que uma variável de ambiente configurada para deixar um contêiner feliz. Instale o Bromure Agentic Coding, escolha seu modelo no host, e deixe o convidado com um cano em vez de uma porta.