← Todos os Posts

Seu agente tem um intermediário que você não verificou

Pesquisadores compraram 28 routers de API LLM pagos no Taobao, no Xianyu e em lojas hospedadas no Shopify, e reuniram outros 400 gratuitos em comunidades públicas. Em cada um, registraram uma conta, passaram por ele um agente de código isolado em sandbox, executaram toda chamada de ferramenta que voltava e observaram o que ela fazia.1

Nove dos 428 reescreveram uma chamada de ferramenta, transformando-a em um comando ou dependência controlados pelo atacante: um router pago e oito gratuitos. Dezessete routers gratuitos chegaram a usar uma credencial canário da AWS depois de vê-la passar, e um esvaziou uma chave de Ethereum plantada. Dois dos routers que injetavam esconderam o comportamento. Um esperava 50 requisições antes de agir, e outro só disparava em sessões no “YOLO mode” (o modo autônomo, que executa sem pedir confirmação) trabalhando em projetos Rust ou Go.1

Um router de API LLM é um proxy de camada de aplicação. Ele encerra o TLS, lê cada requisição e cada resposta em texto puro e pode reescrever a chamada de ferramenta que seu agente está prestes a executar. O artigo não encontrou nenhum grande provedor assinando suas respostas de chamadas de ferramenta, então nada liga o comando que seu agente executa ao que o modelo produziu. O artigo testou quatro frameworks de agentes, entre eles Claude Code e Codex, e nenhum fazia verificação de integridade da resposta.1

Este post foi publicado pela primeira vez em 10 de abril de 2026, com base no resumo do artigo. Em 2 de outubro de 2026, eu o reescrevi a partir do texto completo. A versão de abril errou em vários pontos, listados em “O que a versão de abril errou”, perto do final.

TL;DR

  • O que um router pode fazer. Ele fica entre o seu cliente e o provedor do modelo como o endpoint que você configurou, então tem em texto puro cada requisição e cada resposta e pode alterar qualquer uma delas antes de repassá-la.1
  • O que 428 routers fizeram. Nove injetaram código malicioso nas chamadas de ferramenta devolvidas (1 de 28 pagos, 8 de 400 gratuitos). Dois dos gratuitos usavam gatilhos para esconder isso. Dezessete routers gratuitos usaram uma credencial canário da AWS pertencente aos pesquisadores, e um esvaziou uma chave de Ethereum dos pesquisadores que guardava menos de US$ 50.1
  • O que uma chave vazada e relays fracos fizeram. Uma chave da OpenAI vazada de propósito serviu 100 milhões de tokens do GPT-5.4 e expôs mais de sete sessões do Codex. Relays isca mal configurados serviram cerca de 2 bilhões de tokens e expuseram 99 credenciais em 440 sessões do Codex, 401 delas já rodando com as chamadas de ferramenta aprovadas automaticamente.1
  • O que as defesas fizeram. No benchmark sintético dos próprios autores, uma porta de política fail-closed bloqueou todas as amostras de injeção com 1,0% de falsos positivos, e, em um benchmark adaptativo simples, um atacante que conhecia a porta a contornou em 100% das amostras. A triagem de anomalias sinalizou 89,0% das injeções simples e cerca de metade das evasivas.1
  • O que, segundo os autores, resolveria. Um envelope de resposta assinado pelo provedor, para que o cliente possa conferir uma chamada de ferramenta contra o que o modelo produziu. O artigo não encontrou nenhum grande provedor oferecendo isso.1
  • Se você hospeda o router. A atualização de 1º de outubro, mais abaixo, cobre onze alertas de segurança do LiteLLM; um deles permitia que qualquer usuário autenticado fizesse o proxy enviar para fora suas chaves de provedor.2

Principais conclusões

  • Operadores de agentes: Todo router entre o seu cliente e o provedor do modelo tem acesso em texto puro a cada requisição e cada resposta, e o cliente só configura o primeiro salto. Se você comprou um router em um marketplace ou pegou um em uma lista pública, trate-o como um intermediário hostil até ter um motivo independente para confiar em quem o opera.
  • Quem constrói harness: Um hook PreToolUse roda antes de a chamada de ferramenta ser executada, e a essa altura o router já teve a chance de reescrevê-la. O hook não tem um original para comparar. O que ele pode fazer é falhar fechado: permitir comandos de shell que só baixem de domínios listados e só instalem pacotes listados, e bloquear o resto.3
  • Quem usa YOLO mode: No estudo com iscas dos pesquisadores, 401 das 440 sessões do Codex observadas já rodavam com a execução de ferramentas aprovada automaticamente.1 Nessas sessões, um simples comando reescrito teria sido executado sem precisar de nenhuma lógica de gatilho. Não passe sessões com aprovação automática por um router que você não controla.
  • Equipes que hospedam o próprio proxy: Um router que você mesmo roda concentra as chaves de provedor da mesma forma. Onze alertas do LiteLLM que entraram no banco de dados da PyPA em 1º de outubro de 2026, a maioria públicos desde junho, incluem um que permitia a qualquer usuário autenticado fazer o proxy enviar suas chaves de provedor para um host de sua escolha. As versões a partir da 1.97.0 ficam fora de todos os onze intervalos registrados; os detalhes estão na atualização de 1º de outubro, mais abaixo.2

O que é um router, exatamente?

Um router de API LLM aceita requisições em um formato, geralmente compatível com a OpenAI, escolhe um provedor upstream e devolve a resposta. O artigo encontra routers em todas as escalas: serviços gerenciados na nuvem, como Amazon Bedrock e Azure OpenAI Service, projetos e serviços voltados a desenvolvedores, como LiteLLM e OpenRouter, e um mercado de commodity de acesso a APIs revendido e agregado.1

