GCP - Federation Abuse

Reading time: 4 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

OIDC - Github Actions Abuse

GCP

In order to give access to the Github Actions from a Github repo to a GCP service account the following steps are needed:

  • Create the Service Account to access from github actions with the desired permissions:
bash
projectId=FIXME
gcloud config set project $projectId

# Create the Service Account
gcloud iam service-accounts create "github-demo-sa"
saId="github-demo-sa@${projectId}.iam.gserviceaccount.com"

# Enable the IAM Credentials API
gcloud services enable iamcredentials.googleapis.com

# Give permissions to SA

gcloud projects add-iam-policy-binding $projectId \
    --member="serviceAccount:$saId" \
    --role="roles/iam.securityReviewer"
  • Generate a new workload identity pool:
bash
# Create a Workload Identity Pool
poolName=wi-pool

gcloud iam workload-identity-pools create $poolName \
  --location global \
  --display-name $poolName

poolId=$(gcloud iam workload-identity-pools describe $poolName \
  --location global \
  --format='get(name)')
  • Generate a new workload identity pool OIDC provider that trusts github actions (by org/repo name in this scenario):
bash
attributeMappingScope=repository # could be sub (GitHub repository and branch) or repository_owner (GitHub organization)

gcloud iam workload-identity-pools providers create-oidc $poolName \
  --location global \
  --workload-identity-pool $poolName \
  --display-name $poolName \
  --attribute-mapping "google.subject=assertion.${attributeMappingScope},attribute.actor=assertion.actor,attribute.aud=assertion.aud,attribute.repository=assertion.repository" \
  --issuer-uri "https://token.actions.githubusercontent.com"

providerId=$(gcloud iam workload-identity-pools providers describe $poolName \
  --location global \
  --workload-identity-pool $poolName \
  --format='get(name)')
  • Finally, allow the principal from the provider to use a service principal:
bash
gitHubRepoName="repo-org/repo-name"
gcloud iam service-accounts add-iam-policy-binding $saId \
  --role "roles/iam.workloadIdentityUser" \
  --member "principalSet://iam.googleapis.com/${poolId}/attribute.${attributeMappingScope}/${gitHubRepoName}"

warning

Note how in the previous member we are specifying the org-name/repo-name as conditions to be able to access the service account (other params that makes it more restrictive like the branch could also be used).

However it's also possible to allow all github to access the service account creating a provider such the following using a wildcard:

# Create a Workload Identity Pool
poolName=wi-pool2

gcloud iam workload-identity-pools create $poolName \
  --location global \
  --display-name $poolName

poolId=$(gcloud iam workload-identity-pools describe $poolName \
  --location global \
  --format='get(name)')

gcloud iam workload-identity-pools providers create-oidc $poolName \
  --project="${projectId}" \
  --location="global" \
  --workload-identity-pool="$poolName" \
  --display-name="Demo provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.aud=assertion.aud" \
  --issuer-uri="https://token.actions.githubusercontent.com"

providerId=$(gcloud iam workload-identity-pools providers describe $poolName \
  --location global \
  --workload-identity-pool $poolName \
  --format='get(name)')

# CHECK THE WILDCARD
gcloud iam service-accounts add-iam-policy-binding "${saId}" \
  --project="${projectId}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/${poolId}/*"

warning

In this case anyone could access the service account from github actions, so it's important always to check how the member is defined.
It should be always something like this:

attribute.{custom_attribute}:principalSet://iam.googleapis.com/projects/{project}/locations/{location}/workloadIdentityPools/{pool}/attribute.{custom_attribute}/{value}

Github

Remember to change ${providerId} and ${saId} for their respective values:

yaml
name: Check GCP action
on:
  workflow_dispatch:
  pull_request:
    branches:
      - main

permissions:
  id-token: write

jobs:
  Get_OIDC_ID_token:
    runs-on: ubuntu-latest
    steps:
      - id: "auth"
        name: "Authenticate to GCP"
        uses: "google-github-actions/auth@v2.1.3"
        with:
          create_credentials_file: "true"
          workload_identity_provider: "${providerId}" # In the providerId, the numerical project ID (12 digit number) should be used
          service_account: "${saId}" # instead of the alphanumeric project ID. ex:
          activate_credentials_file: true # projects/123123123123/locations/global/workloadIdentityPools/iam-lab-7-gh-pool/providers/iam-lab-7-gh-pool-oidc-provider'
      - id: "gcloud"
        name: "gcloud"
        run: |-
          gcloud config set project <project-id>
          gcloud config set account '${saId}'
          gcloud auth login --brief --cred-file="${{ steps.auth.outputs.credentials_file_path }}"
          gcloud auth list
          gcloud projects list
          gcloud secrets list

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks