Gh Actions - Context Script Injections

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

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.

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

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.

Modèle vulnérable → RCE on runner

Workflow vulnérable (déclenché lorsqu’une personne ouvre une nouvelle issue):

name: New Issue Created
on:
issues:
types: [opened]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: New issue
run: |
echo "New issue ${{ github.event.issue.title }} created"
- name: Add "new" label to issue
uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: new

Si un attaquant ouvre un issue avec le titre $(id), l’étape rendue devient :

echo "New issue $(id) created"

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 :

  • 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.

Modèle sûr (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 d’exécution. Ne la réinjectez pas avec ${{ … }} dans la commande.

# safe
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: New issue
env:
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:.

Surfaces déclenchables par un lecteur (à traiter comme non fiables)

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:

  • issues, issue_comment
  • discussion, discussion_comment (les organisations 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)
  • 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/

Conseils pratiques

  • 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.

Références

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