From 3132b7a52b0c459d78f7faf38f807ebc7d514e14 Mon Sep 17 00:00:00 2001 From: Translator Date: Mon, 6 Jul 2026 15:30:25 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-ci-cd/github-security/abusing-github-act --- .../gh-actions-context-script-injections.md | 102 +++++++++++++----- 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/src/pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md b/src/pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md index c4180bf2b..2405454bb 100644 --- a/src/pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md +++ b/src/pentesting-ci-cd/github-security/abusing-github-actions/gh-actions-context-script-injections.md @@ -4,18 +4,18 @@ ## Zrozumienie ryzyka -GitHub Actions renderuje wyrażenia ${{ ... }} zanim krok się wykona. Wartość po renderowaniu jest wklejana do programu kroku (dla kroków z run:, skrypt shell). Jeśli interpolujesz niezaufane dane bezpośrednio w run:, atakujący kontroluje część programu shell i może wykonać dowolne polecenia. +GitHub Actions renderuje wyrażenia ${{ ... }} przed wykonaniem kroku. Renderowana wartość jest wklejana do programu kroku (dla kroków run, shell script). Jeśli interpolujesz niezaufane dane wejściowe bezpośrednio wewnątrz run:, atakujący kontroluje część programu shell i może wykonać arbitralne polecenia. -Dokumentacja: 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 and contexts/functions: https://docs.github.com/en/actions/learn-github-actions/contexts Kluczowe punkty: -- Renderowanie odbywa się przed wykonaniem. Skrypt z run: jest wygenerowany z wszystkimi rozwiązanymi wyrażeniami, a następnie wykonany przez shell. -- Wiele contexts zawiera pola kontrolowane przez użytkownika w zależności od zdarzenia wyzwalającego (issues, PRs, comments, discussions, forks, stars, etc.). Zobacz untrusted input reference: https://securitylab.github.com/resources/github-actions-untrusted-input/ -- Cytowanie w shellu wewnątrz run: nie jest niezawodną obroną, ponieważ wstrzyknięcie ma miejsce na etapie renderowania szablonu. Atakujący mogą wyłamać się z cytatów lub wstrzyknąć operatory za pomocą spreparowanego inputu. +- Renderowanie następuje przed wykonaniem. Skrypt run jest generowany po rozwiązaniu wszystkich wyrażeń, a następnie wykonywany przez shell. +- Wiele contexts zawiera pola kontrolowane przez użytkownika, zależnie od triggerującego event (issues, PRs, comments, discussions, forks, stars, etc.). Zobacz reference dla untrusted input: https://securitylab.github.com/resources/github-actions-untrusted-input/ +- Cytowanie shell wewnątrz run: nie jest niezawodną obroną, ponieważ injection zachodzi na etapie renderowania template. Atakujący może wyjść z cudzysłowów albo wstrzyknąć operatory przez przygotowane input. -## Wrażliwy wzorzec → RCE na runnerze +## Vulnerable pattern → RCE on runner -Wrażliwy workflow (wyzwalany, gdy ktoś otwiera nowe issue): +Vulnerable workflow (triggered when someone opens a new issue): ```yaml name: New Issue Created on: @@ -36,20 +36,56 @@ with: github_token: ${{ secrets.GITHUB_TOKEN }} labels: new ``` -Jeśli atakujący otworzy issue zatytułowane $(id), wyrenderowany krok staje się: +Jeśli attacker otworzy issue zatytułowany $(id), renderowany krok staje się: ```sh echo "New issue $(id) created" ``` -Substytucja polecenia uruchamia id na runnerze. Przykładowe wyjście: +Podstawienie komendy uruchamia id na runner. Przykładowy output: ``` New issue uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),100(users),999(systemd-journal) created ``` -Dlaczego cytowanie nie wystarczy: -- Wyrażenia są najpierw renderowane, a następnie uruchamiany jest otrzymany skrypt. Jeśli niezaufana wartość zawiera $(...), `;`, `"`/`'` lub znaki nowej linii, może zmienić strukturę programu pomimo twojego cytowania. +Dlaczego cudzysłowy nie ratują: +- Expressions są renderowane najpierw, a dopiero potem uruchamiany jest wynikowy script. Jeśli niezaufana wartość zawiera $(...), `;`, `"`/`'` albo nowe linie, może zmienić strukturę programu mimo cudzysłowów. -## Bezpieczny wzorzec (shell variables via env) +## Comment-state confusion: spoofed bot comments → shell injection -Poprawne zabezpieczenie: skopiuj niezaufane dane wejściowe do zmiennej środowiskowej, a następnie użyj natywnego rozwinięcia shella ($VAR) w skrypcie run. Nie osadzaj ponownie za pomocą ${{ ... }} wewnątrz polecenia. +Niebezpieczny wariant pojawia się, gdy workflow **wyszukuje komentarze, a potem traktuje zwrócony komentarz jako zaufany stan automatyzacji**. Na przykład `peter-evans/find-comment` może wyszukiwać według `body-includes` i ujawniać pasujący `comment-body` jako output kroku. Jeśli workflow **nie** ogranicza też `comment-author`, każdy użytkownik, który może komentować, może podszyć się pod tekst znacznika oczekiwany od bota. +```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:" +``` +Jeśli ten output zostanie później osadzony w shell syntax, workflow staje się exploitable, mimo że oryginalne źródło było „tylko komentarzem”: +```yaml +- run: | +if [ '${{ steps.fc.outputs.comment-body }}' = '' ]; then +echo "new issue needed" +fi +``` +Atakujący może opublikować komentarz, który jednocześnie: +- pasuje do wyszukanego ciągu znacznika, oraz +- zawiera treść łamiącą shell, taką jak `' ]; ; if [ 'x` + +Po tym, jak GitHub wyrenderuje `${{ ... }}`, Bash otrzymuje składnię kontrolowaną przez atakującego, a nie dane. To tworzy **dwustopniowy exploit**: +1. **Provenance confusion**: workflow myli komentarze atakującego ze stanem bota. +2. **Script injection**: zwrócony `comment-body` jest wklejany do `run:` i wykonywany. + +### TOCTOU race against bot comments + +Jeśli prawidłowy komentarz bota jest tworzony dopiero po jakimś wcześniejszym kroku, atakujący może go wyprzedzić, publikując wcześniej podrobiony komentarz. Jeśli akcja wyszukiwania zwróci komentarz atakującego, zanim istnieje prawdziwy komentarz bota (albo zanim zostanie wybrany), użytkownik publiczny o niskich uprawnieniach może zamienić workflow `issue_comment`/issue w wykonywanie kodu na uprzywilejowanym runnerze. + +### Safer patterns for comment-driven automation + +- Przy użyciu `find-comment`, wymagaj **zarówno treści, jak i provenance** (`comment-author`, tożsamości repository/App lub innego silnego powiązania). +- Nie używaj komentarzy jako stanu, jeśli label, artifact, pole issue lub zewnętrzny datastore mogą bezpieczniej przechowywać ten sam stan. +- Nigdy nie wklejaj `comment-body`, tytułów issue, labeli ani żadnego output workflow pochodzącego z nich bezpośrednio do `run:`. +- Jeśli musisz przetwarzać tekst komentarza, przekaż go przez `env:` albo plik i traktuj wyłącznie jako dane. + +## Safe pattern (shell variables via env) + +Prawidłowa mitigacja: skopiuj niezaufane dane wejściowe do zmiennej środowiskowej, a następnie użyj natywnego rozwinięcia shell ($VAR) w skrypcie `run`. Nie osadzaj ponownie za pomocą ${{ ... }} wewnątrz polecenia. ```yaml # safe jobs: @@ -63,31 +99,39 @@ run: | echo "New issue $TITLE created" ``` Uwagi: -- Unikaj używania ${{ env.TITLE }} inside run:. To ponownie wprowadza renderowanie szablonów do polecenia i powoduje to samo ryzyko wstrzyknięcia. -- Prefer passing untrusted inputs via env: mapping and reference them with $VAR in run:. +- Unikaj używania ${{ env.TITLE }} wewnątrz run:. To ponownie wprowadza template rendering do polecenia i niesie to samo ryzyko injection. +- Preferuj przekazywanie niezaufanych danych wejściowych przez mapowanie env: i odwołuj się do nich za pomocą $VAR w run:. -## Powierzchnie wyzwalane przez użytkowników (traktuj jako niezaufane) +## Reader-triggerable surfaces (traktuj jako untrusted) -Accounts with only read permission on public repositories can still trigger many events. Any field in contexts derived from these events must be considered attacker-controlled unless proven otherwise. Przykłady: +Konta z samym uprawnieniem read na public repositories nadal mogą uruchamiać wiele zdarzeń. Każde pole w contexts pochodzące z tych zdarzeń musi być traktowane jako kontrolowane przez atakującego, chyba że udowodniono inaczej. Przykłady: - issues, issue_comment -- discussion, discussion_comment (organizacje mogą ograniczać dyskusje) +- discussion, discussion_comment (orgs mogą ograniczać discussions) - pull_request, pull_request_review, pull_request_review_comment -- pull_request_target (niebezpieczne przy niewłaściwym użyciu — uruchamia się w kontekście base repo) -- fork (każdy może sforkować publiczne repozytoria) -- watch (gwiazdkowanie repozytorium) -- Indirectly via workflow_run/workflow_call chains +- pull_request_target (niebezpieczne, jeśli użyte niewłaściwie, działa w base repo context) +- fork (każdy może forkować public repos) +- watch (starring a repo) +- Pośrednio przez łańcuchy workflow_run/workflow_call -Które konkretne pola są kontrolowane przez atakującego zależy od zdarzenia. Zapoznaj się z przewodnikiem GitHub Security Lab po niezaufanych wejściach: https://securitylab.github.com/resources/github-actions-untrusted-input/ +Które konkretnie pola są kontrolowane przez atakującego, zależy od event. Sprawdź przewodnik GitHub Security Lab dotyczący untrusted input: https://securitylab.github.com/resources/github-actions-untrusted-input/ -## Praktyczne wskazówki +## Local validation without touching the target repo -- Minimalizuj użycie wyrażeń wewnątrz run:. Preferuj mapowanie env: i odniesienia przez $VAR. -- Jeśli musisz przekształcić dane wejściowe, rób to w shellu używając bezpiecznych narzędzi (printf %q, jq -r itp.), zaczynając nadal od zmiennej shellowej. -- Zachowaj szczególną ostrożność przy interpolowaniu branch names, PR titles, usernames, labels, discussion titles oraz PR head refs do skryptów, opcji wiersza poleceń lub ścieżek plików. -- Dla reusable workflows i composite actions stosuj ten sam wzorzec: mapuj do env, a następnie odwołuj się przez $VAR. +Możesz bezpiecznie odtworzyć wiele GitHub Actions script injections za pomocą [`act`](https://github.com/nektos/act): wygeneruj syntetyczny event JSON, uruchom podatny workflow lokalnie i zastąp wyjście zewnętrznej akcji kontrolowaną wartością (na przykład zamockowanym `comment-body`). Jest to przydatne do debugowania struktury payload, sprawdzenia, czy wstrzyknięty tekst nadal pozostawia poprawną składnię Bash, oraz potwierdzenia nieszkodliwego canary exfiltration przed jakimkolwiek testem na żywo. -## Referencje +## Practical tips +- Ogranicz użycie expressions wewnątrz run:. Preferuj env: mapping + $VAR. +- Jeśli musisz przekształcić input, zrób to w shellu przy użyciu bezpiecznych narzędzi (printf %q, jq -r, itd.), nadal startując od shell variable. +- Zachowaj szczególną ostrożność przy interpolowaniu branch names, PR titles, usernames, labels, discussion titles i PR head refs do scripts, command-line flags lub file paths. +- Dla reusable workflows i composite actions stosuj ten sam wzorzec: mapuj do env, a potem odwołuj się do $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)