back
vulnerabuild_ / build
attack paths
2 components · 5 vulnerabilities
infrastructure-as-code
format
cloud
region
# ================================================================
# Attack path: Container supply chain → host root
# Difficulty: hard  ·  Cloud: multi  ·  Est. time: 60 min
#
# Starting position: Anonymous internet user.
# Objective:         Obtain root on a Kubernetes node by poisoning the container image the cluster pulls.
#
# Playbook:
#   1. Discovers the public registry — Shodan, organisation's GitHub Actions logs, or a leaked Dockerfile.
#      exploits: REG-001
#   2. Anonymously pushes a backdoored image at the same tag the cluster pulls. Scan-on-push is disabled, so no alarm fires.
#      exploits: REG-001, REG-004
#   3. Waits for the next deployment (or triggers a rollout). The pod runs with `privileged: true` and `hostPath: /` mounted read-write.
#      exploits: K8S-101, K8S-108
#   4. Backdoor sees `/host`, runs `chroot /host /bin/bash`, drops a persistent SSH key — root on the node.
#      exploits: K8S-108
#
# 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" }
    kubernetes = { source = "hashicorp/kubernetes", version = "~> 2.0" }
  }
}

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

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

# Configure the kubernetes provider against your EKS cluster before applying pod / RBAC resources:
# provider "kubernetes" {
#   config_path = "~/.kube/config"
# }

# Public registry
# vulnerabilities: REG-001, REG-004
resource "aws_ecr_repository" "ctr_public_registry" {
  name                 = "vbuild-public-registry"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration {
    scan_on_push = false
  }
}

resource "aws_ecr_repository_policy" "ctr_public_registry" {
  repository = aws_ecr_repository.ctr_public_registry.name
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Sid       = "AllowAll",
      Effect    = "Allow",
      Principal = "*",
      Action = [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:PutImage",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload"
      ]
    }]
  })
}

# Privileged Kubernetes pod
# vulnerabilities: K8S-101, K8S-108, K8S-115
# Requires a kubernetes provider configured against the cluster.
resource "kubernetes_manifest" "ctr_priv_pod" {
  manifest = {
    apiVersion = "v1"
    kind       = "Pod"
    metadata = {
      name      = "vbuild-priv-pod"
      namespace = "default"
    }
    spec = {
      hostPID = true
      containers = [{
        name    = "priv"
        image   = "alpine:3.19"
        command = ["sleep", "infinity"]
        securityContext = {
          privileged = true
        }
        volumeMounts = [{
          name      = "host-root"
          mountPath = "/host"
        }]
      }]
      volumes = [{
        name = "host-root"
        hostPath = { path = "/" }
      }]
    }
  }
}

# Default ServiceAccount → cluster-admin (K8S-115).
resource "kubernetes_manifest" "ctr_priv_pod_binding" {
  manifest = {
    apiVersion = "rbac.authorization.k8s.io/v1"
    kind       = "ClusterRoleBinding"
    metadata = { name = "vbuild-default-cluster-admin" }
    roleRef = {
      apiGroup = "rbac.authorization.k8s.io"
      kind     = "ClusterRole"
      name     = "cluster-admin"
    }
    subjects = [{
      kind      = "ServiceAccount"
      name      = "default"
      namespace = "default"
    }]
  }
}

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