- O pesquisador compartilha a experiência de ter descoberto uma vulnerabilidade no Nixpkgs que permitia injetar código malicioso em todo o ecossistema Nix
- Explica o risco estrutural de o gatilho GitHub Actions
pull_request_target expor permissões sensíveis e segredos mesmo em PRs de contribuidores externos
- Demonstrou na prática que era possível escalar privilégios por meio de injeção de comandos e uso de links simbólicos em tarefas do editorconfig-checker e de validação de code owners
- Assim que a falha foi descoberta, a equipe mantenedora do Nixpkgs corrigiu rapidamente a vulnerabilidade, desativou os workflows vulneráveis e revisou a operação das permissões
- Reforça a importância de separar dados não confiáveis de operações sensíveis na infraestrutura de CI/CD, aplicar o princípio do menor privilégio e fortalecer políticas
Hackeando todo o ecossistema Nix
Introdução e contexto
- No NixCon do ano passado, o pesquisador e sua colega Lexi apresentaram uma vulnerabilidade no Nixpkgs
- A falha descoberta abria a possibilidade de inserir código malicioso em todo o ecossistema Nix por meio de um ataque à cadeia de suprimentos
- Foi uma experiência de resposta rápida, da detecção da vulnerabilidade ao reporte e à contenção, tudo em apenas um dia
- Como o pesquisador não poderá participar do NixCon este ano, detalha o processo neste texto
GitHub Actions: um alvo vulnerável
- GitHub Actions é um sistema que oferece suporte a várias tarefas de automação (CI/CD) em repositórios de código
- Basta ter acesso aos arquivos de workflow para que seja fácil inserir código, o que o torna um alvo importante de ataques à cadeia de suprimentos
- Os arquivos de workflow são escritos em YAML e, como não foram pensados como um formato de execução, podem conter vulnerabilidades de segurança inesperadas
- Como exemplo simples, há workflows que executam comandos quando código é enviado ao repositório
O perigoso gatilho pull_request_target
- O GitHub Actions tem vários gatilhos, e pull_request_target é bem diferente do pull_request comum
- Por padrão, o pull_request_target tem permissões de leitura/escrita e acesso a segredos mesmo vindos de forks (PRs)
- Quando esse gatilho é usado de forma incorreta, dados externos não confiáveis acabam combinados com permissões sensíveis
- A própria documentação oficial do GitHub alerta claramente para esse risco
- O pesquisador analisou 14 workflows que usavam pull_request_target no repositório Nixpkgs
Vulnerabilidade no editorconfig-checker
- O primeiro workflow vulnerável encontrado tinha como objetivo verificar regras do editorconfig
- Depois de calcular a lista de arquivos alterados, ela era passada ao editorconfig-checker com xargs
- Se o aviso de segurança do comando xargs for ignorado, surge uma vulnerabilidade que permite inserir nomes de arquivo maliciosamente projetados (por exemplo, --help)
- Com isso, torna-se possível manipular o editorconfig-checker de forma arbitrária ou até abrir caminho para execução adicional de comandos (a análise detalhada ainda exige revisão)
Vulnerabilidade no codeowners-validator: inclusão de arquivo local
- A segunda vulnerabilidade, mais grave, foi encontrada no workflow de validação do arquivo CODEOWNERS
- Esse processo faz checkout do código da PR e depois usa o codeowners-validator para verificar o arquivo
- Quem envia a PR pode substituir o arquivo OWNERS por um link simbólico, permitindo referenciar arquivos arbitrários no runner (por exemplo, credenciais da action)
- Como resultado, durante a validação o conteúdo desse arquivo é impresso nos logs, o que expõe um token do GitHub com permissão de leitura/escrita
- Com esse token em mãos, torna-se possível fazer push diretamente no repositório Nixpkgs
Medidas tomadas e lições aprendidas
- Após o reporte da vulnerabilidade, o mantenedor do Nixpkgs infinisil reagiu imediatamente
- Desativou temporariamente os workflows vulneráveis
- Corrigiu e separou os pontos em que dados não confiáveis estavam ligados a permissões
- Após a correção de segurança, alterou os nomes dos workflows para mitigar problemas de direcionamento por branch
- Principais lições
- Nunca combinar dados não confiáveis com segredos e operações sensíveis, ou no mínimo fazê-lo com extremo cuidado
- Seguir o princípio de operação com menor privilégio
- É essencial conhecer as diretrizes oficiais sobre permissões no GitHub Actions
- Em caso de vulnerabilidades semelhantes, administradores da organização podem desativar o Actions em massa por política (há instruções de configuração)
Conclusão
- Em apenas um dia, o pesquisador encontrou uma vulnerabilidade capaz de colocar todo o ecossistema Nix em risco, reportou o problema e participou da correção
- Isso evidencia a necessidade de extremo cuidado ao usar o GitHub Actions, especialmente o pull_request_target
- Ele agradece a Intrigus, da KITCTF, que ajudou na pesquisa, e a infinisil, que respondeu rapidamente
- Leitores interessados são incentivados a consultar o vídeo da apresentação e materiais adicionais
- No geral, o texto reforça a importância da segurança no GitHub Actions e as lições práticas aprendidas
1 comentários
Comentários no Hacker News
Acho que
pull_request_targeté fundamentalmente inseguro, e o GitHub deveria simplesmente remover esse recurso. Em geral se diz que, para usarpull_request_targetcom segurança, basta não executar durante o processo código controlado pela branch, mas na prática a superfície de ataque é muito maior por causa de injeção de argumentos, inclusão de arquivos locais e afins. Hoje, os únicos casos de uso legítimos são coisas como aplicar labels automaticamente ou postar comentários automáticos em PRs de terceiros. Não acho que esse tipo de tarefa precise, por padrão, de permissão de escrita no repositório. O GitHub deveria poder emitir um token limitado apenas a essa tarefa. Por isso eu marco no zizmor qualquer uso de gatilhos perigosos comopull_request_target; veja zizmor dangerous triggerspull_request. Nessa situação,pull_request_targeté praticamente a única opção. Seria melhor se o GitHub criasse uma configuração para permitir workflows em PRs com merge não limpo, desativada por padrão e usada só para coisas como linters. Até lá, é uma pena que essa limitação nos force a usar apenaspull_request_target. E, por sinal, quando se usa esse tipo de ferramenta externa, fazer merge manual no GitHub quebra todo o fluxo; merge manual é terminantemente proibidopull_request_targetpor dois motivos. Primeiro, porque o workflow sempre roda com base na main, então dá para bloquear código de teste não verificado. Segundo, porque no sub claim do JWT dentro do workflow ojob_workflow_refé fornecido de forma decisiva, o que permite controle de acesso granular em sistemas baseados em OIDCpull_request_targetroda no contexto base do PR, então impediria que código malicioso roubasse o repositório ou os secrets. Mas, na prática, quando você vê como é fácil vazar secrets, a situação fica até meio ridículaSe a equipe do Nix tivesse adotado, como propus no RFC, builds reproduzíveis assinadas de forma independente de commits/reviews assinados, esse tipo de ataque de supply chain de última milha teria sido impossível. No fim, o NixPkgs quer ser fácil de editar por qualquer pessoa e tem medo de que tentativas focadas em segurança afastem voluntários; o objetivo é se concentrar numa distribuição hobby. Tudo bem, mas então isso precisa ser comunicado com clareza, e se quiserem proteger o que tem valor, deveriam parar de usar ou recomendar o Nix em ambientes críticos de segurança. Um sistema operacional que protege algo precisa exigir assinatura de hardware rigorosa por duas partes em toda mudança, e não pode depositar confiança em uma única máquina ou uma única pessoa. Foi por isso que criei o Stagex link do Stagex link do Codeberg
Embora eu tenha passado a vida projetando sistemas computacionais, ainda fico muito espantado que workflows modernos continuem emitindo bearer tokens para programas em que confiam, até tokens de curta duração. Se o framework do GitHub Actions oferecesse apenas acesso a um socket Unix privilegiado ou a um
ssh-agent, esse tipo de vulnerabilidade seria muito mais difícil de explorarAções de CI/CD para pull/merge requests são um verdadeiro pesadelo. Quando o desenvolvedor escreve etapas de teste ou validação, normalmente pensa em termos de “meu código roda no contexto da minha conta do GitHub/GitLab”. Isso vale para commits próprios ou de colegas do time, mas em PRs o pipeline de CI/CD executa código não confiável. É difícil manter essa diferença sempre clara. Para testes simples ou linters tudo bem, mas quando o trabalho precisa integrar com infraestrutura e exige mais permissões, rapidamente fica perigoso
pull_requestepull_request_target). Um deles (por exemplo,pull_request) é quase sempre seguro, a menos que seja usado incorretamente de propósito, enquanto o outro (pull_request_target) é quase impossível de usar com segurança. Pior ainda: o GitHub faz com que tarefas comuns como aplicar labels em PRs ou deixar comentários automáticos só sejam possíveis com o gatilho perigoso (pull_request_target), então todo mundo acaba sendo empurrado para uma escolha instável. As funcionalidades do GitHub Actions têm uma estrutura propensa a erroCom o tempo, fico cada vez mais preocupado com ataques de supply chain. Não é só no sentido de “vou perder meu emprego por causa disso?” ou “surgiu um novo vetor de ataque em NixOS, CI/CD, Node etc.”, e sim uma preocupação mais filosófica. Quanto mais dependemos disso tudo, mais problemas inevitavelmente temos de lidar. Até coisas que deveriam ser tranquilas de usar já ficaram complicadas demais — VSCode, Emacs, Nix, Vim, Firefox, JS, Node, todos esses plugins e pacotes de dependência embolados. Então, por mais vergonhoso que seja, estou chegando cada vez mais perto da conclusão estranha de que só consigo sentir algum controle ou segurança usando papel e tecnologias mínimas, realmente simples. Sei que isso é irracional, mas estou cada vez mais saturado com essa complexidade. Já sinto até o limite da minha tolerância à complexidade
Se você olhar o aviso na man page do xargs, verá a frase “xargs cannot be used securely”. Mas esse problema de segurança é diferente do caso aplicado aqui. Neste caso, basta colocar
--no final para evitar o problemaA frase “xargs cannot be used securely” é frequentemente mal interpretada. Por exemplo, se você executar
cat "$HOME/changed_files" | xargs -r editorconfig-checker --, esse problema específico mencionado aqui pode ser evitado<div>{escapeHtml(value)}</div>em cada valor de HTML. Se for preciso aplicar o uso seguro manualmente em todo lugar, então a abordagem já está errada desde o início--, e a maioria dos usos de xargs é vulnerável a injeção de argumentos. Em outras palavras, executar comandos em geral é inerentemente perigoso. A culpa não é do xargs em si, e sim da realidade em que ferramentas são reutilizadas repetidamente em diferentes contextos de privilégioO artigo tem uma falha crítica muito mais abrangente: no código do PR, o arquivo OWNERS pode ser trocado por um symlink, expondo qualquer arquivo arbitrário, como o arquivo de credenciais do GitHub Actions. Como o git permite commitar softlinks, praticamente qualquer workflow fica sujeito a esse risco
pull_request_targetas credenciais são do repositório de destino, ou seja, do repositório para o qual o merge seria feito. Se rodar empull_request, as credenciais são do repositório de origem controlado pelo atacanteA única notícia “boa” é que OpenBSD e NetBSD ainda usam CVS para gerenciamento de pacotes, então não são afetados por essa vulnerabilidade. Não sei sobre o FreeBSD. Segurança, de certo modo, é obscuridade por falta de adoção. Dito isso, esses projetos também parecem estar pensando em migrar para git, e o OpenBSD provavelmente deve seguir com got(1)