Translated ['', 'src/pentesting-ci-cd/github-security/abusing-github-act

This commit is contained in:
Translator
2026-07-06 15:31:33 +00:00
parent 8b26250ef1
commit d062399865
@@ -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 lexé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:, lattaquant 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 lexé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 lutilisateur 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: nest pas une défense fiable, car linjection 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 quelquun 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 lorsquun workflow **recherche des comments puis traite ensuite le comment retourné comme un état dautomatisation de confiance**. Par exemple, `peter-evans/find-comment` peut rechercher via `body-includes` et exposer le `comment-body` correspondant comme sortie dune étape. Si le workflow ne restreint **pas** aussi `comment-author`, tout utilisateur capable de commenter peut usurper le texte marqueur attendu dun 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 dorigine é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 lattaquant, pas des données. Cela ce un **exploit en deux étapes** :
1. **Confusion de provenance** : le workflow prend les commentaires de lattaquant 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 nest créé quaprès une étape précédente, un attaquant peut le devancer en publiant dabord le faux commentaire. Si laction de recherche renvoie le commentaire de lattaquant avant que le vrai commentaire du bot nexiste (ou avant quil 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 lautomatisation pilotée par commentaires
- Lors de lutilisation de `find-comment`, exigez **à la fois le contenu et la provenance** (`comment-author`, lidentité repository/App, ou un autre lien fort).
- Nutilisez pas les commentaires comme état si un label, un artifact, un champ dissue ou un datastore externe peut conserver le même état plus sûrement.
- Ne collez jamais directement `comment-body`, les titres dissue, les labels ou toute sortie de workflow dérivée deux 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 lentrée non fiable dans une variable denvironnement, puis utilisez lexpansion native du shell ($VAR) dans le script `run`. Ne ré-intégrez pas avec ${{ ... }} à linté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 dutiliser ${{ env.TITLE }} dans run:. Cela réintroduit le template rendering dans la commande et ramène le même risque dinjection.
- 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 lattaquant, 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, sexécute dans le contexte du base repo)
- fork (nimporte 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 lattaquant dépendent de lévénement. Consultez le guide GitHub Security Lab sur luntrusted 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 lexternal action par une valeur contrôlée (par exemple un `comment-body` mocké). Cest 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 lusage 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 dune 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 dbts 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)