Como reconstruir o comprometimento da GitHub Action Changed Files da TJ
17 de março de 2025
0 minutos de leituraNa tarde de sexta-feira, 14 de março de 2025, começaram a surgir detalhes sobre uma grave exploração de segurança em uma popular GitHub Action chamada changed files (tj-actions/changed-files). Cerca de 23 mil repositórios do GitHub usam essa Action em seus fluxos de trabalho de CI e DevOps. Ela permite acompanhar quais arquivos foram alterados entre branches e commits.
Um invasor com privilégios de gravação no repositório da Action fez um commit que fez com que segredos criptografados aparecessem em texto simples nos logs do GitHub Actions. Isso poderia causar uma violação devastadora em um repositório público, onde os logs da Action também são públicos.
Primeiro, queremos tranquilizar nossos clientes: a Snyk concluiu uma avaliação da nossa infraestrutura e podemos confirmar que não usamos uma versão afetada da biblioteca vulnerável. Portanto, a Snyk está protegida contra essa vulnerabilidade.
Dito isso, queremos apresentar a análise aprofundada da vulnerabilidade feita pela Snyk, explicar como ela funciona e compartilhar medidas práticas para corrigi-la — o foco deste artigo.
Até o momento, não se sabe por que o invasor tinha permissões de gravação no repositório que hospeda o código dessa GitHub Action. Mas os principais fatores que viabilizaram o ataque, além das permissões de gravação, foram:
Alterar tags de versão existentes para apontar para o commit do ataque
Manter o commit do ataque órfão de qualquer branch, inclusive da branch principal
Esses fatores ajudaram a ocultar o ataque. Ele dependia de uma chamada de rede externa para baixar o código malicioso, o que acabou chamando a atenção de serviços que monitoram atividades de rede anômalas.
No fim deste artigo, reproduzo o ataque (de forma inofensiva) para você entender melhor como ele foi possível e como se proteger.
Sobre commits órfãos do Git
Experimente fazer o seguinte. Crie um novo repositório no GitHub e clone-o localmente. Crie uma branch e envie-a para o GitHub. Anote o hash do commit. Em seguida, exclua a branch remota. Você ainda poderá acessar esse commit, embora ele não esteja mais associado a nenhuma branch existente.
Veja as etapas descritas acima:
1. Crie um repositório no GitHub
2. Crie um repositório local e adicione o repositório remoto do GitHub que você acabou de criar
3. Crie uma branch
4. Adicione um commit e envie-o
5. Anote o commit no GitHub

6. Exclua a branch no GitHub

7. Acesse a URL do commit que você anotou acima

É assim que se parece uma branch órfã. Esse é um dos principais fatores que viabilizaram a exploração do fluxo de trabalho da GitHub Action changes-files.
O outro fator importante foi mover as tags de versão pelo repositório.
Sobre as tags de versão do GitHub
Infelizmente, às vezes desenvolvedores atribuem poderes quase mágicos às tags de versão do GitHub. Se nos dizem para usar a versão v35 de uma determinada release, referenciamos essa tag e presumimos que é isso que vamos obter. Em condições normais, essa é uma suposição razoável, mas as tags do GitHub são apenas strings convenientes que apontam para um commit específico — nada de mágico, mesmo com o uso cuidadoso de versionamento semântico. Veja um exemplo parcial de um arquivo YAML de GitHub Action que referencia a Action changes-files:
O ponto crucial está na última linha, que referencia uma tag específica do repositório da GitHub Action. O invasor excluiu as tags de versão originais e as transferiu para o commit malicioso, que era o commit órfão. Ao redirecionar várias tags de versão existentes para seu commit malicioso, o invasor ampliou o impacto do ataque.
Posso demonstrar isso dando continuidade ao exemplo anterior. Na minha máquina local, vou criar uma tag para a branch em que estou trabalhando:
No GitHub, o que acontece é que uma tag passa a ser associada ao commit órfão. Posso acessar:
Em seguida, vejo isto:

