7 min de lectura

Qué revisar en su cuenta AWS esta semana

Siete comprobaciones de solo lectura que caben en una hora y cubren lo que más se deja abierto en una cuenta AWS, con los comandos de la CLI.

Lo que suele aparecer en una cuenta AWS no es sofisticado. Es lo que se dejó abierto “por un rato”: una clave de acceso vieja con permisos amplios, un bucket público, un puerto de base de datos abierto a internet o un registro de auditoría apagado. Esta es la lista que usamos como primer paso, con los comandos para revisarla.

Todo lo que sigue es de solo lectura y se ejecuta sobre su propia cuenta. Basta un usuario o rol con la política administrada SecurityAudit.

1. Usuario root: con MFA y sin claves

El usuario root no se puede limitar con políticas. No debería tener claves de acceso y debería tener MFA.

aws iam get-account-summary \
  --query 'SummaryMap.{mfa:AccountMFAEnabled,claves:AccountAccessKeysPresent}'

Lo correcto es mfa: 1 y claves: 0. Si hay claves de root, elimínelas después de confirmar que nada las usa.

2. IAM: claves antiguas y usuarios sin uso

El informe de credenciales muestra, por usuario, la antigüedad y el último uso de cada clave y contraseña.

aws iam generate-credential-report
aws iam get-credential-report --query Content --output text \
  | base64 --decode > credenciales.csv

En ese archivo, busque claves activas de más de 90 días, claves que nunca se usaron y usuarios sin actividad reciente. Las personas deberían entrar con SSO o roles, no con claves permanentes; los servicios, con roles de IAM.

Revise también las políticas propias con comodines:

for arn in $(aws iam list-policies --scope Local --query 'Policies[].Arn' --output text); do
  v=$(aws iam get-policy --policy-arn "$arn" --query 'Policy.DefaultVersionId' --output text)
  aws iam get-policy-version --policy-arn "$arn" --version-id "$v" \
    --query 'PolicyVersion.Document' --output json | grep -q '"\*"' && echo "$arn"
done

No todo "*" es un error, pero cada uno merece una explicación. IAM Access Analyzer ayuda con los accesos desde fuera de la cuenta.

3. S3: acceso público

Primero, el bloqueo de acceso público a nivel de cuenta, que protege incluso si alguien se equivoca en un bucket:

aws s3control get-public-access-block \
  --account-id "$(aws sts get-caller-identity --query Account --output text)"

Las cuatro opciones deberían estar en true, salvo que tenga un sitio estático servido directamente desde S3; en ese caso conviene moverlo detrás de CloudFront con acceso de origen controlado. Luego, bucket por bucket:

for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
  echo "$b $(aws s3api get-bucket-policy-status --bucket "$b" \
    --query 'PolicyStatus.IsPublic' --output text 2>/dev/null)"
done

Esta consulta solo detecta los buckets públicos por política. En buckets creados antes de abril de 2023 también puede haber acceso público por ACL: revíselo con aws s3api get-bucket-acl --bucket <nombre> o con IAM Access Analyzer para S3.

4. Security groups abiertos a internet

Puertos de base de datos, SSH o RDP abiertos a todo internet son una de las causas más comunes de compromiso. Revise IPv4 e IPv6:

aws ec2 describe-security-groups \
  --filters Name=ip-permission.cidr,Values=0.0.0.0/0 \
  --query 'SecurityGroups[].{id:GroupId,nombre:GroupName,puertos:IpPermissions[].FromPort}'
aws ec2 describe-security-groups \
  --filters Name=ip-permission.ipv6-cidr,Values=::/0 \
  --query 'SecurityGroups[].{id:GroupId,nombre:GroupName,puertos:IpPermissions[].FromPort}'

Los puertos 80 y 443 en un balanceador son normales. El 22, el 3389, el 3306, el 5432, el 6379 o el 27017 abiertos a todo internet no lo son. Para administrar servidores, use Session Manager en lugar de abrir SSH.

Este comando revisa una sola región: repítalo en cada región que use.

5. CloudTrail y GuardDuty

Sin registro de auditoría no hay forma de saber qué pasó después de un incidente.

aws cloudtrail describe-trails \
  --query 'trailList[].{nombre:Name,multiregion:IsMultiRegionTrail}'
aws cloudtrail get-trail-status --name <trail> --query IsLogging
aws guardduty list-detectors

describe-trails muestra que el trail existe; get-trail-status confirma que está registrando (true). Conviene tener al menos un trail multirregión activo, con los registros en un bucket protegido, y GuardDuty encendido en las regiones que usa. GuardDuty tiene costo, que en cuentas pequeñas suele ser bajo; revise la estimación en la consola antes de activarlo en todas las regiones.

6. Secretos en texto plano

Las variables de entorno de las definiciones de tarea de ECS y de las funciones Lambda se ven en la consola y en la API. No son un lugar para contraseñas.

aws ecs describe-task-definition --task-definition <familia> \
  --query 'taskDefinition.containerDefinitions[].environment'
aws lambda get-function-configuration --function-name <función> \
  --query 'Environment.Variables'

Si aparecen contraseñas, tokens o cadenas de conexión, muévalos a Secrets Manager o Parameter Store y referéncielos desde la definición de tarea con secrets. Después, rótelos: un secreto que estuvo visible se considera expuesto. Revise también el repositorio con una herramienta como gitleaks, porque el mismo valor suele estar en el historial.

7. Snapshots e imágenes compartidas públicamente

Un snapshot público de un disco o de una base de datos es una copia completa de sus datos al alcance de cualquiera.

aws ec2 describe-snapshots --owner-ids self --restorable-by-user-ids all \
  --query 'Snapshots[].SnapshotId'
aws ec2 describe-images --owners self --filters Name=is-public,Values=true \
  --query 'Images[].ImageId'

La respuesta correcta para los dos es una lista vacía. Revise también los snapshots de RDS con describe-db-snapshot-attributes.

Qué hacer con lo que encuentre

Ordene por exposición: primero lo que es accesible desde internet sin credenciales (buckets, snapshots, puertos), después las credenciales con privilegios (root, claves antiguas con permisos amplios) y al final la detección (CloudTrail, GuardDuty). Antes de cerrar algo en producción, confirme qué lo usa: cerrar un security group que un servicio necesitaba también es un incidente.

Esta lista es un punto de partida y no reemplaza una revisión completa, que lee una por una las relaciones de confianza entre roles y busca rutas de escalamiento de privilegios. Pero cubre lo que más se repite.

Solo sobre sistemas propios o autorizados.

Las técnicas de esta nota se aplican sobre sistemas de los que usted es titular o con permiso por escrito del titular. Probar sistemas ajenos sin permiso es un delito en Ecuador (COIP, art. 234) y en la mayoría de los países.

Más notas

¿Quiere recibir las próximas? Siga el RSS.

Siguiente paso

¿Quiere que lo revisemos en su sistema?

Esta nota es parte de lo que probamos en el servicio de Revisión de seguridad AWS. Empezamos con una llamada gratuita de 30 minutos.