As pessoas usam routers por bons motivos. O artigo lista “model fallback, load balancing, cost optimization, and a single API key across providers” (fallback entre modelos, balanceamento de carga, otimização de custos e uma única chave para todos os provedores) e observa que o roteamento é especialmente comum “in regions where direct provider access is restricted, expensive, or subject to quota limitations” (em regiões onde o acesso direto ao provedor é restrito, caro ou sujeito a cotas).1

O problema é a posição. Nenhum truque de interceptação é necessário, nas palavras do artigo: “the client voluntarily configures the router’s URL as the API endpoint, the router terminates the client-side TLS connection, and it originates a separate TLS connection upstream” (o próprio cliente configura a URL do router como endpoint; o router encerra essa conexão TLS e abre outra rumo ao provedor). O TLS protege cada trecho. Ele não faz nada para proteger o payload do próprio router, que lê o JSON da requisição, encaminha, lê o JSON da resposta e devolve, com a chance de alterar qualquer um dos dois.1

Routers também se encadeiam. O exemplo do artigo é um desenvolvedor que compra acesso de um revendedor no Taobao, que agrega chaves de um agregador de segundo nível, que roteia pelo OpenRouter, que despacha para o host do modelo: quatro saltos, cada um com acesso completo em texto puro. “The client configures only the first hop; subsequent hops are invisible.” (O cliente só configura o primeiro salto; os seguintes são invisíveis.) Um salto ruim em qualquer ponto contamina o caminho, e os saltos honestos depois dele não têm como perceber.1

Nada nas APIs atuais fecha essa lacuna. Os argumentos das chamadas de ferramenta trafegam como JSON em texto puro, e “No provider-level integrity mechanism binds the arguments returned by the model to the arguments received by the client.” (nenhum mecanismo de integridade do provedor vincula os argumentos devolvidos pelo modelo aos recebidos pelo cliente).1

Como a medição funcionou

Para cada router, os autores seguiram quatro etapas. Registraram uma conta controlada por eles e obtiveram credenciais de API. Passaram essa conta por um ambiente de agente isolado que rodava “a randomized subset of custom tasks and TerminalBench 2.0 tasks” (um subconjunto aleatório de tarefas próprias e do TerminalBench 2.0), que geram chamadas de ferramenta realistas de shell, instalação de pacotes e manipulação de arquivos. Executaram cada payload de chamada de ferramenta devolvido dentro da sandbox, que monitorava callbacks de rede, mudanças no sistema de arquivos e anomalias na árvore de processos. Em seguida, um agente de análise separado revisava as execuções sinalizadas e classificava cada uma como reescrita benigna, potencialmente maliciosa ou maliciosa confirmada.1

As definições são conservadoras. Um router só conta como injetor de código malicioso “only if a returned tool-call payload is rewritten into an attacker-controlled command or dependency” (se uma chamada devolvida for reescrita em um comando ou dependência do atacante). Uma credencial canário da AWS conta como usada quando uma credencial que passou pelo router “later produces follow-on AWS API activity attributable to that credential” (gera depois atividade na API da AWS atribuível a ela).1

O corpus é o mercado de commodity, não o campo inteiro. Os 400 routers gratuitos são dominados por dois templates open source, sub2api e new-api, e os autores dizem que implantações corporativas e apenas por convite ficam fora do estudo. A maior parte do corpus vem de marketplaces e comunidades em língua chinesa.1

Os dados de campo

Comportamento Routers pagos (28) Routers gratuitos (400)
Injeta código malicioso nas chamadas de ferramenta devolvidas 1 8
Esconde a injeção atrás de um gatilho 0 observados 2
Usa uma credencial canário da AWS dos pesquisadores 0 observados 17
Esvazia uma chave de Ethereum dos pesquisadores 0 observados 1

As injeções não são cosméticas. O artigo diz que elas “change the command or dependency that the agent intends to execute” (mudam o comando ou a dependência que o agente pretende executar). O resultado dos pagos é um router em 28, e os autores tiram a conclusão óbvia: “Paid access improves service stability, but it does not prove tool-call integrity.” (Pagar melhora a estabilidade do serviço, mas não prova a integridade das chamadas de ferramenta.)1

Os dois routers com gatilhos são a descoberta que derrota os testes. Em um, “malicious tool calls are injected only after the first 50 requests, which hides the attack from shallow smoke tests” (as chamadas maliciosas só são injetadas depois das primeiras 50 requisições, o que esconde o ataque de testes superficiais). No outro, a injeção é ativada “only for sessions that appear to be running in autonomous YOLO mode and only when the project fingerprint matches Rust or Go” (só em sessões que parecem estar em YOLO mode autônomo e só quando a impressão digital do projeto indica Rust ou Go). Os autores acrescentam que os gatilhos que viram podem não ser os únicos. Eles observaram esses routers apenas por meio de sondagem finita de caixa-preta, então “a router may implement additional latent conditions that our probes did not activate” (um router pode ter outras condições latentes que as sondagens deles não ativaram).1

Todas as descobertas sobre credenciais estão no conjunto gratuito. Dezessete routers “trigger follow-on use of at least one researcher-owned AWS canary credential after seeing it in transit” (levam ao uso posterior de pelo menos uma credencial canário da AWS dos pesquisadores depois de vê-la passar), e um esvaziou uma chave de Ethereum com saldo pré-carregado. O apêndice estima a perda desse esvaziamento em menos de US$ 50.1

Os dois estudos de envenenamento

Um router não precisa ser malicioso para acabar na mesma posição. Os autores fizeram dois estudos sobre como um caminho de routers de aparência benigna é envenenado.

