back
vulnerabuild_ / build
attack paths
1 components · 2 vulnerabilities
infrastructure-as-code
format
cloud
region
# ================================================================
# Attack path: GitHub Actions OIDC role takeover
# Difficulty: medium  ·  Cloud: aws  ·  Est. time: 20 min
#
# Starting position: Any GitHub user; you can create a workflow in a repo you control.
# Objective:         Assume the target AWS role from an arbitrary workflow you own.
#
# Playbook:
#   1. Discovers the target's role ARN — often committed to a public `.github/workflows/*.yml` or leaked in build logs.
#   2. Creates a workflow in their own repo that calls `aws-actions/configure-aws-credentials` with the target role-arn. The role's trust policy has no `sub` condition.
#      exploits: IAM-040
#   3. Workflow assumes the role and runs `aws sts get-caller-identity` → AdministratorAccess. Pivots to any resource.
#      exploits: IAM-040, IAM-041
#
# Cleanup: terraform destroy -auto-approve   (or: aws cloudformation delete-stack)
# ================================================================

terraform {
  required_providers {
    aws    = { source = "hashicorp/aws",    version = "~> 5.0" }
    random = { source = "hashicorp/random", version = "~> 3.6" }
  }
}

provider "aws" {
  region = "us-east-1"
}

resource "random_id" "suffix" {
  byte_length = 4
}

# Weak OIDC trust
# vulnerabilities: IAM-040, IAM-041
resource "aws_iam_openid_connect_provider" "id_oidc_trust" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}

resource "aws_iam_role" "id_oidc_trust" {
  name = "vbuild-id-oidc-trust"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Federated = aws_iam_openid_connect_provider.id_oidc_trust.arn }
      Action    = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
          # IAM-040: no sub-claim restriction — ANY GitHub workflow can assume.
          # Real fix: "token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:ref:refs/heads/main"
        }
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "id_oidc_trust_admin" {
  role       = aws_iam_role.id_oidc_trust.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

intentionally vulnerable. Apply only in an isolated sub-account / project, time-boxed and tagged. Never deploy on top of production.