Incidente de ransomware en entorno de desarrollo: crónica de una contención

El 17 de julio de 2026, mientras intentaba crear una nueva instancia en Evolution API, empecé a recibir errores. La base de datos no respondía. Lo que parecía un bug resultó ser un ataque de ransomware automatizado que había tumbado el Postgres.

Al revisar el contenedor, las bases evolution y whatsapp_ingestion habían sido dropedas. En su lugar había una base llamada readme_to_recover con una nota de extorsión: 0.0078 BTC, DATAID 3GDBO. No exfiltraron datos. Los bots automatizados solo borran y extorsionan.

Causa raíz

Un Postgres expuesto en 0.0.0.0:5432 con credenciales postgres:postgres. Un bot escaneó el puerto, encontró credenciales débiles y ejecutó DROP DATABASE. No fue dirigido: fue un barrido automatizado de IPv4.

El docker-compose.yml publicaba varios puertos en 0.0.0.0:

  • Postgres en 5432 (el que sufrió el ataque)
  • Redis en 6379 (el SG nunca lo permitió — no expuesto realmente)
  • RabbitMQ en 5672 y 15672 (ídem, bloqueados por SG)
  • MariaDB de qhipa en 3001 (credenciales no-default, no comprometida)

Contención

En menos de una hora se cerraron todas las puertas:

  1. Password de Postgres rotado a 48 caracteres hex, verificado contra scram-sha-256
  2. Postgres bindeado a 127.0.0.1
  3. Regla 5432 eliminada del Security Group sg-0f1135f2bdfe3cdfe
  4. Redis y RabbitMQ bindeados a 127.0.0.1 (defensa en profundidad)
  5. SSM Parameter Store actualizado con los nuevos secretos
  6. MariaDB qhipa: puerto 3001 cerrado en su SG
  7. Puertos huérfanos 8051, 8002, 1337 limpiados del SG

Remediación completa

En una segunda sesión se ejecutó la remediación completa:

  • docker compose down -v --remove-orphans (volúmenes eliminados, readme_to_recover borrada)
  • Re-pull de imágenes y up -d con compose hardened
  • Alembic: 13 migraciones ejecutadas
  • API key de Evolution rotada: de SUMlung9541 (11 chars, débil) a 48 caracteres hex. Verificado: nueva 200, vieja 401
  • WhatsApp reconectado (instancia creada + QR escaneado)

Terraform en la solución

La infraestructura está organizada en dos capas de Terraform: platform (Security Groups, host EC2) e ingesta (SSM secrets, IAM roles, ECS). Tenerla como código permitió alinear SSM Parameter Store como fuente de verdad de secretos, planificar el hardening del compose como PRs en lugar de parches en el servidor, y dejar definida la estrategia de backups (issue #3: pg_dump → S3 Object Lock). No evitó el ataque, pero hizo que la recuperación fuera un procedimiento y no una crisis.

Lección de Prosegur (2019)

No es la primera vez que enfrento un ransomware. En 2019 trabajaba en el área de seguridad electrónica de Prosegur cuando la empresa sufrió el ataque de Ryuk, uno de los casos más conocidos en el mundo hispanohablante.

El 26 de noviembre de 2019, Ryuk se propagó por la red corporativa encriptando servidores. Mi tarea fue analizar cómo contrarrestar el avance: el malware se expandía activamente y había que contenerlo antes de que llegara a sistemas críticos. Investigamos vectores de desencriptación, mapeamos sistemas comprometidos y aplicamos Terraform como parte de la reconstrucción de infraestructura.

En Prosegur aprendí que un ransomware no avisa, no discrimina, y explota siempre el eslabón más débil. En 2019 fueron credenciales comprometidas; en 2026 fue un puerto abierto con password por defecto. La diferencia es que esta vez la infraestructura estaba en código.

Estado actual

Perímetro final inbound 0.0.0.0/0: sg-0f solo puerto 22, sg-05 solo 80/443/22. Evolution API detrás de Traefik con TLS. Ningún datastore publica puertos. Secretos en SSM, no hardcodeados. Issues #1 y #2 cerradas. Issue #3 (backups) abierta.

Lecciones

  1. Un bot escaneando IPv4 va a encontrar tu puerto abierto. No es si, es cuándo.
  2. Si usás postgres:postgres en un entorno expuesto, te van a vulnerar.
  3. Los Security Groups son tu última línea. No confíes solo en el firewall de Docker.
  4. Tener Terraform no evita el ataque, pero hace que la recuperación sea un procedimiento.
  5. Un backup que el atacante puede borrar no es un backup.