Estudo 1: uma chave vazada. Eles vazaram uma chave de API da OpenAI pertencente aos pesquisadores “on Chinese forums, WeChat, and Telegram groups frequented by router operators” (em fóruns chineses e em grupos de WeChat e Telegram frequentados por operadores de routers). Essa única chave serviu 100 milhões de tokens do GPT-5.4 e expôs mais de sete sessões de usuários do Codex, e em pelo menos uma sessão várias credenciais distintas apareceram por trás dela. O artigo é cuidadoso sobre como esse tráfego chegou lá. O resultado “is consistent with several paths: direct reuse by a router operator, direct reuse by unrelated third parties, or reuse somewhere inside a longer relay chain” (é compatível com vários caminhos: reuso direto por um operador de router, por terceiros sem relação ou em algum ponto de uma cadeia de relays mais longa), e os autores “cannot always distinguish among those paths from provider-side logs alone” (nem sempre conseguem distinguir entre eles só com os logs do provedor). O que o estudo estabelece é o raio de impacto: quem detém uma chave upstream reutilizada vê em texto puro o tráfego que passa por ela.1

Estudo 2: relays fracos. Eles implantaram instâncias vulneráveis do claude-relay-service em 20 domínios e instâncias do Sub2API e do CLIProxyAPI com senhas fracas em 20 IPs. As iscas atraíram mais de 40.000 tentativas de acesso não autorizado de 147 IPs, que os autores leem como uma mistura de varredura e reuso de relays, e não, em todos os casos, como ataques direcionados. O sinal mais forte veio depois: as iscas “were folded into active agent-facing relay paths” (foram incorporadas a caminhos de relay ativos usados por agentes). Elas serviram cerca de 2 bilhões de tokens do GPT-5.4 e do 5.3-codex, cerca de 13 GB de tráfego visível de prompts e respostas, e expuseram 99 credenciais únicas em 440 sessões do Codex, distribuídas por 398 projetos ou hosts.1

Cada uma dessas 440 sessões era passível de injeção de comandos pela definição do artigo, e 401 “already operated in YOLO mode, i.e., with tool execution auto-approved rather than confirmed command by command” (já operavam em YOLO mode, ou seja, com a execução de ferramentas aprovada automaticamente em vez de confirmada comando a comando). Para essas 401, escrevem os autores, “sophisticated adaptive evasion was unnecessary: basic payload injection would have been sufficient to change executed commands” (evasão adaptativa sofisticada era desnecessária: uma injeção básica já bastaria para mudar os comandos executados).1

Eram sessões de outras pessoas. O tráfego delas chegou às iscas dos pesquisadores depois que as iscas foram incorporadas a caminhos de relay, e um usuário que só configura o primeiro salto não teria como perceber. Os autores guardaram apenas metadados agregados e hashes com salt, e desativaram as próprias credenciais expostas quando a janela de observação se encerrou.1

Duas classes principais de ataque e duas variantes de evasão

O artigo reduz o comportamento de um router malicioso a duas primitivas e dois refinamentos da primeira.1

AC-1: injeção de payload na resposta. O router reescreve uma chamada de ferramenta depois que ela sai do provedor e antes que chegue ao cliente, mantendo intactos o nome da ferramenta e o schema. O exemplo do artigo é uma chamada Bash cuja URL de instalador é trocada pela de um atacante. Como o resultado continua sendo JSON válido para a ferramenta esperada, nada parece errado para o cliente.

AC-2: exfiltração passiva de segredos. O router não muda nada. Ele varre requisições e respostas em busca de padrões de credenciais e guarda o que encontra. O tráfego não é modificado, então “clients cannot observe router-side retention” (os clientes não conseguem observar o que o router retém). A exposição vai além das chaves de API: “system prompts, tool definitions, user-supplied file contents, and environment variables all transit the same plaintext channel” (prompts de sistema, definições de ferramentas, conteúdo de arquivos do usuário e variáveis de ambiente passam todos pelo mesmo canal em texto puro).

AC-1.a: injeção direcionada a dependências. Uma variante do AC-1 voltada para comandos de instalação de pacotes. Em vez de trocar uma URL, o router “substitutes a legitimate dependency name with an attacker-controlled package pre-registered on the target registry” (substitui o nome de uma dependência legítima por um pacote do atacante registrado de antemão no registro de destino). O exemplo do artigo transforma pip install requests em pip install reqeusts. O comando continua instalando a partir do registro confiável, então uma porta que só verifica domínios deixa passar, e o pacote ruim fica em cache para sessões futuras.

AC-1.b: entrega condicional. Uma variante que decide quando o AC-1 dispara. O router se comporta honestamente até que um gatilho seja atendido. O artigo lista cinco famílias de gatilhos: nome da ferramenta, palavras-chave no conteúdo, impressões digitais do usuário como o YOLO mode, janelas de tempo e contagem de requisições. “A router deploying a 50-request warm-up appears entirely transparent during a 10-request probe.” (Um router com um aquecimento de 50 requisições parece totalmente transparente em uma sondagem de 10.)

O artigo cita o comprometimento do LiteLLM de março de 2026 como AC-1 em escala: depois que um atacante controlou o pipeline de requisições do proxy, toda chamada de ferramenta que passava por ele ficou sujeita a reescrita. (O artigo descreve esse comprometimento como dependency confusion, a confusão de dependências. As reportagens que usei no post sobre a cadeia de suprimentos o atribuem a um token de publicação do PyPI roubado por meio do comprometimento do Trivy.)

O que o proxy de pesquisa mostrou sobre os clientes de agentes

Os autores construíram um proxy que chamam de Mine, que implementa as quatro classes, e rodaram 1.000 requisições com chamadas de ferramenta por módulo de ataque em cada um de quatro frameworks públicos de agentes: OpenClaw, OpenCode, Codex da OpenAI e Claude Code da Anthropic.1

