Gh Actions - Context Script Injections

Reading time: 5 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Verständnis des Risikos

GitHub Actions wertet Ausdrücke ${{ ... }} aus, bevor der Schritt ausgeführt wird. Der ausgewertete Wert wird in das Programm des Schritts eingefügt (bei run-Schritten ein Shell-Skript). Wenn untrusted input direkt in run: interpoliert wird, kontrolliert der Angreifer einen Teil des Shell-Programms und kann beliebige Befehle ausführen.

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

Wichtige Punkte:

  • Das Rendern erfolgt vor der Ausführung. Das run-Skript wird mit allen aufgelösten Ausdrücken generiert und anschließend von der Shell ausgeführt.
  • Viele contexts enthalten vom Benutzer kontrollierte Felder, abhängig vom auslösenden Ereignis (issues, PRs, comments, discussions, forks, stars, etc.). Siehe die untrusted input reference: https://securitylab.github.com/resources/github-actions-untrusted-input/
  • Shell quoting innerhalb von run: ist keine verlässliche Verteidigung, da die injection in der Phase des Template-Renderings auftritt. Angreifer können aus Anführungszeichen ausbrechen oder Operatoren durch manipulierte Eingaben injizieren.

Verwundbares Muster → RCE auf dem Runner

Verwundbarer Workflow (ausgelöst, wenn jemand ein neues issue öffnet):

yaml
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

Wenn ein Angreifer ein Issue mit dem Titel $(id) eröffnet, wird der gerenderte Schritt:

sh
echo "New issue $(id) created"

Die Kommando-Substitution führt id auf dem Runner aus. Beispielausgabe:

New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created

Warum Quoting Sie nicht schützt:

  • Ausdrücke werden zuerst gerendert, und das resultierende Skript wird danach ausgeführt. Wenn der unsichere Wert $(...), ;, "/' oder Zeilenumbrüche enthält, kann er die Programmstruktur trotz Ihres Quoting verändern.

Sicheres Muster (shell variables via env)

Korrekte Gegenmaßnahme: Kopieren Sie die unsichere Eingabe in eine Umgebungsvariable und verwenden Sie dann die native Shell-Erweiterung ($VAR) im run-Skript. Binden Sie nicht erneut mit ${{ ... }} innerhalb des Befehls ein.

yaml
# safe
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: New issue
env:
TITLE: ${{ github.event.issue.title }}
run: |
echo "New issue $TITLE created"

Hinweise:

  • Avoid using ${{ env.TITLE }} inside run:. That reintroduces template rendering back into the command and brings the same injection risk.
  • Prefer passing untrusted inputs via env: mapping and reference them with $VAR in run:.

Vom Leser auslösbare Angriffsflächen (als nicht vertrauenswürdig behandeln)

Accounts mit nur Lesezugriff auf public repositories können trotzdem viele Events auslösen. Jedes Feld in Contexts, die aus diesen Events abgeleitet werden, muss als von einem Angreifer kontrolliert betrachtet werden, sofern nicht das Gegenteil bewiesen ist. Beispiele:

  • issues, issue_comment
  • discussion, discussion_comment (orgs can restrict discussions)
  • pull_request, pull_request_review, pull_request_review_comment
  • pull_request_target (dangerous if misused, runs in base repo context)
  • fork (anyone can fork public repos)
  • watch (starring a repo)
  • Indirectly via workflow_run/workflow_call chains

Welche konkreten Felder angreifer-kontrolliert sind, ist eventspezifisch. Siehe GitHub Security Lab’s untrusted input guide: https://securitylab.github.com/resources/github-actions-untrusted-input/

Praktische Tipps

  • Minimize use of expressions inside run:. Prefer env: mapping + $VAR.
  • If you must transform input, do it in the shell using safe tools (printf %q, jq -r, etc.), still starting from a shell variable.
  • Sei besonders vorsichtig, wenn du branch names, PR titles, usernames, labels, discussion titles und PR head refs in scripts, command-line flags oder file paths interpolierst.
  • Für reusable workflows und composite actions gilt dasselbe Muster: map to env then reference $VAR.

References

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks