← All field notes
cloud iamgithub actions oidcfor developers

OIDC trust gone wide: when a fork can assume your deploy role

A wildcard in a cloud role's OIDC trust condition lets any fork's workflow assume the deploy role. There is no key to rotate. You fix the trust condition itself.

OIDC federation lets a GitHub Actions workflow assume a cloud role with no stored key. The trust policy’s subject condition is the entire gate. When that condition is too loose, anyone can walk through it.

How the attack works

A cloud IAM role trusts the GitHub OIDC issuer for deployments, but its subject condition uses a wildcard or omits the repo and branch claim. An attacker triggers a workflow from a fork, receives a short lived OIDC token, and exchanges it for the deploy role’s credentials. The cloud federation log records the assume role event with a subject claim from a repo or ref that is not the trusted source. Operating as the pipeline, the session changes production infrastructure, reads deployment secrets, and stages those secrets out to an external endpoint. In ATT&CK terms this is T1199, Trusted Relationship, paired with T1078.004, Valid Accounts: Cloud Accounts.

Why it works

In OIDC federation there is no static access key and no human in the loop. The subject condition is the only thing standing between a token and the role. An over broad condition trusts forks and other repos that should never deploy.

How to fix it

The scenario teaches the counterintuitive containment: there is no key to rotate. You tighten the trust condition itself so the subject matches the exact repo and ref or environment, with the correct audience and issuer, and the fork’s token fails the check. Then rotate every secret the bad session read, because those are now exposed and unlock downstream systems. The class wide eradication is to audit every OIDC federated role for wildcard or missing subject conditions and pin each one. The cloud control plane audit log, filtered to the federated session and window, shows exactly which calls were made and secrets read.

Practice it

We built this as a GraphLattice Range scenario so developers can rehearse fixing the trust condition, not chasing keys that do not exist.