Como um scanner de segurança comprometido abriu caminho para backdoor no LiteLLM
24 de março de 2026
0 minutos de leituraEm 24 de março de 2026, duas versões do pacote Python litellm no PyPI foram identificadas com código malicioso. Os pacotes (versões 1.82.7 e 1.82.8) foram publicados pelo agente de ameaças conhecido como TeamPCP, que obteve as credenciais do PyPI do mantenedor após um comprometimento anterior do Trivy, um scanner de segurança de código aberto usado no pipeline de CI/CD do LiteLLM.
As versões maliciosas ficaram disponíveis por aproximadamente três horas, até que o PyPI colocou o pacote em quarentena. O LiteLLM é baixado cerca de 3,4 milhões de vezes por dia.
A Snyk está acompanhando esse incidente. Se você é cliente da Snyk, talvez já tenha visto o alerta no banner do app e recebido uma notificação por e-mail. O registro da vulnerabilidade é SNYK-PYTHON-LITELLM-15762713, e as atualizações de status estão no Snyk Trust Center.
Resumo
Pacote afetado |
|
Versões afetadas | 1.82.7, 1.82.8 |
Versões seguras | ≤ 1.82.6 |
ID da Snyk | |
Primeira detecção | 10:39 UTC, 24 de março de 2026 (upload da versão 1.82.7) |
Quarentena no PyPI | \~13:38 UTC, 24 de março de 2026 |
Atacante | TeamPCP (também conhecido como PCPcat, Persy_PCP, ShellForce, DeadCatx3) |
Vetor de ataque | Cadeia de suprimentos: credenciais comprometidas de publicação no PyPI por meio de uma GitHub Action do Trivy adulterada no CI/CD do LiteLLM |
Tipo de payload | Três etapas: coleta de credenciais + exfiltração criptografada + backdoor persistente + worm de Kubernetes |
Domínio de exfiltração |
|
MITRE ATT\&CK | T1546.018 (Python Startup Hooks), T1003 (Credential Dumping), T1610 (Deploy Container) |
Principais eventos
Horário (UTC) | Evidência | Evento |
|---|---|---|
Final de fev. de 2026 |
| |
19 de mar., 17:43 UTC | As tags da GitHub Action do Trivy | |
23 de mar., 12:58 UTC | Endor Labs (capturou os metadados do PyPI antes da exclusão) | GitHub Action do Checkmarx KICS comprometida; domínio de C2 |
24 de mar., 10:39 UTC | Endor Labs (capturou os metadados do PyPI antes da exclusão) |
|
24 de mar., 10:52 UTC |
| |
24 de mar., 11:48 UTC | FutureSearch (Callum McMahon) abre uma issue para divulgar o problema | |
24 de mar., 12:36 UTC | Tópico publicado no HN; chega a 324 pontos | |
24 de mar., \~12:44 UTC | Issue #24512 no GitHub (visível nos horários dos comentários) | Comentários de bots inundam a issue #24512; a issue é encerrada usando a conta comprometida do mantenedor |
24 de mar., 13:03 UTC | FutureSearch (atualização com data e hora) | FutureSearch confirma o encerramento da issue e o spam de bots |
24 de mar., 13:48 UTC | Nova issue de acompanhamento aberta | |
24 de mar., 15:09 UTC | Mantenedor do LiteLLM confirma que todas as chaves do GitHub, Docker e PyPI foram trocadas; as contas dos mantenedores foram migradas para novas identidades | |
24 de mar., 15:27 UTC | Versões comprometidas excluídas; pacote retirado da quarentena no PyPI |
Como o problema foi descoberto
Callum McMahon, da FutureSearch, estava testando um plugin Cursor MCP que incluía litellm como dependência transitiva. Pouco depois da inicialização do Python, a máquina parou de responder por falta de RAM. Ele rastreou o problema até o pacote litellm recém-instalado e encontrou litellm_init.pth, um arquivo de 34.628 bytes em site-packages/, codificado duas vezes em base64.
A falta de RAM foi um efeito colateral do payload, não um recurso intencional. O mecanismo .pth é acionado a cada inicialização do interpretador Python. Como o payload inicia um novo subprocesso Python, e esse novo processo também aciona a execução de .pth, o resultado foi um fork bomb não intencional. McMahon publicou suas descobertas em futuresearch.ai, e a divulgação chegou a r/LocalLLaMA, r/Python e Hacker News em menos de uma hora.
A cadeia de ataque
O ataque ao LiteLLM começou cinco dias antes, com o Trivy.
19 de março: Os atacantes reescreveram tags do Git no repositório da GitHub Action trivy-action para apontar para uma versão maliciosa (v0.69.4) que continha o mesmo payload de coleta de credenciais e a mesma infraestrutura de exfiltração usados em operações posteriores. (Para saber todos os detalhes sobre o comprometimento do Trivy, consulte a cobertura da Snyk sobre o comprometimento da cadeia de suprimentos da GitHub Actions do Trivy.)
23 de março: A mesma infraestrutura foi usada em um ataque separado ao Checkmarx KICS (Keep Infrastructure as Code Secure). O domínio de C2 checkmarx.zone, que se passa pela empresa de segurança Checkmarx, foi registrado e ativado nessa operação.
24 de março: O pipeline de CI/CD do LiteLLM executou o Trivy como parte do processo de build, instalando-o de apt sem fixar uma versão. A action comprometida exfiltrou o token PYPI_PUBLISH do ambiente do runner do GitHub Actions. Com essa credencial, os atacantes publicaram litellm 1.82.7 às 10:39 UTC e 1.82.8 às 10:52 UTC, ambas com payloads maliciosos.

Resposta inicial do mantenedor do LiteLLM no Hacker News.
Dois mecanismos de distribuição
As duas versões maliciosas usaram técnicas de injeção diferentes:
1.82.7 (injeção no código-fonte): O payload foi codificado em base64 e inserido diretamente em litellm/proxy/proxy_server.py. Ele é executado quando qualquer coisa importa litellm.proxy, o caminho de importação padrão para o modo de servidor proxy do LiteLLM.
1.82.8 (arquivo .pth): Esta versão adicionou litellm_init.pth a site-packages/. O mecanismo .pth é acionado a cada inicialização do interpretador Python, sem necessidade de importação, inclusive quando pip, python -c ou o servidor de linguagem de uma IDE inicia o Python.
O arquivo .pth está declarado corretamente no arquivo RECORD do wheel e, por isso, passa na verificação de hash do pip. O pacote passa por todas as verificações de integridade padrão porque o conteúdo malicioso foi publicado com credenciais legítimas, e não injetado depois. A Snyk Learn apresenta um guia detalhado sobre como esse tipo de ataque funciona em Comprometimento de um pacote legítimo.
Os mantenedores do CPython reconheceram o risco à segurança dos hooks de inicialização .pth na issue #113659, mas nenhuma correção foi aplicada. Essa técnica corresponde à MITRE ATT\&CK T1546.018 (Python Startup Hooks).
O payload em três etapas
Etapa 1: coleta de informações
O script coleta:
Dados do sistema: nome do host, variáveis de ambiente, processos em execução, roteamento de rede e registros recentes de autenticação em
/var/log/auth.logCredenciais: chaves privadas SSH, arquivos
.env, credenciais do Git, chaves de API do histórico do shell, tokens de webhook do Slack e Discord, configurações de CI/CD (Jenkins, Travis CI, Terraform) e hashes de senhas em/etc/shadowCredenciais de nuvem: credenciais e configurações da AWS, arquivos de contas de serviço do GCP e Azure, metadados de nuvem por meio de consultas IMDS diretas, AWS Secrets Manager e SSM Parameter Store (com assinatura completa do IMDSv2 implementada no script)
Contêineres e orquestração:
config.jsondo Docker (credenciais de registro), arquivoskubeconfigdo Kubernetes, tokens de contas de serviço, segredos e certificadosCriptomoedas: arquivos de carteiras e frases-semente de Bitcoin, Ethereum, Solana, Cardano e Monero, além de configurações de carteiras físicas Ledger

