Pacote LiteLLM 1.107.0 adulterado rouba credenciais e chaves
A Microsoft detalhou como três arquivos de inicialização do Python embutidos no pacote recolhiam chaves de nuvem, de provedores de modelos e de ferramentas de desenvolvimento. O artefato ficou menos de duas horas no PyPI, e rotacionar credenciais é a única resposta proporcional para quem o instalou.
Imagem editorial original gerada para o Plugged Ninja. Arte abstrata, sem texto.
A versão 1.107.0 do LiteLLM, uma das camadas de integração mais usadas para conectar aplicações a modelos de linguagem, foi publicada adulterada no repositório PyPI e nas imagens de contêiner oficiais do projeto em 21 de outubro de 2026. O código malicioso não precisava que o LiteLLM fosse importado para rodar: ele era executado a cada inicialização do interpretador Python no ambiente afetado, coletava credenciais de nuvem, de ferramentas de desenvolvimento e de assistentes de IA e as enviava cifradas para um servidor controlado pelos atacantes.
A análise técnica foi publicada em 23 de outubro pela Microsoft Security Research, assinada pelos pesquisadores Mahesh Mandava, Srinivasan Govindarajan, Sagar Patil e Suriyaraj Natarajan, e atualizada em 27 de outubro. Os mantenedores do projeto publicaram seu próprio relato de incidente no mesmo período, com uma linha do tempo minuto a minuto. As duas leituras convergem no essencial e divergem em um ponto importante: a origem do acesso inicial segue não confirmada.
O caso importa além da base de usuários do LiteLLM. É o segundo incidente grave envolvendo esse componente em 2026 — em junho, a CISA incluiu no catálogo KEV uma vulnerabilidade do LiteLLM sob exploração ativa — e mostra como a infraestrutura que liga empresas a provedores de IA concentra, em um único processo, segredos de praticamente todo o ambiente.
Como o pacote adulterado chegou ao PyPI
O relato dos mantenedores descreve uma sequência de pouco mais de duas horas, toda em 21 de outubro (horários em UTC). Às 10h08min29s, a proteção de branch do GitHub rejeitou uma alteração não autorizada no fluxo de trabalho privado de release. Cinco segundos depois, às 10h08min34s, uma credencial de conta de desenvolvedor com privilégios foi usada para contornar essa proteção e aplicar a mudança.
Às 10h46min39s, o fluxo modificado iniciou uma execução de homologação apontando para um repositório sósia controlado pelo atacante, Berri-AI/litellm, em vez do legítimo BerriAI/litellm — um hífen no nome da organização. Às 11h11min18s, a credencial de uma segunda conta de desenvolvedor satisfez a etapa de aprovação de produção. Entre 11h39min51s e 11h40min13s, sete wheels maliciosos de litellm 1.107.0 foram enviados ao PyPI e, às 11h46min56s, os trabalhos de publicação propagaram o conteúdo para as imagens de contêiner.
Entre 11h53min49s e 11h54min01s, uma terceira credencial de desenvolvedor apagou os registros das execuções de produção e de homologação, reduzindo o material disponível para a investigação. O PyPI removeu a versão 1.107.0 às 13h23. Às 13h51, as tags no GHCR já apareciam indisponíveis, mas as do Docker Hub ainda apontavam para a imagem comprometida; às 14h20 todas as variantes afetadas foram removidas ou redirecionadas para artefatos verificados.
Sobre como o atacante obteve as credenciais, os mantenedores apresentam uma hipótese, e é preciso tratá-la como tal: eles afirmam ter encontrado uma conta de usuário de nome discreto em uma instalação interna do LiteLLM exposta à internet e acreditam que foi dali, a partir de registros de log, que os tokens do GitHub CLI foram extraídos. O relato suspeita de uma vulnerabilidade corrigida recentemente, mas declara que o acesso àquela instalação não foi confirmado. A Microsoft, por sua vez, afirma explicitamente que não determinou como o atacante obteve o acesso inicial. O próprio texto dos mantenedores adverte que foi escrito durante a investigação e que algumas suposições iniciais se mostraram imprecisas ou incompletas.
Por que o código rodava sem ninguém importar o LiteLLM
O detalhe mais instrutivo do incidente é o mecanismo de execução. Os wheels adulterados traziam três arquivos — cook.pth, cook.py e sitecustomize.py — instalados no diretório de bibliotecas do Python, fora da pasta litellm.
Esses dois formatos são recursos documentados da linguagem, não falhas. Arquivos com extensão .pth colocados no diretório de pacotes podem conter linhas iniciadas por import, que o módulo site executa durante a inicialização do interpretador. O sitecustomize.py é o gancho oficial de configuração específica da instalação, também carregado automaticamente. Vale registrar um detalhe que a documentação do Python traz: a execução de linhas iniciadas por import em arquivos .pth existe por compatibilidade retroativa e está marcada como obsoleta a partir do Python 3.15 — ou seja, o próprio ecossistema já caminha para fechar essa porta. Em outras palavras: bastava executar python ou pip no ambiente para acionar o código, mesmo em um comando que nada tivesse a ver com LiteLLM. A Microsoft registra justamente esse caso — a instalação de uma dependência sem relação com o projeto ativou o coletor em uma máquina Windows.
O conteúdo malicioso vinha embutido em Base64 comprimido com zlib. Segundo a Socket, que analisou os artefatos de forma independente, cada coletor decodificado tem cerca de 61 KB. Ele roda em uma thread daemon, usa uma variável de ambiente para evitar execuções duplicadas e grava um arquivo marcador em /tmp depois de um envio bem-sucedido.
O segundo caminho, no sitecustomize.py, é um carregador remoto: ele toma um lock em /tmp/remote_code_loader.lock, lê um arquivo hospedado no GitHub como ponteiro para o endereço do servidor do atacante e busca um recurso de código para executar, sem qualquer verificação de assinatura ou de hash, em ciclos de 3.600 segundos. No momento da análise da Microsoft, o arquivo de ponteiro estava vazio — ou seja, não houve confirmação de que um segundo estágio tenha sido entregue. A empresa descreve o componente como um mecanismo de execução remota rearmável, o que é diferente de dizer que ele foi usado.
O que era coletado
A lista de alvos é o ponto que torna o incidente grave para quem apenas tinha o pacote em um ambiente de build. A Socket conta 16 módulos de coleta; Microsoft e Socket descrevem, em conjunto, as seguintes categorias:
- Nuvem e orquestração: configurações e caches de credenciais de AWS, Azure CLI e Google Cloud, arquivos de conta de serviço do Kubernetes, configuração do Docker, segredos montados e requisições aos serviços de metadados de AWS e GCP.
- Chaves, código e pacotes: credenciais do Git, configuração de gerenciadores de pacotes, arquivos
.netrce.env, chaves SSH, GPG e TLS, configurações de CI/CD, Vault e infraestrutura como código. - Ferramentas de IA, IDEs e estação de trabalho: arquivos de autenticação de assistentes de código, editores e integrações MCP; bases de credenciais e histórico de navegadores Chromium e Firefox; históricos de shell e de clientes de banco; variáveis de ambiente do próprio processo e, no Linux, de outros processos legíveis via
procfs. - Base do proxy LiteLLM: quando a variável
DATABASE_URLestava definida, um processo filho consultava campos selecionados de duas tabelas,LiteLLM_CredentialsTableeLiteLLM_ProxyModelTable, com limite de 500 linhas por consulta. É aqui que ficam as chaves dos provedores de modelos usadas por toda a organização.
O envio usava criptografia híbrida: uma chave AES gerada por execução protegia os dados, e uma chave pública RSA de 4.096 bits embutida no código protegia a chave simétrica. O pacote resultante era enviado por HTTP POST a um domínio sósia do projeto, www[.]litellm[.]dev.
Escala, incertezas e o que não se sabe
Nenhuma das fontes informa quantas vezes a versão 1.107.0 foi baixada antes da remoção. A Socket registra que o projeto recebe cerca de 23 milhões de downloads semanais, número que descreve a popularidade do pacote, não o alcance do artefato adulterado. A janela de exposição no PyPI foi de aproximadamente 1h44 (11h39 às 13h23 UTC) e, nas imagens de contêiner, de cerca de 2h33 (11h46 às 14h20 UTC) — intervalos curtos, mas suficientes para contaminar pipelines de integração contínua que buscam dependências a cada execução.
Não há CVE atribuído: não se trata de uma vulnerabilidade de software, e sim do comprometimento da cadeia de publicação. A descoberta, segundo a Socket, veio de varredura automatizada que sinalizou os sete wheels cerca de sete minutos após a publicação; foi a comparação com a proveniência de build da versão legítima 1.106.2 que expôs o repositório sósia.
Um ponto do relato dos mantenedores merece cautela: eles afirmam que tokens OAuth do GitHub CLI podem acabar registrados em logs do LiteLLM a partir do uso de um assistente de programação. É uma hipótese da equipe do projeto sobre a origem das credenciais, não verificada de forma independente pelas outras fontes consultadas, em um documento que já foi corrigido em outros pontos.
O que fazer agora
As recomendações da Microsoft e dos mantenedores são convergentes e têm uma ordem que importa:
- Não execute
pythonnempipno ambiente suspeito para investigar. Qualquer invocação do interpretador reaciona os ganchos de inicialização. Use ferramentas confiáveis a partir de um sistema limpo. - Localize todas as cópias da 1.107.0: caches, espelhos internos, arquivos de lock, saídas de build e imagens de contêiner, comparando com os hashes publicados no aviso da Microsoft. Confirme a remoção de
cook.pth,cook.py,sitecustomize.pye dos arquivos correspondentes em__pycache__. - Reinstale fixando a versão 1.106.2. Os mantenedores afirmam que todas as versões anteriores estão íntegras e que a release privada corporativa não foi afetada. A tag de código-fonte da 1.107.0 no GitHub também não contém os arquivos maliciosos — o problema estava nos artefatos publicados. Reconstruir imagens e ambientes virtuais do zero é preferível a apagar arquivos.
- Rotacione credenciais, não apenas senhas. O escopo é tudo que a máquina alcançava: papéis IAM da instância, chaves de todos os provedores de modelos armazenadas na base do proxy, chaves mestras e virtuais, a senha do
DATABASE_URL, chaves SSH e GPG, tokens de CI/CD. Revogue também as sessões ativas e procure, no registro de auditoria da nuvem, uso dessas credenciais a partir de fora da sua infraestrutura.
Há um detalhe operacional que vale sublinhar: os mantenedores afirmam que a ausência do arquivo marcador em /tmp não prova nada, e que, se a versão 1.107.0 esteve instalada em um host com saída para a internet, o correto é presumir que a exfiltração ocorreu. É a postura certa para um incidente em que os registros de execução do pipeline foram deliberadamente apagados.
Conclusão
A técnica não é nova — ganchos de inicialização do Python são documentados há anos e já apareceram em pacotes maliciosos no PyPI. O que muda é o alvo: uma camada de integração de modelos é, por desenho, onde ficam as chaves de todos os provedores de IA de uma empresa, com frequência ao lado de credenciais de nuvem no mesmo contêiner. Vale notar que a proteção de branch funcionou e rejeitou a primeira tentativa; o que falhou foi a existência de credenciais capazes de sobrepor essa barreira e, depois, de apagar os próprios registros.
O incidente está contido: o artefato saiu do ar em menos de duas horas no PyPI, as imagens foram corrigidas e existe uma versão segura para fixar. O risco residual não está no pacote, está nas credenciais. Quem instalou a 1.107.0 em 21 de outubro deve assumir que tudo o que estava alcançável naquele host foi lido, e a rotação é a única resposta proporcional.
Dois pontos merecem acompanhamento nas próximas semanas: a publicação da análise de causa raiz definitiva pelos mantenedores, que deve esclarecer o acesso inicial hoje apenas suposto, e eventuais relatos de uso das chaves de provedores de modelos exfiltradas. Até lá, a lição prática é mais simples do que o incidente: trate o ambiente que hospeda seu proxy de modelos com o mesmo rigor que você aplica a um servidor de segredos, porque é exatamente isso que ele é.
Fontes e referências
- Microsoft Security Research — Inside the LiteLLM 1.107.0 supply chain compromise: Credential theft through Python startup hooks (23 de outubro de 2026, atualizado em 27 de outubro)
- LiteLLM (mantenedores) — Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised
- Socket — LiteLLM PyPI Package and Docker Images Compromised to Steal Cloud and AI Credentials
- Documentação do Python — site — Site-specific configuration hook