mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['', 'src/pentesting-ci-cd/github-security/abusing-github-act
This commit is contained in:
+73
-30
@@ -4,18 +4,18 @@
|
||||
|
||||
## Comprendre le risque
|
||||
|
||||
GitHub Actions évalue les expressions ${{ ... }} avant que l'étape n'exécute. La valeur évaluée est insérée dans le programme de l'étape (pour les étapes run, un script shell). Si vous interpolez des entrées non fiables directement dans run:, l'attaquant contrôle une partie du script shell et peut exécuter des commandes arbitraires.
|
||||
GitHub Actions rend les expressions ${{ ... }} avant l’exécution de l’étape. La valeur rendue est collée dans le programme de l’étape (pour les steps run, un shell script). Si vous interpolez directement une entrée non fiable dans run:, l’attaquant contrôle une partie du programme shell et peut exécuter des commandes arbitraires.
|
||||
|
||||
Docs: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions and contexts/functions: https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||
Docs: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions et contexts/functions: https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||
|
||||
Points clés :
|
||||
- L'évaluation a lieu avant l'exécution. Le script run est généré avec toutes les expressions résolues, puis exécuté par le shell.
|
||||
- De nombreux contexts contiennent des champs contrôlés par l'utilisateur selon l'événement déclencheur (issues, PRs, comments, discussions, forks, stars, etc.). Voir la référence sur les entrées non fiables : https://securitylab.github.com/resources/github-actions-untrusted-input/
|
||||
- Le quoting du shell à l'intérieur de run: n'est pas une défense fiable, car l'injection se produit lors de l'étape de rendu du template. Les attaquants peuvent sortir des guillemets ou injecter des opérateurs via des entrées spécialement conçues.
|
||||
Points clés:
|
||||
- Le rendu se produit avant l’exécution. Le run script est généré avec toutes les expressions résolues, puis exécuté par le shell.
|
||||
- Beaucoup de contexts contiennent des champs contrôlés par l’utilisateur selon l’événement déclencheur (issues, PRs, comments, discussions, forks, stars, etc.). Voir la référence sur les untrusted input: https://securitylab.github.com/resources/github-actions-untrusted-input/
|
||||
- Le shell quoting dans run: n’est pas une défense fiable, car l’injection se produit à l’étape de rendu du template. Les attaquants peuvent sortir des quotes ou injecter des opérateurs via une entrée forgée.
|
||||
|
||||
## Modèle vulnérable → RCE on runner
|
||||
## Schéma vulnérable → RCE sur runner
|
||||
|
||||
Workflow vulnérable (déclenché lorsqu'une personne ouvre une nouvelle issue):
|
||||
Workflow vulnérable (déclenché quand quelqu’un ouvre une nouvelle issue):
|
||||
```yaml
|
||||
name: New Issue Created
|
||||
on:
|
||||
@@ -36,21 +36,56 @@ with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: new
|
||||
```
|
||||
Si un attaquant ouvre un issue avec le titre $(id), l'étape rendue devient :
|
||||
Si un attacker ouvre un issue intitulé $(id), l’étape rendue devient :
|
||||
```sh
|
||||
echo "New issue $(id) created"
|
||||
```
|
||||
La substitution de commande exécute id sur le runner. Exemple de sortie :
|
||||
La substitution de commande exécute `id` sur le runner. Exemple de sortie :
|
||||
```
|
||||
New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created
|
||||
```
|
||||
Pourquoi les guillemets ne suffisent pas :
|
||||
Pourquoi le quoting ne vous protège pas :
|
||||
- Les expressions sont rendues en premier, puis le script النات النات النات النات النات النات النات النات النات النات النات النات النات النات النات النات النات النات النات puis exécute. Si la valeur non fiable contient $(...), `;`, `"`/`'`, ou des retours à la ligne, elle peut modifier la structure du programme malgré votre quoting.
|
||||
|
||||
- Les expressions sont évaluées en premier, puis le script résultant s'exécute. Si la valeur non fiable contient $(...), `;`, `"`/`'`, ou des retours à la ligne, elle peut modifier la structure du programme malgré vos guillemets.
|
||||
## Comment-state confusion: spoofed bot comments → shell injection
|
||||
|
||||
## Modèle sûr (shell variables via env)
|
||||
Une variante dangereuse apparaît lorsqu’un workflow **recherche des comments puis traite ensuite le comment retourné comme un état d’automatisation de confiance**. Par exemple, `peter-evans/find-comment` peut rechercher via `body-includes` et exposer le `comment-body` correspondant comme sortie d’une étape. Si le workflow ne restreint **pas** aussi `comment-author`, tout utilisateur capable de commenter peut usurper le texte marqueur attendu d’un bot.
|
||||
```yaml
|
||||
- uses: peter-evans/find-comment@v4
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body-includes: "Opened a new issue in org/repo:"
|
||||
```
|
||||
Si ce output est ensuite intégré dans une syntaxe shell, le workflow devient exploitable même si la source d’origine était « juste un commentaire » :
|
||||
```yaml
|
||||
- run: |
|
||||
if [ '${{ steps.fc.outputs.comment-body }}' = '' ]; then
|
||||
echo "new issue needed"
|
||||
fi
|
||||
```
|
||||
Un attaquant peut publier un commentaire qui :
|
||||
- correspond à la chaîne marqueur recherchée, et
|
||||
- contient du contenu capable de casser le shell, comme `' ]; <cmd>; if [ 'x`
|
||||
|
||||
Mitigation correcte : copiez l'entrée non fiable dans une variable d'environnement, puis utilisez l'expansion native du shell ($VAR) dans le script d'exécution. Ne la réinjectez pas avec ${{ ... }} dans la commande.
|
||||
Après que GitHub a rendu `${{ ... }}`, Bash reçoit une syntaxe contrôlée par l’attaquant, pas des données. Cela crée un **exploit en deux étapes** :
|
||||
1. **Confusion de provenance** : le workflow prend les commentaires de l’attaquant pour l’état du bot.
|
||||
2. **Script injection** : le `comment-body` renvoyé est collé dans `run:` et exécuté.
|
||||
|
||||
### Course TOCTOU contre les commentaires du bot
|
||||
|
||||
Si le commentaire légitime du bot n’est créé qu’après une étape précédente, un attaquant peut le devancer en publiant d’abord le faux commentaire. Si l’action de recherche renvoie le commentaire de l’attaquant avant que le vrai commentaire du bot n’existe (ou avant qu’il ne soit sélectionné), un commentateur public à faible privilège peut transformer un workflow `issue_comment`/issue en exécution privilégiée sur le runner.
|
||||
|
||||
### Schémas plus sûrs pour l’automatisation pilotée par commentaires
|
||||
|
||||
- Lors de l’utilisation de `find-comment`, exigez **à la fois le contenu et la provenance** (`comment-author`, l’identité repository/App, ou un autre lien fort).
|
||||
- N’utilisez pas les commentaires comme état si un label, un artifact, un champ d’issue ou un datastore externe peut conserver le même état plus sûrement.
|
||||
- Ne collez jamais directement `comment-body`, les titres d’issue, les labels ou toute sortie de workflow dérivée d’eux dans `run:`.
|
||||
- Si vous devez consommer du texte de commentaire, passez-le via `env:` ou un fichier et traitez-le uniquement comme des données.
|
||||
|
||||
## Safe pattern (shell variables via env)
|
||||
|
||||
Mitigation correcte : copiez l’entrée non fiable dans une variable d’environnement, puis utilisez l’expansion native du shell ($VAR) dans le script `run`. Ne ré-intégrez pas avec ${{ ... }} à l’intérieur de la commande.
|
||||
```yaml
|
||||
# safe
|
||||
jobs:
|
||||
@@ -63,32 +98,40 @@ TITLE: ${{ github.event.issue.title }}
|
||||
run: |
|
||||
echo "New issue $TITLE created"
|
||||
```
|
||||
Remarques:
|
||||
- Évitez d'utiliser ${{ env.TITLE }} dans run:. Cela réintroduit le rendu du template dans la commande et entraîne le même risque d'injection.
|
||||
- Privilégiez le passage d'entrées non fiables via le mapping env: et référencez-les avec $VAR dans run:.
|
||||
Notes:
|
||||
- Évitez d’utiliser ${{ env.TITLE }} dans run:. Cela réintroduit le template rendering dans la commande et ramène le même risque d’injection.
|
||||
- Préférez passer les inputs non fiables via le mapping env: et les référencer avec $VAR dans run:.
|
||||
|
||||
## Surfaces déclenchables par un lecteur (à traiter comme non fiables)
|
||||
## Reader-triggerable surfaces (treat as untrusted)
|
||||
|
||||
Les comptes avec seulement la permission de lecture sur les dépôts publics peuvent quand même déclencher de nombreux événements. Tout champ des contextes dérivés de ces événements doit être considéré comme contrôlé par un attaquant sauf preuve du contraire. Exemples:
|
||||
Les comptes avec seulement le droit de lecture sur les public repositories peuvent toujours déclencher de nombreux événements. Tout champ dans les contexts dérivés de ces événements doit être considéré comme contrôlé par l’attaquant, sauf preuve contraire. Exemples :
|
||||
- issues, issue_comment
|
||||
- discussion, discussion_comment (les organisations peuvent restreindre les discussions)
|
||||
- discussion, discussion_comment (les orgs peuvent restreindre les discussions)
|
||||
- pull_request, pull_request_review, pull_request_review_comment
|
||||
- pull_request_target (dangereux s'il est mal utilisé, s'exécute dans le contexte du dépôt de base)
|
||||
- fork (n'importe qui peut forker des dépôts publics)
|
||||
- watch (le fait d'ajouter une étoile à un dépôt)
|
||||
- pull_request_target (dangereux en cas de mauvaise utilisation, s’exécute dans le contexte du base repo)
|
||||
- fork (n’importe qui peut fork des public repos)
|
||||
- watch (starring un repo)
|
||||
- Indirectement via des chaînes workflow_run/workflow_call
|
||||
|
||||
Les champs spécifiques contrôlés par un attaquant dépendent de l'événement. Consultez le guide de GitHub Security Lab sur les entrées non fiables : https://securitylab.github.com/resources/github-actions-untrusted-input/
|
||||
Les champs précis contrôlés par l’attaquant dépendent de l’événement. Consultez le guide GitHub Security Lab sur l’untrusted input : https://securitylab.github.com/resources/github-actions-untrusted-input/
|
||||
|
||||
## Conseils pratiques
|
||||
## Local validation without touching the target repo
|
||||
|
||||
- Minimisez l'utilisation d'expressions dans run:. Privilégiez le mapping env: + $VAR.
|
||||
- Si vous devez transformer une entrée, faites-le dans le shell en utilisant des outils sûrs (printf %q, jq -r, etc.), en partant toujours d'une variable shell.
|
||||
- Soyez particulièrement prudent lors de l'interpolation de branch names, PR titles, usernames, labels, discussion titles et PR head refs dans des scripts, des options en ligne de commande ou des chemins de fichiers.
|
||||
- Pour les reusable workflows et composite actions, appliquez le même schéma : mappez vers env puis référencez $VAR.
|
||||
Vous pouvez reproduire en toute sécurité de nombreuses injections de script GitHub Actions avec [`act`](https://github.com/nektos/act) : générez un JSON d’événement synthétique, exécutez localement le workflow vulnérable, et remplacez la sortie de l’external action par une valeur contrôlée (par exemple un `comment-body` mocké). C’est utile pour déboguer la structure du payload, vérifier si le texte injecté laisse encore une syntaxe Bash valide, et confirmer une exfiltration canary inoffensive avant tout test en conditions réelles.
|
||||
|
||||
## Références
|
||||
## Practical tips
|
||||
|
||||
- Réduisez au minimum l’usage des expressions dans run:. Préférez le mapping env: + $VAR.
|
||||
- Si vous devez transformer une input, faites-le dans le shell avec des outils sûrs (printf %q, jq -r, etc.), en partant toujours d’une shell variable.
|
||||
- Soyez particulièrement prudent quand vous interpolez des branch names, des PR titles, des usernames, des labels, des discussion titles et des PR head refs dans des scripts, des command-line flags ou des file paths.
|
||||
- Pour les reusable workflows et les composite actions, appliquez le même pattern : mappez vers env puis référencez $VAR.
|
||||
|
||||
## References
|
||||
|
||||
- [Find Comment, Get Shell: Command Injection in dbt’s GitHub Actions](https://landh.tech/blog/20260701-find-comment-get-shell)
|
||||
- [peter-evans/find-comment](https://github.com/peter-evans/find-comment)
|
||||
- [GHSL-2023-109: GitHub Actions command injection in a TDesign Vue Next workflow](https://securitylab.github.com/advisories/GHSL-2023-109_TDesign_Vue_Next/)
|
||||
- [nektos/act](https://github.com/nektos/act)
|
||||
- [GitHub Actions: A Cloudy Day for Security - Part 1](https://binarysecurity.no/posts/2025/08/securing-gh-actions-part1)
|
||||
- [GitHub workflow syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions)
|
||||
- [Contexts and expression syntax](https://docs.github.com/en/actions/learn-github-actions/contexts)
|
||||
|
||||
Reference in New Issue
Block a user