Envenenamento de cache do GitHub

Todas as mensagens

Você sabe o que acontece nos bastidores do seu CI? Sem um conhecimento profundo, você poderá ficar vulnerável a ataques inovadores à cadeia de suprimentos. Este artigo descreve esse tipo de ataque.

O cache é usado para acelerar processos. Em vez de construir ou baixar repetidamente um pacote, ele mantém artefatos de forma simples e automática para reutilização. No entanto, os caches podem estar envenenados. Por exemplo, uma ferramenta maliciosa usada em um fluxo de trabalho de teste pode envenenar seu cache. Posteriormente, outro fluxo de trabalho que use o mesmo cache poderá ser afetado. Se este fluxo de trabalho tiver privilégios mais elevados, este é na verdade um método de dinamizar um ataque. Nesta postagem, relatamos um ataque experimental em um pipeline de CI do GitHub, mas existe uma vulnerabilidade lógica semelhante em outros produtos de CI.

O ataque acontece da seguinte forma: 

  1. Um invasor publica uma ferramenta maliciosa ou uma ação do Github que é detectada por um fluxo de trabalho desavisado no Github.
  2. O fluxo de trabalho é projetado com um cache
  3. A carga maliciosa modifica o cache para incluir dados maliciosos.
  4. Outros fluxos de trabalho que invocam esse cache a partir deste ponto poderão ser afetados.

Em resposta à nossa divulgação, o GitHub disse que não tem planos para fortalecer o recurso de cache contra esse tipo de ataque. 

Propomos a mitigação assinando criptograficamente o valor hash do conteúdo do cache e verificando a assinatura antes do uso. Para saber mais sobre o ataque e as mitigações, continue lendo.

Ataque de envenenamento de cache

Cache GitHub

frequentemente reutilizam as mesmas saídas ou dependências baixadas de uma execução para outra (por exemplo, os pacotes baixados por ferramentas de gerenciamento de pacotes e dependências, como Maven, Gradle, npm e Yarn. Esses pacotes geralmente são mantidos em um cache local de dependências baixadas).

Para otimizar as execuções de CI, o GitHub concede acesso a um recurso de cache que pode ser usado no pipeline por meio de uma API. As entradas no cache são uma combinação de chave-valor, onde as chaves são baseadas em strings e os valores são arquivos ou diretórios que você gostaria de armazenar em cache.

Com o ação/cache A ação do Git em qualquer lugar do CI será executada em duas etapas: uma etapa ocorrerá durante o corrida processo quando for chamado e o outro ocorrerá após de gestão de documentos (se a ação de execução retornou uma falta de cache).

  • Executar ação – é usado para pesquisar e recuperar o cache. A pesquisa é feita usando a chave de cache, e o resultado é um cache-hit (sucesso, dados encontrados no cache) ou cache-miss. Se encontrados, os arquivos e diretórios serão recuperados do cache para uso ativo. Se o resultado for falta de cache, os arquivos e diretórios desejados serão baixados como se fosse a primeira vez que foram chamados.
  • Ação pós-fluxo de trabalho – usado para salvar o cache. Se o resultado da chamada de cache na ação run retornar cache-miss, esta ação salvará o estado atual dos diretórios que queremos armazenar em cache com a chave fornecida. Esta ação acontece automaticamente e não precisa ser chamada explicitamente.

Permissões de cache do GitHub

As restrições de acesso fornecem isolamento de cache e segurança criando um limite lógico entre diferentes filiais (por exemplo: um cache criado para a filial Recurso-A [com a base main] não seria acessível a uma solicitação pull para o branch Recurso-B [com a base principal]).

A ação de cache primeiro pesquisa ocorrências de cache em busca de uma chave e restaura as chaves na ramificação que contém a chave. execução do fluxo de trabalho. Se não houver ocorrências na ramificação atual, a ação de cache procura a chave e restaura as chaves na ramificação pai e nas ramificações upstream.

O acesso a um cache tem escopo definido por branch (atual e pai), o que significa que o acesso é fornecido a todos fluxos de trabalho em é executado do referido ramo.

Outra observação importante é que o GitHub não permite modificações depois que as entradas são enviadas – as entradas de cache são registros somente leitura.

