Aprende › Vulnerabilidades web

S3 buckets públicos: cómo se descubren, se auditan y se protegen

· 10 min de lectura

Los buckets S3 mal configurados siguen siendo la causa número uno de filtraciones masivas de datos en la nube. Aprende cómo descubrirlos, enumerar contenido, escalar con write, y asegurarlos con Block Public Access, ACLs y políticas correctas.

Cada año aparecen titulares del tipo «empresa X expone millones de registros en un bucket S3 abierto». No es exageración: AWS S3 mal configurado sigue siendo, en 2026, la causa número uno de filtraciones masivas de datos en la nube. Capital One, Twilio, Accenture, FedEx, Verizon, Facebook, Microsoft han protagonizado alguno de esos titulares. Y el patrón se repite: un desarrollador marca un bucket como público «solo para una prueba», olvida cerrarlo, y meses después lo encuentra un bot con la lista entera de clientes dentro.

Amazon S3 (Simple Storage Service) es almacenamiento de objetos. Guarda archivos en contenedores llamados buckets, cada bucket tiene un nombre globalmente único y se expone en URLs predecibles (https://<bucket>.s3.amazonaws.com/<key>). El problema de su modelo de permisos clásico es que mezcla cuatro mecanismos distintos (ACLs, bucket policies, IAM policies, Block Public Access), con combinaciones nada intuitivas. Un error en cualquiera abre el bucket al mundo.

En esta lección vas a ver cómo se enumeran, se auditan, se escalan y se defienden buckets S3. Es la primera lección del bloque cloud en /aprende; las siguientes cubren IAM y los metadata endpoints.

Prueba solo contra buckets propios o laboratorios autorizados. Acceder a un bucket ajeno sin permiso, incluso aunque sea público, cae bajo la Computer Fraud and Abuse Act en EE.UU. y equivalentes europeos. Mira, no toques.

El modelo de permisos en S3#

Capas de permisos S3: Block Public Access, Bucket Policy, Bucket ACL, Object ACL, IAM
Capas de permisos S3: Block Public Access, Bucket Policy, Bucket ACL, Object ACL, IAM

Hay cinco capas que controlan el acceso a un objeto:

  • Block Public Access (BPA): switch maestro a nivel de cuenta y bucket. Si está activo, bloquea todo lo público sin importar el resto.
  • Bucket Policy: JSON que define qué principales pueden hacer qué sobre el bucket.
  • Bucket ACL: lista de accesos legada, poco granular. AWS recomienda deshabilitarla.
  • Object ACL: ACL por objeto individual. Aún más legada.
  • IAM Policy: permisos del usuario o rol que llama.

El acceso efectivo se calcula como unión de todos los allow menos todos los deny, pero con BPA actuando como filtro final. La combinación más común que termina en filtración: Bucket Policy mal escrita con "Principal": "*" y BPA desactivado.

Enumeración: encontrar buckets#

Patrones de nombre#

Los buckets siguen convenciones predecibles:

  • <empresa>-backup, <empresa>-logs, <empresa>-prod, <empresa>-dev, <empresa>-media.
  • <servicio>.<empresa>.com cuando se usan como CDN.
  • <empresa>-<región>, <empresa>-<año>, <empresa>-<cliente>.

Si tienes el dominio whoami-labs.com, los candidatos a probar: whoami-labs-backup, whoami-backup, whoami-logs, whoami-dev, etc.

Herramientas#

# s3scanner: barre una wordlist y marca abiertos
s3scanner scan --bucket-file buckets.txt

# aws-cli sin credenciales: lista pública si existe
aws s3 ls s3://<bucket> --no-sign-request

# curl directo: 403 = existe, 404 = no existe
curl -sI https://bucket-nombre.s3.amazonaws.com/

El código 403 es información útil: indica que el bucket existe pero no es listable. Un 404 significa que no existe. La diferencia ayuda a descubrir nombres válidos y afinar la fuerza bruta.

Fuentes públicas#

  • grayhatwarfare.com: motor de búsqueda de objetos S3 públicos indexados.
  • buckets.grayhatwarfare.com: 5+ millones de buckets indexados, consultables por keyword.
  • Shodan: product:"Amazon S3" y consultas por región.
  • GitHub: commits con URLs s3.amazonaws.com revelan buckets usados por la empresa.

Lectura: cuando el bucket lista#

Con aws s3 ls s3://bucket --no-sign-request y el bucket responde con contenido, tienes acceso de lectura público. A partir de ahí:

# Listar recursivo
aws s3 ls s3://bucket --recursive --no-sign-request

# Descargar objeto específico
aws s3 cp s3://bucket/path/archivo.sql . --no-sign-request

# Sincronizar todo (cuidado con el volumen)
aws s3 sync s3://bucket ./local --no-sign-request

En labs y auditorías autorizadas, ese paso te da inventario inmediato de todo lo que hay. En bug bounty jamás descargas más de lo necesario para demostrar el hallazgo: una captura con la lista de archivos y un objeto representativo es suficiente.

Qué contenido suele aparecer#

  • Backups de bases de datos: .sql, .dump, .bak con millones de registros.
  • Archivos de configuración: .env, config.json, con credenciales, API keys, strings de conexión.
  • Logs de aplicación: con información sensible, errores con stack traces, cookies.
  • Documentos internos: contratos, acuerdos, hojas de cálculo con datos personales.
  • Assets de aplicaciones móviles: APKs, mapas de código, claves privadas de firma.
  • Snapshots de infraestructura: Terraform states (terraform.tfstate) con secretos en texto plano.

Un solo bucket abierto con un .tfstate suele contener claves de acceso a toda la cuenta AWS.

Escritura: la variante crítica#

El caso grave es cuando un bucket permite PUT público sin autenticación. Significa que puedes subir archivos. Las consecuencias según el uso del bucket:

  • CDN de la empresa: sustituyes su logo, su JavaScript, inyectas malware.
  • Hosting estático: cambias su página de inicio o inyectas phishing con dominio legítimo.
  • Bucket que sirve assets a su aplicación web: XSS persistente en toda la aplicación sin tocar su código.
  • Bucket de uploads de usuarios: subes a cualquier carpeta, ataques directos a usuarios.

Prueba de escritura:

echo "test" > prueba.txt
aws s3 cp prueba.txt s3://bucket/prueba.txt --no-sign-request

Si responde con upload: prueba.txt to s3://..., escritura pública confirmada.

Toma de control: cuando el DNS apunta al bucket#

S3 subdomain takeover: CNAME apunta a bucket eliminado, registras el nombre libre, controlas el subdominio
S3 subdomain takeover: CNAME apunta a bucket eliminado, registras el nombre libre, controlas el subdominio

Un ataque específico: subdomain takeover por S3. El patrón:

  1. La empresa tiene assets.empresa.com con CNAME a assets-empresa.s3.amazonaws.com.
  2. Dejan de usar el bucket y lo eliminan sin borrar el CNAME.
  3. Tú registras un bucket con ese mismo nombre (son globales).
  4. Ahora assets.empresa.com sirve tu contenido como si fuera suyo.

Detección con herramientas:

# subzy: identifica subdominios tomables
subzy run --targets subdominios.txt

# nuclei: template subdomain-takeover-s3
nuclei -u https://assets.empresa.com -t takeovers/

Con el takeover controlas:

  • Cookies de sesión: si el subdominio compartía dominio con la app, puedes robar cookies.
  • Confianza del navegador: contenido tuyo bajo dominio legítimo (phishing creíble).
  • SEO: pages rank heredado del subdominio original para posicionar lo que quieras.

En programas bug bounty, un S3 takeover suele puntuar como High (3000-10000 USD).

Auditar tu propia cuenta AWS#

Si eres defensor, auditarte es más fácil que descubrir buckets ajenos:

# Listar todos tus buckets
aws s3api list-buckets --query 'Buckets[*].Name' --output text

# Para cada bucket, ver políticas y BPA
for b in $(aws s3api list-buckets --query 'Buckets[*].Name' --output text); do
  echo "=== $b ==="
  aws s3api get-public-access-block --bucket "$b" 2>&1
  aws s3api get-bucket-policy --bucket "$b" 2>&1
  aws s3api get-bucket-acl --bucket "$b" 2>&1
done

Herramientas específicas:

  • ScoutSuite: audita la cuenta entera, marca buckets con exposición pública.
  • Prowler: checks detallados sobre cada bucket contra CIS Benchmark.
  • AWS Config Rules: detección continua, s3-bucket-public-read-prohibited.
  • AWS Access Analyzer: identifica buckets accesibles desde fuera de la organización.

Defensa correcta#

La configuración moderna recomendada para un bucket que no debe ser público:

# Block Public Access (todas las opciones a true)
{
  "BlockPublicAcls": true,
  "IgnorePublicAcls": true,
  "BlockPublicPolicy": true,
  "RestrictPublicBuckets": true
}

Y a nivel de cuenta activar BPA global, para que ningún bucket de la cuenta pueda ser público ni por error.

Para buckets que sí deben ser públicos (CDN estático), la mejor práctica no es dejarlos públicos: ponlos detrás de CloudFront con Origin Access Identity (OAI) o la versión moderna Origin Access Control (OAC). El bucket queda privado, solo CloudFront puede leerlo, y CloudFront es lo que ve Internet.

Además:

  • Cifrado por defecto (SSE-S3 mínimo, SSE-KMS recomendado para datos sensibles).
  • Versioning y MFA Delete para prevenir borrados accidentales o maliciosos.
  • Object Lock (modo Compliance) para backups: ni root puede borrarlos durante el periodo.
  • Logs de acceso activados y enviados a otro bucket con sus propios logs.

Casos reales famosos#

Algunos incidentes conocidos que cualquier profesional cloud debería tener en mente al diseñar permisos:

AñoEmpresaQué se filtró
2017Verizon14 millones de registros de clientes en un bucket público de un partner
2017AccentureClaves maestras, datos internos en cuatro buckets
2018FedExEscaneos de pasaportes y DNI de clientes del servicio de envío
2019Capital One100 millones de registros vía SSRF al metadata + bucket (lo verás en la próxima lección)
2020TwilioBackup público con credenciales hardcodeadas
2023Varios SaaSBuckets de desarrollo con producción espejada

El patrón se repite: no es que S3 sea inseguro, es que un error de configuración humano queda permanentemente expuesto. Las empresas con prácticas maduras cierran esa superficie con Block Public Access global, pipelines de IaC con checks previos a apply, y auditoría continua con Config Rules o Access Analyzer.

Errores comunes del defensor#

  • Marcar público «solo para una prueba» y olvidarlo.
  • Usar Principal: "*" en una bucket policy sin restringir con Condition.
  • Pensar que un bucket público pero con nombres largos no será encontrado.
  • Deshabilitar BPA a nivel de cuenta por un caso puntual.
  • Mezclar ACLs y Bucket Policies sin saber cuál manda.
  • No activar logs de acceso: imposible saber qué se descargó cuando llega la noticia.

Preguntas frecuentes#

¿Los buckets con nombres UUID están a salvo?#

Más, pero no del todo. Un UUID aleatorio resiste la fuerza bruta directa, pero si aparece en cualquier commit público, header HTTP, respuesta de aplicación o config filtrada, el bucket se vuelve descubrible. Buckets privados con BPA activo no dependen del nombre.

¿Google Cloud Storage y Azure Blob tienen el mismo problema?#

Sí. GCS buckets y Azure Storage containers tienen exactamente los mismos patrones de exposición. Herramientas equivalentes: gcpbucketbrute para GCS, MicroBurst para Azure. La lección aplica al cloud entero.

¿Quién puede listar mi bucket si es público?#

Cualquiera en Internet. Google indexa muchos. Bots de bug bounty y scrapers maliciosos los barren continuamente. Si el bucket contiene algo sensible, trátalo como si estuviera en una web pública.

¿Un bucket público solo de descarga es seguro?#

Para CDN de assets no sensibles, sí (imágenes de un blog, por ejemplo). Para cualquier otra cosa, no: muchos incidentes documentados empezaron con «era solo para una prueba».

¿Dónde practico?#

En el laboratorio database de whoami-labs hay un escenario con un bucket público simulado que contiene backups con credenciales que llevan al siguiente paso del lab. Practicas descubrimiento, listado, descarga controlada y el uso de lo encontrado.

¿Un bucket privado pero firmado con URL pre-signed es seguro?#

Los pre-signed URLs son enlaces temporales que permiten a quien tenga el link acceder al objeto. Son seguros mientras el link no se comparta de forma inapropiada, pero conviene elegir TTL cortos (minutos, no días), y nunca enviarlos por canales inseguros o loggearlos en texto plano. Cada URL firmada es tan privada como la cadena de texto que la compone: si acaba en un log público, el objeto queda expuesto hasta que caduque.

¿Vale la pena S3 Object Lock para backups?#

Sí, en modo Compliance es la mejor defensa contra ransomware que cifra tus backups. Durante el periodo de retención configurado, ni siquiera el usuario root de la cuenta puede borrar ni modificar el objeto. Cambió el juego defensivo tras los ataques masivos de ransomware a servidores de backup de 2020-2022. Para datos regulados (compliance financiera, sanitaria), suele ser requisito.

Resumen#

  • S3 mal configurado es la causa nº 1 de filtraciones masivas en la nube.
  • Hay cinco capas de permisos; Block Public Access es el switch maestro.
  • Enumeración: patrones de nombre, s3scanner, grayhatwarfare.com, GitHub dorks.
  • Lectura pública da inventario, backups, .env, .tfstate, claves AWS.
  • Escritura pública es la variante crítica: defacement, XSS persistente, malware.
  • S3 subdomain takeover: CNAME a bucket borrado, registras tú el nombre.
  • Defensa: BPA siempre, CloudFront+OAC para CDN, cifrado, logs, Object Lock.

Fuentes y lecturas recomendadas#

Practícalo en estos laboratorios

Ver todos los laboratorios de esta categoría