Nenhum dos quatro verificou a integridade da resposta. A chamada de ferramenta reescrita chegou em formato válido e nativo do framework em 100% dos casos de AC-1 e em 99,6% dos casos de AC-1.a. O artigo é preciso sobre o que isso mostra e o que não mostra: “These are compatibility measurements, not end-to-end execution rates: they show that the tampered tool call reaches the client in a valid framework-native format, but downstream permission prompts or local execution sandboxes may still block the resulting action.” (São medições de compatibilidade, não taxas de execução de ponta a ponta: a chamada adulterada chega em formato válido, mas um pedido de permissão ou uma sandbox local ainda podem bloquear a ação.)1

Essa ressalva é onde um pedido de permissão mostra seu valor, e é por isso que a aprovação automática pesa tanto nos números das iscas. Os autores não publicaram o Mine.1

O padrão de composição, uma camada abaixo

Continuo escrevendo sobre o mesmo bug estrutural: componentes autorizados individualmente que, combinados, produzem comportamento não autorizado. Trivy-to-LiteLLM foi composição na camada de pacotes. Egress silencioso foi composição na camada de conteúdo buscado. Envenenamento de ferramentas MCP foi composição na camada de protocolo.

O ataque do router é composição no salto entre cliente e provedor. Seu cliente está autorizado a chamar o router. O router está autorizado a chamar o modelo upstream. O modelo está autorizado a responder. Cada salto é autorizado, e ainda assim a composição produz comandos reescritos, porque ninguém assina o que atravessa o salto, e segredos copiados, porque cada salto o lê em texto puro.

O artigo estende a linha até o próprio MCP: “a malicious MCP server receives tool-call requests in plaintext and can return forged results, so the same basic manipulation and collection ideas transfer with adaptation to the MCP message format” (um servidor MCP malicioso recebe em texto puro as requisições de chamadas de ferramenta e pode devolver resultados forjados, então as mesmas ideias de manipulação e coleta se transferem, com adaptação, para o formato de mensagens do MCP). Também marca a diferença. Um servidor MCP fica do lado da execução de ferramentas e pode forjar a saída delas. Um router fica no caminho até o modelo e vê cada chamada de ferramenta e todo o contexto da requisição.1

Três defesas, com os números do artigo

O artigo avalia três controles que um cliente pode implantar sem ajuda de nenhum provedor. Uma ressalva vale para todos os números abaixo: “All numbers in this section come from controlled artifact-side evaluation rather than field data.” (Todos os números desta seção vêm de uma avaliação controlada, não de dados de campo.) Os corpora são sintéticos, gerados pelos scripts dos autores.1

Defesa AC-1 AC-1.a AC-1.b Custo e limite
Porta de política fail-closed 100% bloqueado 100% bloqueado não medido 1,0% de falsos positivos; contornada em 100% das amostras em um benchmark adaptativo simples
Triagem de anomalias na resposta 89,0% sinalizado 50,0% sinalizado 50,9% sinalizado 6,7% de falsos positivos
Log de transparência append-only só auditoria só auditoria só auditoria cerca de 1,26 KB por entrada

A porta de política bloqueia comandos de shell que “fetch from non-allowlisted domains or install packages outside a local allowlist” (baixam de domínios fora da lista de permitidos ou instalam pacotes fora de uma lista local). Em 1.000 comandos benignos, 500 amostras de AC-1 e 500 de AC-1.a, ela bloqueou todas as amostras de ataque, e todos os seus falsos positivos foram downloads de instaladores a partir de domínios espelho ausentes da lista. Os autores a chamam de “the strongest immediately deployable control” (o controle mais forte que dá para implantar de imediato). Eles também relatam sua falha sem rodeios. Se o atacante hospeda o payload em um domínio permitido, ou deixa preparado um script local que um comando de aparência benigna executa depois, “the policy gate is bypassed in 100% of samples” (a porta é contornada em 100% das amostras). O veredito deles: a porta “should be understood as a coarse but useful filter for obvious high-risk workflows, not as a robust defense against defense-aware attackers” (deve ser vista como um filtro grosseiro, mas útil, para fluxos de alto risco evidentes, e não como uma defesa robusta contra atacantes que conhecem as defesas).1

Um detalhe importa para quem for construir a porta, e o artigo não é consistente sobre ele. A seção 4 descreve a porta como baseada só em domínios e projeta o AC-1.a para derrotá-la: “the gate blocks non-allowlisted domains but does not maintain a per-package allowlist” (a porta bloqueia domínios fora da lista, mas não mantém uma lista de pacotes). A porta da seção 7, a que marcou 100% contra o AC-1.a, também bloqueia instalações “outside a local allowlist” (fora de uma lista local). Na minha leitura, uma porta sem a lista de pacotes deixa passar por completo a substituição por typosquatting.

A triagem de anomalias pontua cada chamada de ferramenta devolvida com base em características visíveis localmente, como padrões de risco de shell e entropia dos argumentos, sem um original upstream para comparar. Com 6,7% de falsos positivos, ela sinalizou 89,0% das amostras de AC-1, 50,0% de AC-1.a e 50,9% de AC-1.b. Com um orçamento de 1% de falsos positivos, pegou 43,5% de AC-1 e praticamente nada de AC-1.a. Os autores a consideram “useful for review prioritization” (útil para priorizar revisões) e mais fraca que a porta.1

O log de transparência registra a requisição, a resposta, a URL do router, os metadados de TLS e um hash da resposta bruta. Ele não impede nada. Permite responder, depois de um incidente, até onde um router ou uma credencial chegou e quais sessões passaram por ele.1

A correção que os autores pedem

Nenhum dos três controles autentica de onde veio uma chamada de ferramenta. Os autores dizem isso: “No client-side control available today can prove that a router preserved the upstream provider’s response.” (Nenhum controle do lado do cliente disponível hoje consegue provar que um router preservou a resposta do provedor.)1