Assim, o invasor poderia direcionar as pessoas ao commit malicioso simplesmente atualizando o repositório do código da GitHub Action changed-files.
Em resumo, um agente malicioso tinha acesso de gravação a um repositório do qual outros 23 mil repositórios dependiam. Embora o invasor tenha encontrado uma forma engenhosa de ocultar suas ações, não teria conseguido realizar o ataque sem esse acesso de gravação.
Vamos analisar mais de perto o que o invasor queria obter.
Sobre o vazamento de segredos
Muitas vezes, são necessários segredos para criar scripts que interajam com outros serviços ou fornecer as chaves necessárias para compilar e executar testes.
O GitHub conta com um esquema sofisticado de criptografia para gerenciar segredos. Você pode definir um segredo no nível do repositório Git e acessá-lo no script de compilação sem que ele apareça nos logs de compilação ou seja exposto. Nos bastidores, o GitHub descriptografa e usa o segredo como referência no seu script de compilação. Na prática, uma linha do arquivo YAML da sua GitHub Action pode ser parecida com esta:
O restante do script passa a ter acesso ao segredo, sem que ele precise aparecer no próprio script ou nos logs.
O invasor modificou a GitHub Action changed-files da TJ para baixar um script de um local remoto, salvá-lo em um arquivo local na máquina virtual em que a GitHub Action estava sendo executada e, em seguida, executá-lo. Isso foi feito em duas etapas. A primeira foi o commit malicioso e órfão, que já foi excluído. Veja o trecho principal desse commit do Git:
Se você decodificar a longa string acima com Base64 (como o script faz), verá:
O gist referenciado foi removido, mas este é o script em Python que estava lá:
O script acima, executado como root com o comando sudo, examina a memória dos processos para encontrar os segredos descriptografados e exibi-los no log do Actions. Isso é extremamente prejudicial, pois os logs do GitHub Actions em repositórios públicos do GitHub também são públicos por definição.
Uma publicação no Hacker News (disponível aqui), publicada em 14 de março, indicou que a exploração havia sido detectada pela StepSecurity, um serviço que monitora chamadas de rede não autorizadas no GitHub Actions, entre outras atividades. O autor e mantenedor da popular GitHub Action comentou na publicação e explicou o que aconteceu. O primeiro problema que ele apontou foi o fato de o invasor ter acesso de gravação ao repositório.
Graças à rápida descoberta da exploração e à agilidade do mantenedor, ela foi corrigida e bloqueada rapidamente. Ainda assim, recomenda-se que usuários da GitHub Action changed-files verifiquem os logs desde 14 de março para garantir que nenhum segredo tenha sido exposto em um log público do Actions.
A exploração em ação
Criei um conjunto de repositórios para demonstrar essa exploração, configurados da forma mais próxima possível do ambiente do invasor.
O primeiro repositório Git define a GitHub Action personalizada: tj-changed-files-action-goof.
O arquivo index.js é simples e foi, em grande parte, baseado no tutorial do GitHub sobre como criar Actions:
Ele espera uma entrada chamada who-to-greet e envia a data e a hora como saída.
O outro repositório tem apenas um arquivo: um arquivo YAML configurado para executar a GitHub Action personalizada. Ele também tem um segredo definido no repositório, chamado A_SECRET. O repositório se chama tj-changed-files-action-exploit-goof. No arquivo .github/workflows/main.yml, você verá o seguinte:
Ele faz algumas coisas:
Exibe uma mensagem "Hello, world!"
Executando a GitHub Action
tj-changed-files-action-goofExibe a hora retornada pela Action. Também usa o segredo do repositório da forma esperada em uma GitHub Action.
Compare a saída de A Goof Step em duas execuções diferentes. Primeiro, esta:
Em seguida, esta:
Nas duas execuções, a GitHub Action snyk-labs/tj-changed-files-action-goof@v1.0 está sendo executada. Na segunda, você pode ver a saída da GitHub Action comprometida. Se decodificarmos a string codificada em Base64 duas vezes, veremos:
Nas duas execuções, você pode ver que o GitHub Actions protege o valor do segredo quando ele é referenciado: a_secret: ***. No entanto, o código da exploração extrai os valores em texto simples dos segredos diretamente da memória do sistema.
Se voltarmos à tag v1.0 do repositório tj-changed-files-action-goof, veremos uma branch órfã semelhante à da exploração original:

Inicialmente, v1.0 apontava para a branch main. Mas, ao simular a exploração, simplesmente redirecionei a tag para uma branch attack, que depois deixei órfã:
O último detalhe — fácil de passar despercebido — é o comando ncc build index.js. Ele atualiza o arquivo dist/index.js, e somente esse arquivo atualizado é incluído no commit. É o arquivo dist/index.js, empacotado para a web, que a GitHub Action realmente executa. Por isso, se você olhar o arquivo index.js na branch v1.0 (órfã), não vai parecer que nada mudou.
Veja o diff do código webpack em dist/index.js na versão v1.0, em comparação com a branch main.
Proteja suas GitHub Actions
Tenho certeza de que mais detalhes virão à tona sobre como ou por que o invasor tinha acesso de gravação a esse popular repositório de GitHub Action.
É comum usar tags do Git como versões de referência para as diversas bibliotecas das quais dependemos.
Há algumas maneiras de evitar essa exploração específica.
1. Em GitHub Actions personalizadas e remotas, referencie o hash do commit diretamente
Por exemplo, você poderia:
Isso referencia diretamente o hash do commit, em vez de uma tag. Essa prática não é comum, mas garante que a versão executada da GitHub Action personalizada seja a versão esperada.
2. Use outras GitHub Actions personalizadas para sinalizar chamadas de rede inesperadas. Foi assim que a StepSecurity detectou a exploração.
É animador ver uma comunidade de colaboradores de código aberto, empresas e pessoas se mobilizando para detectar e corrigir novas explorações assim que elas surgem, como aconteceu neste caso.
A Snyk já publicou pesquisas e boas práticas sobre GitHub Actions que recomendamos fortemente que você conheça e use para avaliar suas práticas de DevSecOps e segurança:
Um chamado à ação: explorando vulnerabilidades no GitHub Actions
Como criar um pipeline de CI/CD seguro com GitHub Actions para sua aplicação Java
Como publicar pacotes npm com segurança usando o GitHub Actions
Conheça o cenário atual da segurança de código aberto
Entenda as tendências e abordagens atuais para proteger softwares de código aberto e a cadeia de suprimentos.