Escopo de IC do GitHub

GitHub escopos permitem especificar exatamente que tipo de acesso é necessário para diversas tarefas. O CI do GitHub tem escopo definido de diversas maneiras, cada uma com seu próprio conjunto de valores e recursos:

  • Máquina Virtual (VM) para cada trabalho
  • Permissões de trabalho
  • Escopos de fluxo de trabalho
  • Execuções de fluxo de trabalho
  • Ramificações do Git
  • Tokens de identidade de fluxo de trabalho
  • e outros

O escopo do cache do GitHub foi definido de uma forma que pode quebrar algumas das outras restrições de escopo (ex: o cache do GitHub pode afetar vários fluxos de trabalho).

O ataque

Usamos um IC de exemplo que incluía dois fluxos de trabalho. Este exemplo mostra como um ataque pode passar de um fluxo de trabalho de baixa permissão para um de alta permissão.

  • Teste de unidade fluxo de trabalho executando ferramentas de teste de unidade e cobertura de código. Presumimos que uma das ferramentas é maliciosa ou vulnerável à execução remota de código. O fluxo de trabalho precisa usar o ação/cache Ação Git. Qualquer fluxo de trabalho pode acessar o cache.
  • Solte o fluxo de trabalho cria e libera o artefato do aplicativo. Este fluxo de trabalho usa um cache para otimizar usando as dependências do Golang.

A teste de unidade o fluxo de trabalho usa uma ação maliciosa que adiciona uma entrada de cache com conteúdo malicioso, alterando uma biblioteca de log Golang (go.uber.org/zap@v1) para adicionar a sequência 'BAD library' à descrição do artefato do aplicativo.

Em seguida, o liberar o fluxo de trabalho usa essa entrada de cache envenenada. Como resultado, o código malicioso é injetado no binário e na imagem Golang construídos. O cache permanece envenenado até que a chave de entrada seja descartada (geralmente acionada por atualizações de dependência). O mesmo cache envenenado afetará qualquer outro de gestão de documentos, corrida e ramo filho usando a mesma chave de cache.

No teste que realizamos, conseguimos injetar a string ‘BAD library’ na descrição da imagem:

Biblioteca RUIM

Isso estava na versão 0.4.1. Em seguida, atualizamos a tag e reconstruímos a imagem diversas vezes, e observamos que 'Biblioteca ruim' permaneceu na descrição.

Divulgação do GitHub

A resposta do Github à nossa divulgação foi que a ação do Git,  ação/cache se comporta conforme o esperado e eles não têm planos de restringir o escopo do cache.
Embora a equipe do GitHub não considere esse comportamento problemático, aconselhamos os profissionais de DevSecOps a serem cautelosos com esse vetor de ataque.

Mitigações

  1. Não use caches na versão ou em fluxos de trabalho importantes.
  2. Execute seus fluxos de trabalho sequencialmente, com o fluxo de trabalho confiável sendo executado primeiro para garantir que seu cache seja criado em um fluxo de trabalho confiável.
  3. Forneça suas dependências – A venda no GoLang é um método para garantir que todos os pacotes de terceiros usados ​​no projeto Go sejam consistentes para todos que desenvolvem para aquele aplicativo. Dessa forma, as dependências armazenadas em cache permanecerão válidas para todas as ramificações do projeto. Outros idiomas podem não suportar este método.
  4. Assine o valor do cache criptograficamente e verifique a assinatura antes do uso.
    O Scribe mitiga esses ataques assinando granularmente qualquer objeto ou diretório em um fluxo de trabalho, como um cache. Antes do lançamento, você pode usar o Scribe para validar que apenas um cache gerado por um fluxo de trabalho confiável foi usado para construir o lançamento.

Resumo 

Nesta postagem, descrevemos um ataque de envenenamento de cache em fluxos de trabalho de CI, que está oculto aos olhos da equipe DevSecOps, e discutimos as mitigações.

Este conteúdo é oferecido a você pela Scribe Security, um fornecedor líder de soluções de segurança de cadeia de suprimentos de software ponta a ponta – fornecendo segurança de última geração para artefatos de código e processos de desenvolvimento e entrega de código em todas as cadeias de suprimentos de software. Saiba mais.