O que provaria é uma assinatura do provedor. O artigo propõe “a provider-signed canonical response envelope, similar in spirit to DKIM for email” (um envelope de resposta canônico assinado pelo provedor, semelhante em espírito ao DKIM no e-mail), cobrindo o identificador do modelo, o nome da ferramenta, os argumentos, o motivo de término e um nonce do cliente, que o cliente verifica antes de executar qualquer chamada de ferramenta. E relata que ninguém oferece isso: “To our knowledge, none of the major provider tool-use APIs or the current MCP specification expose a deployed response-signing mechanism for tool-call arguments today.” (Até onde os autores sabem, nem as APIs dos grandes provedores nem a especificação atual do MCP oferecem hoje um mecanismo implantado de assinatura dos argumentos.)1

A proposta vem com dois limites. Segurança de transporte não a substitui: TLS mútuo e pinning de certificado “can authenticate the router endpoint the client chose, but they do not say whether the returned tool call preserves upstream semantics” (podem autenticar o endpoint escolhido pelo cliente, mas não dizem se a chamada devolvida preserva o que o provedor produziu). E assinar não ajuda em nada contra segredos roubados: “AC-2 cannot be mitigated by response-signing proposals because the secrets are exposed on the request path before any provider-side mechanism can act.” (Assinar respostas não mitiga o AC-2, porque os segredos ficam expostos no caminho da requisição antes que o provedor possa agir.)1

O que você deveria realmente fazer

Se o seu agente chama um modelo por meio de um router que você não construiu:

  1. Conecte-se direto quando puder e conheça o operador quando não puder. Basta trocar uma URL base para adicionar um router, e é por isso que ele entra sem que ninguém decida. Transforme isso em uma decisão. “Confiança” aqui significa uma base externa, como uma equipe conhecida, um contrato ou uma jurisdição em que você possa fazer valer seus direitos. Avaliações em marketplace não contam.
  2. Falhe fechado nas chamadas de ferramenta de alto risco. No Claude Code, isso é um hook PreToolUse que bloqueia comandos de shell que baixam de domínios fora de uma lista de permitidos e instalações de pacotes fora de outra. Mantenha a lista de pacotes: a variante de substituição de dependências existe para derrotar uma porta baseada só em domínios. Escreva o hook para negar tudo o que ele não conseguir analisar, com código de saída 2 ou uma decisão deny: o Claude Code trata outros códigos de saída e timeouts como não bloqueantes, então um hook que trava ou quebra não interrompe a chamada, que segue pelo fluxo normal de permissões e, em uma sessão com aprovação automática, simplesmente roda. Confira também se o hook rodou na primeira chamada. A documentação avisa que “a mistyped path in settings.json leaves the gate silently disabled” (um caminho digitado errado no settings.json deixa a porta desativada sem aviso). Conte com manter as duas listas e com um atacante determinado contornando-as.3
  3. Nunca passe sessões com aprovação automática por um router que você não controla. As 401 sessões do estudo com iscas são o precedente. Um pedido de permissão é uma das poucas coisas entre uma chamada de ferramenta reescrita e a sua execução.
  4. Mantenha segredos fora do tráfego. A coleta passiva não muda nada que você possa observar. Tudo o que está em um prompt, em um resultado de ferramenta ou em um arquivo que o agente lê atravessa o router em texto puro. Restrinja o escopo das credenciais, mantenha-as fora do contexto quando puder e rotacione tudo o que tiver passado por um router de que você venha a desconfiar.
  5. Registre logs localmente. Requisições, respostas, a URL do router e um hash da resposta, com os segredos removidos das requisições antes, guardados onde o router não alcança. Isso não vai impedir um ataque. Vai dizer depois o que ficou exposto.
  6. Rode a execução em sandbox. O artigo observa que sandboxes “reduce post-execution blast radius but do not authenticate where a tool call came from” (reduzem o raio de impacto depois da execução, mas não autenticam de onde veio uma chamada de ferramenta). Fique com a primeira metade.

A implicação desconfortável

A camada de routers é um exemplo claro de um ecossistema de agentes que lança infraestrutura mais rápido do que a protege. As pessoas querem uma chave para todos os modelos, preços menores e acesso a partir de regiões que um provedor não atende. Os routers entregam as três coisas, e o mercado os recompensa.

A mesma sequência já aconteceu na camada MCP, na camada de pacotes e na camada de conteúdo buscado. Surge uma nova camada da stack de agentes. Os desenvolvedores a adotam antes que alguém a audite. Chegam os atacantes, depois os pesquisadores. Aqui os pesquisadores contaram 428 routers, 9 injetando código malicioso, 17 usando credenciais plantadas, 1 esvaziando uma carteira e 401 sessões com aprovação automática passando por caminhos de relay que incluíam as iscas dos pesquisadores.1

A peça que fecharia a lacuna, uma resposta assinada pelo provedor, não é algo que um operador possa adicionar. Até que os provedores a lancem, os controles acima reduzem a exposição, e nenhum deles prova que uma chamada de ferramenta é mesmo do modelo.

Atualização, 1º de outubro de 2026: o router que você hospeda também está na lista

Este post trata de routers que outra pessoa opera. O registro de alertas de segurança completa a outra metade. Em 1º de outubro de 2026, o banco de dados de alertas da PyPA adicionou onze entradas contra o LiteLLM, um proxy open source que equipes hospedam por conta própria pelos motivos acima: uma chave para todos os modelos.2 Nenhuma delas é nova. Nove estão no banco de dados de alertas do GitHub e no NVD desde 21 de junho, uma décima desde meados de setembro, e a que mais importa para um proxy, porque expõe as chaves de provedor, foi publicada no próprio repositório do LiteLLM em 26 de agosto, com correções no PyPI desde 9 de agosto; o GitHub a classifica como Moderate. Vale lê-las juntas pelo que dizem sobre essa camada, e porque toda versão final publicada antes de 9 de agosto está dentro do intervalo do CVE-2026-84377.