Trecho do código malicioso decodificado, mostrando a iteração estruturada e a coleta de segredos em vários diretórios
Etapa 2: criptografia e exfiltração
Os dados coletados são criptografados antes da transmissão:
Uma chave de sessão AES-256 de 32 bytes é gerada com
openssl randOs dados são criptografados com AES-256-CBC (derivação de chave PBKDF2)
A chave de sessão é criptografada com uma chave pública RSA de 4096 bits embutida no código (preenchimento OAEP)
Tudo é empacotado em
tpcp.tar.gzO pacote é enviado por POST para
https://models.litellm.cloud/usandocurl
models.litellm.cloud é diferente do domínio legítimo litellm.ai e foi registrado em 23 de março, um dia antes da publicação dos pacotes maliciosos.
A chave pública RSA desse payload é idêntica à encontrada nos payloads do Trivy e do KICS. Pesquisadores da Wiz confirmaram essa correspondência nas três operações, estabelecendo o vínculo técnico de atribuição mais forte.
Arquivos temporários criados durante a exfiltração: session.key, payload.enc, session.key.enc, tpcp.tar.gz no diretório temporário do sistema.

Visualização neutralizada do código malicioso. O nome do arquivo tpcp.tar.gz é um indicador consistente nas operações do Trivy, KICS e LiteLLM.
Etapa 3: persistência e movimento lateral
Persistência local:
Grava um script Python de backdoor em
~/.config/sysmon/sysmon.py(ou/root/.config/sysmon/sysmon.py)Cria um serviço de usuário do systemd em
~/.config/systemd/user/sysmon.servicecom a descrição "System Telemetry Service"Registra e ativa o serviço:
systemctl --user daemon-reload && systemctl --user enable --now sysmon.service
O script de persistência consulta https://checkmarx.zone/raw a cada 5 minutos em busca de uma URL, baixa o arquivo para /tmp/pglog, dá permissão de execução e o executa em segundo plano. O estado é registrado em /tmp/.pg_state. No momento da análise, o endpoint retornava URLs do YouTube; o operador pode passar a distribuir payloads ativos quando quiser.
Movimento lateral no Kubernetes: Se o script encontra um token de conta de serviço do Kubernetes no caminho de montagem padrão, ele lê todos os segredos de todos os namespaces. Em seguida, tenta implantar um pod privilegiado em cada nó de kube-system usando alpine:latest. Esses pods montam o sistema de arquivos do host e instalam o backdoor sysmon no nó subjacente.
Os pods maliciosos recebem o nome node-setup-{node_name} (com o nome do nó truncado para 35 caracteres) e contêm um contêiner chamado setup.
Sobre o TeamPCP
O TeamPCP (também identificado como PCPcat, Persy_PCP, ShellForce e DeadCatx3, segundo o Wiz Threat Center) está ativo desde pelo menos dezembro de 2025. O agente mantém canais no Telegram, @Persy_PCP e @teampcp, e inclui a string "TeamPCP Cloud stealer" nos payloads. A Wiz acompanha a campanha completa (Wiz Threat Center; blog da Wiz; ramimac.me).
O comprometimento do LiteLLM é a Fase 09 de uma campanha em andamento. Todas as operações compartilham uma infraestrutura consistente: o mesmo par de chaves RSA, o mesmo nome de pacote tpcp.tar.gz e repositórios do GitHub com prefixo tpcp-docs, usados como pontos de apoio para preparar o C2. Os três domínios dessa operação compartilham o mesmo registrador (Spaceship, Inc.) e provedor de hospedagem (DEMENIN B.V.).
O agente também implantou o CanisterWorm, que usa o Internet Computer Protocol (ICP) como canal de C2. Os canisters do ICP não podem ser desativados por registradores de domínio nem por provedores de hospedagem. Pesquisadores de segurança da Aikido documentam esse caso como o primeiro uso observado do ICP como mecanismo de C2 em uma campanha da cadeia de suprimentos.
Um componente chamado hackerbot-claw usa um agente de IA (openclaw) para direcionar ataques de forma automatizada. Pesquisadores da Aikido documentaram esse caso como um dos primeiros usos operacionais de um agente de IA em um ataque à cadeia de suprimentos.
Supressão de issues
Quando membros da comunidade começaram a relatar o comprometimento na issue #24512 do GitHub, os invasores publicaram 88 comentários de bots usando 73 contas únicas em uma janela de 102 segundos (12:44–12:46 UTC). As contas usadas eram contas de desenvolvedores previamente comprometidas, não perfis criados para esse fim. A análise de Rami McCarthy constatou uma sobreposição de 76% de contas com a botnet usada durante a divulgação do caso Trivy.
Usando a conta de mantenedor comprometida krrishdholakia, os invasores fecharam a issue #24512 como "not planned" e fizeram commits em repositórios sem relação com o caso, usando a mensagem "teampcp update."
A comunidade abriu uma issue paralela para acompanhar o caso (#24518) e continuou a discussão no Hacker News, onde a thread chegou a 324 pontos.
Impacto confirmado
As versões afetadas ficaram no PyPI por aproximadamente 3 horas. Os projetos a seguir abriram PRs ou issues de segurança em 24 de março para fixar a versão e evitar a 1.82.7 e a 1.82.8:
Projeto | Evidência |
|---|---|
DSPy | PR #9498 mesclado; relatório de falha no CI |
MLflow | PR #21971 mesclado |
OpenHands | |
CrewAI | |
langwatch | |
strands-agents/sdk-python | |
Arize Phoenix | |
nanobot | |
dreadnode/rigging | |
CoPaw | |
Aider | Confirmado como seguro (fixa a versão |
O mecanismo .pth é acionado quando qualquer processo Python é iniciado, inclusive o próprio pip. Em ambientes de CI/CD, isso significa que o payload pode ser executado durante as etapas de build, e não apenas em tempo de execução da aplicação.
Detecção: você foi afetado?
Etapa 1: confira a versão instalada
Se a saída mostrar 1.82.7 ou 1.82.8, considere o sistema comprometido e siga as instruções de correção abaixo. Não basta fazer upgrade: o payload pode já ter sido executado.
Etapa 2: procure artefatos de persistência
Etapa 3: procure arquivos .pth maliciosos
Etapa 4: verifique os hashes dos arquivos
Etapa 5: procure indicadores de rede
Etapa 6: verifique o Kubernetes
Etapa 7: faça uma varredura com o Snyk

O alerta em banner no app do Snyk, com os repositórios afetados exibidos no inventário de ativos. O link do Trust Center leva ao status do incidente em tempo real.
Clientes do Snyk também podem consultar o consultor do pacote litellm e o registro completo da vulnerabilidade em SNYK-PYTHON-LITELLM-15762713.
Correção
Se você NÃO instalou a versão 1.82.7 ou 1.82.8:
Fixe a versão em <=1.82.6 até que uma versão limpa esteja disponível:
Se você instalou a versão 1.82.7 ou 1.82.8:
O payload é executado quando o Python é iniciado, inclusive durante o próprio pip install. Considere o sistema potencialmente comprometido, independentemente de você ter executado algum código de aplicação.
Remova os artefatos de persistência:
Troque as credenciais no sistema afetado:
Chaves privadas SSH: gere novas chaves e revogue as antigas em
authorized_keys, GitHub e GitLabCredenciais de nuvem: chaves de acesso da AWS, chaves de conta de serviço do GCP e entidades de serviço do Azure
Chaves de API: arquivos
.env, variáveis de ambiente do shell e segredos de CI/CDCredenciais de registro do Docker:
~/.docker/config.jsonKubernetes:
~/.kube/config, tokens de conta de serviço no clusterSenhas de banco de dados em qualquer arquivo de configuração do sistema
Credenciais do Git em
~/.gitconfigou no armazenamento de credenciais do sistemaFrases-semente de carteiras de criptomoedas
Faça uma auditoria no AWS Secrets Manager e no SSM Parameter Store, pois o payload consulta esses serviços diretamente quando os metadados da instância estão acessíveis.
Faça uma auditoria nos segredos do cluster Kubernetes. Se havia um token de conta de serviço, todos os segredos de todos os namespaces podem ter sido lidos. Procure pods
node-setup-*emkube-system.
Instale uma versão limpa em um ambiente novo, em vez de fazer upgrade no ambiente atual:
Por que a verificação de hash do pip não detectou o problema
A verificação de hash confirma que um arquivo corresponde ao que o PyPI anunciou, mas não indica se o conteúdo anunciado é malicioso.
O arquivo litellm_init.pth na versão 1.82.8 está declarado corretamente no arquivo RECORD do wheel, com um hash correspondente. O comando pip install --require-hashes teria sido concluído sem problemas. O pacote passa por todas as verificações de integridade padrão porque o conteúdo malicioso foi publicado com credenciais legítimas: não há divergência de hash, domínios suspeitos nem nome de pacote com erro de digitação.
A única forma de detectar o problema durante a instalação é verificar se um pacote instala arquivos .pth e se esses arquivos contêm padrões como subprocess, base64 ou exec. Atualmente, nenhum plugin do pip amplamente utilizado faz isso automaticamente.
O padrão mais amplo
A seleção de alvos nesta campanha se concentra em ferramentas com acesso elevado a pipelines automatizados: um scanner de contêineres (Trivy), uma ferramenta de varredura de infraestrutura (KICS) e uma biblioteca de roteamento de modelos de IA (LiteLLM). Por definição, cada uma dessas ferramentas precisa de amplo acesso de leitura aos sistemas com que opera (credenciais, configurações e variáveis de ambiente).
O LiteLLM vem sendo cada vez mais usado como gateway centralizado de LLMs que armazena credenciais de API de vários provedores de modelos. Nessa configuração, o conjunto de credenciais acessível a partir de um único host comprometido é mais amplo do que o de uma aplicação típica.
A divulgação inicial se espalhou por comunidades de desenvolvedores de IA (r/LocalLLaMA, r/Python, Hacker News), e não por canais tradicionais de segurança, como r/netsec ou feeds de CVEs.
Para saber mais sobre riscos à cadeia de suprimentos específicos de ferramentas para LLMs, confira o conteúdo do Snyk Learn sobre vulnerabilidades na cadeia de suprimentos de LLMs. O ataque à cadeia de suprimentos do Ultralytics AI Pwn Request, de 2024, também é um caso útil para comparação: outra biblioteca Python de IA amplamente usada foi comprometida por meio de uma exploração de CI/CD, em uma cadeia de ataque semelhante.
Indicadores de comprometimento
Hashes dos arquivos:
Arquivo | SHA-256 |
|---|---|
|
|
|
|
|
|
Rede:
Exfiltração:
https://models.litellm.cloud/(POST)Consulta ao C2:
https://checkmarx.zone/raw(GET)
Sistema de arquivos:
~/.config/sysmon/sysmon.pyou/root/.config/sysmon/sysmon.py~/.config/systemd/user/sysmon.service(descrição: "System Telemetry Service")/tmp/tpcp.tar.gz,/tmp/session.key,/tmp/payload.enc,/tmp/session.key.enc/tmp/.pg_state,/tmp/pglog
Kubernetes:
Pods:
node-setup-{node_name}emkube-systemNome do contêiner:
setup, imagem:alpine:latest
Prefixo da chave pública RSA (codificado nos payloads das três operações):
O que fazer agora
Confira a versão do litellm:
pip show litellm | grep VersionFixe a versão em
<=1.82.6em todos os ambientesExecute
snyk test --package-manager=pipSe você tinha a versão 1.82.7 ou 1.82.8 instalada: troque as credenciais e procure artefatos de persistência
Faça uma auditoria nos pipelines de CI/CD em busca de versões de ferramentas sem fixação, incluindo GitHub Actions
Verifique o Kubernetes:
kubectl get pods -A | grep node-setup-
WHITE PAPER
A crise de segurança da IA no seu ambiente Python
Com o ritmo de desenvolvimento acelerando cada vez mais, você sabe o que o seu ambiente de IA pode acessar?