A primeira a ler é o CVE-2026-84377. Nas palavras do alerta do repositório, “Any authenticated LiteLLM proxy user could redirect an outbound provider call to a destination they control and cause the proxy to send its own configured provider credentials to that destination.” (Qualquer usuário autenticado do proxy podia redirecionar uma chamada de saída para um destino seu e fazer o proxy enviar para lá as próprias credenciais de provedor.) A causa é o formato da verificação: “The proxy’s request-body validation was a denylist that did not cover every sensitive parameter and did not inspect parameters nested inside other request fields.” (A validação do corpo da requisição era uma lista de bloqueio que não cobria todos os parâmetros sensíveis nem inspecionava os aninhados em outros campos.) A correção saiu em nove linhas de versões, da 1.88.6 à 1.96.2. Para quem ainda não pode atualizar, o alerta lista três paliativos em uma só frase: “Set general_settings.allow_client_side_credentials to false so callers cannot override connection parameters, restrict proxy keys to trusted callers, and block the affected parameters (api_base, base_url, model_list, fallbacks, provider credential fields) at a reverse proxy or API gateway.” (definir essa opção como false para que ninguém sobrescreva parâmetros de conexão, restringir as chaves do proxy a chamadores confiáveis e bloquear esses parâmetros em um proxy reverso ou gateway de API).2 O primeiro não basta sozinho. No código-fonte da 1.95.0, uma versão afetada, a verificação do corpo da requisição só é pulada por completo quando essa configuração é true (o configurable_clientside_auth_params de uma implantação ainda pode isentar parâmetros individuais), então false é onde já está uma instalação que nunca mexeu nisso, e a verificação incompleta é justamente a falha que o alerta descreve. A correção é atualizar.6

Uma segunda entrada, o CVE-2026-59823, é a mesma falha em miniatura: a proteção “blocks the api_base and base_url parameters but does not cover user_config,” (bloqueia esses dois parâmetros, mas não cobre o user_config), então quem tivesse uma chave virtual válida podia colocar um api_base dentro dele e apontar o proxy para qualquer host. Essa foi corrigida na 1.83.9, no PyPI desde 17 de abril; o alerta veio em setembro.4

Duas entradas tocam o lado MCP do proxy: autenticação inadequada no proxy MCP (CVE-2026-12773, com um intervalo de versões que termina em uma correção na 1.84.0) e falsificação de requisição do lado do servidor pelo argumento spec_path do carregador de especificações OpenAPI do MCP (CVE-2026-12798, registrado como afetando versões até a 1.82.2). As outras sete cobrem o tratamento de chaves de administrador, o fluxo de depuração de SSO, a invalidação de sessões SSO, a expiração de sessão para chaves geradas, a enumeração de usuários na interface, um bypass de guardrail em endpoints assíncronos e o tratamento de JWT entre máquinas.5

Esses nove registros são mais pobres que os dois primeiros. As descrições trazem o texto padronizado do VulDB em vez de uma explicação do mantenedor, e a descrição da entrada do proxy MCP diz “up to 1.59.8” (até a 1.59.8) enquanto o intervalo de versões diz corrigido na 1.84.0.5 O registro do CVE-2026-84377 tem uma divergência própria: a página do repositório lista as versões afetadas como “<1.94.0”, enquanto os intervalos do registro revisado cobrem a linha 1.96 até a correção na 1.96.2.2

Confira os intervalos contra a versão que você roda em vez de confiar em um resumo, inclusive este. Todo intervalo registrado termina na 1.96.2 ou abaixo, então as versões a partir da 1.97.0, no PyPI desde 16 de agosto, ficam fora de todas as onze; a versão atual em 1º de outubro é a 1.103.2.5

O perigo do router está em onde ficam as credenciais, seja quem for que o opere. Um router de marketplace que usa uma chave AWS plantada e um proxy auto-hospedado que pode ser convencido a mandar para fora suas chaves de provedor são a mesma exposição alcançada por duas direções, uma pelo operador e outra por qualquer inquilino com uma chave virtual. As defesas das seções acima pressupõem que você é o cliente.

Para um proxy que você roda, acrescente a metade do operador: trate cada chave virtual como uma credencial para as chaves upstream por trás dela, mantenha desligados os parâmetros de conexão fornecidos pelo cliente, incluindo o configurable_clientside_auth_params por implantação, a menos que precise deles, e coloque o proxy no mesmo ritmo de patches de tudo o mais que guarda segredos.

Se alguém em quem você não confia totalmente teve uma chave virtual enquanto o proxy rodava uma versão afetada, rotacione as chaves de provedor e quaisquer outros segredos configurados no proxy, e verifique os logs dele em busca de chamadas de saída para hosts que você não configurou; o impacto descrito no alerta abrange “other configured secrets” (outros segredos configurados) e requisições a “internal services reachable from the proxy” (serviços internos acessíveis a partir do proxy). Duas das onze entradas, o CVE-2026-12773 no proxy MCP e o CVE-2026-12795 no fluxo de depuração de SSO, são falhas de autenticação em versões abaixo da 1.84.0, então, nessas versões, quem tinha uma chave talvez não delimite quem conseguia alcançar o proxy; se ele estava acessível a partir de redes em que você não confia, aplique a mesma rotação.

O que a versão de abril errou

A versão de 10 de abril deste post foi escrita a partir do resumo do artigo. Lida contra o texto completo em 2 de outubro de 2026, ela estava errada ou induzia a erro nestes pontos, todos corrigidos acima:

  • AC-1.a. Descrevi o AC-1.a como uma injeção que “only fires when the request matches a specific dependency or context” (só dispara quando a requisição corresponde a uma dependência ou contexto específicos). Na verdade é a substituição do nome de um pacote dentro de um comando de instalação. Gatilhos são o AC-1.b.
  • As defesas. Escrevi que “the abstract does not rank the defenses” (o resumo não classifica as defesas) e ofereci uma classificação como opinião minha. O corpo do artigo mede as três, chama a porta de política de “the strongest immediately deployable control” (o controle mais forte que dá para implantar de imediato) e relata que, em um benchmark adaptativo simples, ela foi contornada em 100% das amostras. Deixei isso de fora.
  • O conselho sobre assinatura. Recomendei aos operadores assinar as requisições no cliente e verificá-las no upstream, e chamei isso de “the only real fix” (a única correção real). O artigo pede a direção oposta, uma resposta assinada pelo provedor e verificada pelo cliente, e diz que assinar não resolve segredos roubados.
  • A chave vazada. Disse que a chave foi vazada “as if it had been exposed through a developer mistake” (como se tivesse sido exposta por um erro de desenvolvedor) e concluí que “The router was a laundering layer for a stolen key.” (o router foi uma camada de lavagem para uma chave roubada). Ela foi vazada em fóruns e grupos de chat onde operadores de routers compartilham credenciais, e o artigo diz que nem sempre consegue distinguir se quem a reutilizou foi um operador de router, um terceiro sem relação ou uma cadeia de relays mais longa.
  • As contagens de credenciais. O bloco de resposta dizia “17 of 28 paid routers touched planted AWS credentials” (17 de 28 routers pagos tocaram credenciais AWS plantadas), e a descrição dizia que os pesquisadores testaram 28 routers. A linha dos pagos no artigo não mostra abuso de credenciais observado. Os 17 routers que usaram canários da AWS, e o que esvaziou ETH, estão todos entre os 400 routers gratuitos.
  • O conselho sobre hooks. Recomendei hooks PostToolUse que “validate response shapes” (validem o formato das respostas). Um hook PostToolUse roda depois que a ferramenta já executou, tarde demais para um comando reescrito. O controle que o artigo testa é uma porta antes da execução, que no Claude Code é um hook PreToolUse com uma lista de permitidos.
  • Erros menores. O artigo tem seis autores, não cinco. Ele não diz que um router “knows when it is being sampled” (sabe quando está sendo amostrado); isso foi um floreio meu. A abertura chamava o tema de “MCP trust chains” (cadeias de confiança do MCP), e o artigo estuda routers, não MCP. Descrevi o post sobre egress silencioso como se fosse sobre descrições de ferramentas, quando ele trata de instruções escondidas em conteúdo buscado.

Perguntas frequentes

O que é um router de API LLM neste contexto?

Um serviço que aceita requisições em um formato unificado, geralmente compatível com a OpenAI, escolhe um provedor de modelo upstream e devolve a resposta. É um proxy de camada de aplicação com acesso em texto puro a cada requisição e cada resposta.1

O TLS me protege de um router malicioso?

Não. O cliente configura o router como seu endpoint, então o router encerra a sessão TLS do cliente e abre outra, separada, rumo ao provedor. O TLS protege cada trecho e não faz nada para proteger o payload do router.1

Como eu detectaria um router que está reescrevendo chamadas de ferramenta?

Não de forma confiável testando. Dois routers do estudo só injetavam depois de 50 requisições ou só em sessões com aprovação automática em projetos Rust ou Go, e o artigo conclui que “no fixed-length client test can guarantee that the router is benign” (nenhum teste de cliente de duração fixa consegue garantir que o router seja benigno). Uma lista de permitidos fail-closed para comandos de shell e instalações de pacotes bloqueia os casos simples, e o artigo mostra que um atacante que conhece a defesa passa por ela.1

Um hook PreToolUse ajuda?

Sim, como porta de política. O hook vê a chamada de ferramenta que o cliente recebeu, que é a reescrita se um router a reescreveu, e pode bloquear comandos que baixam de domínios não listados ou instalam pacotes não listados. Ele não consegue saber se a chamada é a que o modelo produziu.13

Estou rodando o Claude Code direto contra api.anthropic.com. Sou afetado?

Não pelos ataques de router deste artigo, porque não há intermediário. Se você passa o Claude Code por um proxy por qualquer motivo, como um gateway corporativo ou um agregador de modelos, esse proxy ocupa a mesma posição.

E o OpenRouter, o LiteLLM ou outros agregadores conhecidos?

O artigo mede 28 routers pagos de três marketplaces e 400 routers gratuitos construídos principalmente a partir de dois templates open source. Ele cita o LiteLLM e o OpenRouter como contexto e não os testa. O argumento estrutural vale para qualquer router: ele pode ler e reescrever o tráfego, e visibilidade é uma propriedade diferente de integridade. Para um proxy que você mesmo hospeda, a atualização de 1º de outubro, acima, cobre onze alertas do LiteLLM.

De quem eram as 401 sessões com aprovação automática?

De terceiros cujo tráfego chegou aos relays isca dos pesquisadores. Se você roda sessões de agentes com aprovação automática por um router que não construiu, pare, rotacione toda credencial que passou por ele e revise os logs das sessões em busca de chamadas de ferramenta inesperadas.


Referências


  1. Hanzhi Liu, Chaofan Shou, Hongbo Wen, Yanju Chen, Ryan Jingyang Fang e Yu Feng, “Your Agent Is Mine: Measuring Malicious Intermediary Attacks on the LLM Supply Chain,” arXiv:2604.08407v1, 9 de abril de 2026, listado para a ACM Conference on Computer and Communications Security, outubro de 2026. Texto completo lido em 1º e 2 de outubro de 2026. Seções usadas: introdução e 2.1 (o que são routers, o exemplo dos quatro saltos, o encerramento do TLS); 2.2 (nenhum mecanismo de integridade no nível do provedor); 4.1 e 4.2 (as quatro classes de ataque, o exemplo de requests para reqeusts, as cinco famílias de gatilhos); 5.1 (o pipeline de quatro etapas e as definições); 5.2 e tabelas 3 e 4 (1 router pago e 8 gratuitos injetando, 2 gratuitos com gatilhos, 17 gratuitos usando canários da AWS, 1 esvaziando ETH); 5.3 (a chave vazada e as iscas: 100 milhões de tokens, mais de sete sessões do Codex, mais de 40.000 tentativas de acesso de 147 IPs, cerca de 2 bilhões de tokens, cerca de 13 GB, 99 credenciais, 440 sessões, 398 projetos ou hosts, 401 em YOLO mode); 5.4 (as principais descobertas, incluindo a frase sobre acesso pago); 5.5 (escopo); 6 e tabela 5 (Mine, quatro frameworks, 1.000 requisições por módulo, compatibilidade de 100% e 99,6%); 7 e tabela 6 (as três defesas e seus resultados); 8.2 e 8.3 (o envelope de resposta assinado, MCP); 9 (a diferença entre a posição de um servidor MCP e a de um router); apêndice A (retenção, desativação de credenciais, o esvaziamento de menos de US$ 50, Mine não publicado). ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  2. Alerta do repositório do LiteLLM GHSA-3cv6-jpf6-8222 (CVE-2026-84377, PYSEC-2026-4066), “Authenticated SSRF and provider-credential exfiltration via unvalidated request-body routing parameters,” publicado no repositório em 26 de agosto de 2026, listado pelo NVD em 2 de setembro e revisado pelo GitHub em 30 de setembro; lido na página do repositório e no registro do OSV em 1º de outubro de 2026. O impacto e a frase completa dos paliativos são citados do alerta; a linha de patches do registro do OSV diz “Fixed in 1.96.2, 1.95.1, 1.94.3, 1.93.2, 1.92.2, 1.91.5, 1.90.7, 1.89.7, and 1.88.6,” e a página do repositório indica “Affected versions <1.94.0.” A contagem de onze corresponde às entradas PYSEC-2026-4066 a PYSEC-2026-4076 do banco de dados de alertas da PyPA, todas para o pacote litellm, todas datadas de 1º de outubro de 2026, nenhuma retirada. ↩↩↩↩↩

  3. Anthropic, Hooks reference, documentação do Claude Code, consultada em 2 de outubro de 2026. O PreToolUse roda “Before a tool call executes. Can block it” (antes de uma chamada de ferramenta ser executada; pode bloqueá-la); o PostToolUse roda “After a tool call succeeds” (depois que uma chamada de ferramenta é bem-sucedida); “Any other exit code doesn’t block on its own for most hook events” (qualquer outro código de saída não bloqueia por si só na maioria dos eventos); um hook de comando que estoura o tempo “doesn’t block the tool call” (não bloqueia a chamada de ferramenta), e “a mistyped path in settings.json leaves the gate silently disabled” (um caminho digitado errado no settings.json deixa a barreira desativada sem aviso). ↩↩↩

  4. GitHub Security Advisory GHSA-hx8v-g79f-8w5f (CVE-2026-59823, PYSEC-2026-4070), “LiteLLM Proxy has server-side request forgery via the user_config request parameter,” publicado em 17 de setembro de 2026, lido pelo OSV em 1º de outubro de 2026: afetadas <= 1.83.8, corrigido na 1.83.9, versão que o alerta data de 17 de abril de 2026. ↩

  5. Registros do OSV lidos em 1º de outubro de 2026: PYSEC-2026-4067 (CVE-2026-12773, autenticação do proxy MCP), PYSEC-2026-4069 (CVE-2026-12798, carregador de especificações OpenAPI do MCP) e PYSEC-2026-4068, 4071, 4072, 4073, 4074, 4075 e 4076. Os alertas do GitHub por trás desses nove foram publicados em 21 de junho de 2026, o mesmo dia em que o NVD os listou, e cada um cita o VulDB. As datas de upload e a versão atual vêm da página do projeto no PyPI e do seu JSON, lidos no mesmo dia: da 1.88.6 à 1.95.1 em 9 de agosto, 1.96.2 em 11 de agosto, 1.97.0 em 16 de agosto e 1.103.2 em 1º de outubro. ↩↩↩

  6. Leitura do autor do wheel do litellm 1.95.0 do PyPI (intervalo afetado da 1.95.0 até a correção na 1.95.1), 1º de outubro de 2026: em litellm/proxy/auth/auth_utils.py, _check_banned_params retorna antes de rejeitar qualquer coisa quando general_settings.get("allow_client_side_credentials") is True e, caso contrário, rejeita uma requisição cujo corpo traga um parâmetro da lista proibida, a menos que o configurable_clientside_auth_params da implantação permita esse parâmetro. A lista proibida, nessa versão, inclui vertex_ai_credentials e credenciais e hosts de observabilidade. Li uma versão afetada, não as nove linhas. ↩

Artigos relacionados

A fork bomb nos salvou

O atacante do LiteLLM cometeu um único erro de implementação. Foi só por causa dele que 47.000 instalações foram descobe…

7 min de leitura

Servidores MCP são a nova superfície de ataque

50 vulnerabilidades MCP, 30 CVEs em 60 dias, 13 críticas: a superfície de ataque que ninguém audita, com taxonomia e cor…

7 min de leitura

O Ralph Loop: Como Executo Agentes de IA Autônomos Durante a Noite

Construí um sistema de agentes autônomos com stop hooks, orçamentos de spawn e memória em sistema de arquivos. As falhas…

10 min de leitura