← Volver al blog

CARBONATO: Un gusano de IA secuestra servidores Docker. Lista de verificación para quienes tienen parsers en VPS

El botnet CARBONATO captura servidores a través de Docker API sin contraseña, instala el agente de IA GH0ST y primero roba las claves de los modelos de lenguaje. Analizamos la cadena de ataque, los signos de infección y una lista de verificación de 15 minutos para aquellos que mantienen parsers, claves LLM y credenciales de proxy en su VPS.

📅27 de septiembre de 2026
CARBONATO: Un gusano de IA secuestra servidores Docker. Lista de verificación para quienes tienen parsers en VPS

El 22 de septiembre de 2026, los investigadores de ThreatDown describieron el botnet CARBONATO. Este accede a los servidores a través de una API de Docker abierta sin contraseña, levanta un agente de IA en el host y primero busca claves para modelos de lenguaje. Las claves SSH y los tokens de acceso vienen después. Si en su VPS están corriendo parsers en contenedores, y en .env hay claves de OpenRouter o OpenAI y credenciales de proxy, ese es su perfil de riesgo. A continuación, un análisis de cómo se lleva a cabo el ataque y una lista de verificación de 15 minutos que cierra esta entrada.

Qué encontraron: 4,3 GB de imágenes y un agente llamado GH0ST

Todo comenzó con un registro de Docker no cerrado de los propios operadores. Según ThreatDown, había 59 repositorios, 234 etiquetas de imágenes y 4,3 GB de datos. El archivo abarca el período de octubre de 2024 a agosto de 2026, es decir, el botnet operó casi dos años antes de ser descrito. En los repositorios se encontraron un minero XMRig y imágenes con nombres del tipo fsociety/agent.

La cadena de infección según el informe es la siguiente:

  1. El escáner busca hosts donde la API de Docker esté abierta en TCP 2375 sin autenticación.
  2. A través de esta API, el gusano lanza un contenedor privilegiado con el sistema de archivos del host montado. Desde este momento, tiene acceso root en la máquina.
  3. Se establece un túnel SSH inverso hacia la infraestructura de los operadores, y se instala un servidor SSH con su clave.
  4. El anclaje se realiza a través de cron, temporizadores de systemd, rc.local y OpenRC, y los archivos se marcan como inmutables. Los procesos de vigilancia vuelven a descargar las imágenes si se elimina algo.
  5. El contenedor se disfraza como systemd-resolved, y el proceso como un hilo del núcleo [kworker/u2:0].
  6. Cada cinco minutos, los scripts escanean subredes vecinas /24 y puentes de Docker en busca del siguiente puerto abierto 2375.

La propagación es completamente automática y no depende de la IA. La IA aquí se encarga de lo que sucede dentro del servidor ya comprometido.

Por qué el botnet necesita un agente de IA y por qué necesita específicamente claves LLM

En el host se instala el marco abierto Hermes Agent con la persona GH0ST (sus instrucciones están en el archivo SOUL.md). El operador escribe una tarea en Telegram, el agente la reenvía junto con las instrucciones al gateway LLM de la operación. El modelo descompone la tarea, escribe comandos para la terminal, lee la salida y decide qué hacer a continuación. Los resultados regresan a Telegram.

En las instrucciones del agente, las prioridades están claramente delineadas. ThreatDown cita: «Las claves API de IA son la máxima prioridad. Exfiltrar primero». En la lista hay 14 proveedores de modelos, incluidos OpenAI, Anthropic, Google, Groq, Mistral y OpenRouter. Las cuentas SSH, los tokens de acceso y los datos de bases de datos son los siguientes puntos.

La lógica es simple: una clave LLM robada se convierte inmediatamente en cálculos gratuitos o en un producto para reventa, y el propietario de la clave paga por ellos. A diferencia de la minería, este robo no se nota por la carga en el procesador. Solo se notará por la factura del proveedor del modelo.

Por qué esto afecta a quienes hacen scraping

El stack típico de scraping en 2026 se ve así: VPS, varios contenedores (crawler, cola, base de datos, navegador sin cabeza), LLM para analizar páginas y un pool de proxies. Todos los secretos están en un .env o en las variables de entorno de los contenedores. Para un agente que «lee todo» con acceso root, esto es un botín listo en un solo archivo:

  • claves LLM: por ellas se creó CARBONATO;
  • credenciales de proxy: el informe no las destaca por separado, pero el agente con acceso root recoge cualquier cuenta y token que ve, y las credenciales de proxy suelen estar cerca;
  • claves de nube y accesos a bases de datos con los resultados del scraping;
  • el propio servidor: XMRig en el mismo archivo significa que la CPU de su crawler se destinará a la minería, y las tareas comenzarán a fallar por timeouts.

Un problema adicional es la reputación. El servidor desde el cual el gusano escanea subredes ajenas rápidamente entra en listas de abuso, y el proveedor de hosting puede bloquearlo por quejas. Para el scraping, esto es un doble golpe: la IP del servidor está marcada, y las credenciales de proxy robadas ya están consumiendo su tráfico con solicitudes ajenas.

Cómo el puerto 2375 se encuentra abierto, aunque usted no lo haya abierto

Por defecto, Docker escucha en un socket UNIX local, no en la red. El puerto 2375 aparece cuando alguien ha añadido intencionadamente -H tcp://0.0.0.0:2375 en la configuración del demonio. Normalmente, esto se hace para conectar un IDE remoto, CI o un panel de control de contenedores, y luego se olvida. La documentación de Docker advierte que el acceso al demonio equivale a acceso root a la máquina, y aconseja proteger las claves de él como si fueran la contraseña root. La versión cifrada con TLS funciona en el puerto 2376, mientras que el 2375 significa texto abierto sin verificación del cliente.

La segunda trampa afecta a quienes están seguros de que «tengo ufw». La documentación de Docker indica que el tráfico de los puertos publicados de los contenedores se redirige en la tabla nat antes de las cadenas INPUT y OUTPUT, en las que se basa ufw. En la práctica, las reglas de ufw para tales puertos simplemente no se activan. Si ha iniciado Redis, un panel de cola o un gestor de proxies con -p 6379:6379, el puerto está expuesto a Internet, independientemente de lo que muestre ufw status.

Lista de verificación de 15 minutos para un servidor con parsers

1. Verifique que la API de Docker no escuche en la red

  1. Revise los puertos escuchando: ss -tlnp | grep -E '2375|2376|dockerd'. Si en la salida aparece 0.0.0.0:2375 o :::2375, ciérrelo inmediatamente.
  2. Verifique de dónde proviene la bandera: /etc/docker/daemon.json (clave "hosts") y la unidad systemctl cat docker (línea ExecStart con -H tcp://).
  3. Elimine el listener TCP y reinicie el demonio. Para el control remoto, use el contexto SSH: docker context create remote --docker host=ssh://user@server. En este caso, el puerto externo no es necesario en absoluto.
  4. Si TCP es realmente necesario (CI, orquestador), entonces solo 2376 con autenticación mutua TLS y una lista blanca de IP de origen.

2. Verifique qué está expuesto desde los contenedores

  • Ejecute docker ps --format '{{.Names}} {{.Ports}}'. Todo lo que comience con 0.0.0.0: está disponible desde Internet eludiendo ufw.
  • Los servicios de utilidad (Redis, Postgres, Mongo, paneles de cola, Selenium Grid, API de gestor de proxies) publíquelos solo en la dirección local: -p 127.0.0.1:6379:6379. El acceso externo hágalo a través de un túnel SSH.
  • Si no puede evitar un puerto externo, filtre en la cadena DOCKER-USER: Docker no la sobrescribe, y es la que se aplica al tráfico de los contenedores.
  • No mantenga un proxy abierto en el servidor sin autorización (Squid en 3128, SOCKS en 1080 «para los de casa»). Los escáneres encuentran regularmente tales puertos, y a través de su IP pasa tráfico ajeno con quejas ajenas.

3. Maneje los secretos

  • Divida las claves: una clave LLM separada para cada servidor o proyecto, con límite de gastos en el proveedor del modelo. Una clave robada con un límite de $20 es un inconveniente, sin límite es un agujero en el presupuesto.
  • Las claves de proxy también divídalas por tareas: una credencial (subcuenta) separada para cada parser. Así, una filtración se puede ver por el consumo de una credencial específica, y solo se puede revocar esa, sin detener el resto del trabajo.
  • No pase todo el .env al contenedor a través de env_file, si el servicio solo necesita dos claves de veinte.
  • Donde sea posible, vincule el acceso a la IP del servidor: lista blanca en el proveedor de proxies o restricción de la clave API por dirección.

Más detalles sobre dónde y cómo almacenar credenciales de proxy en scripts y contenedores, hemos escrito en el análisis de almacenamiento seguro de credenciales de proxy.

4. Elimine privilegios innecesarios

  • No ejecute contenedores con --privileged y no monte / o /var/run/docker.sock dentro sin necesidad extrema. El socket dentro del contenedor es lo mismo que root en el host.
  • Para navegadores sin cabeza, generalmente es suficiente con --shm-size y un perfil seccomp. El modo privilegiado «para que Chrome funcione» es un mal compromiso.

Cómo saber si ya ha sido comprometido

ThreatDown y los análisis de su informe mencionan los siguientes signos de compromiso:

  • archivo SOUL.md con la palabra GH0ST (por ejemplo, /root/.hermes/SOUL.md);
  • variable de entorno o línea en .env: CARBONATO_API_KEY;
  • archivos /usr/local/bin/.docker-network-monitor y sospechoso /usr/sbin/systemd-logind;
  • contenedor con el nombre systemd-resolved (el verdadero systemd-resolved es un servicio del host, no un contenedor);
  • tráfico saliente inesperado hacia la API de Telegram y túneles SSH inversos hacia AS262145;
  • conexiones a las direcciones 45.79.183.61, 213.136.79.115, 190.211.124.187;
  • archivos inmutables en cron y systemd: lsattr /etc/cron.d/* /etc/systemd/system/* mostrará la bandera i.

Si al menos un signo coincide, limpiar el servidor manualmente es inútil: el anclaje es multicapa, y el guardián devolverá los implantes. El orden correcto es el siguiente:

  1. Desde otra máquina, revoque todas las claves que estaban en el servidor: LLM, nube, bases de datos, proxies.
  2. Verifique el consumo de cada clave en las últimas semanas con los proveedores. La estadística por la credencial de proxy mostrará inmediatamente el tráfico ajeno.
  3. Levante un nuevo servidor desde una imagen limpia, emita nuevas claves y solo después transfiera datos, sin binarios ni archivos cron de la máquina antigua.

Dónde están los proxies y qué no resuelven

Los proxies no protegen al servidor de CARBONATO. El gusano no llega a través de sus solicitudes salientes, sino a través de un puerto de entrada. Sin embargo, un esquema de trabajo adecuado con proxies reduce el daño. Credenciales separadas por tareas, límites de tráfico y vinculación por IP convierten una filtración de «todo el saldo se fue» en «revocó una credencial».

También hay un lado opuesto que los botnets muestran regularmente: los dispositivos capturados ajenos se convierten en «proxies residenciales» de redes dudosas. Por lo tanto, para el scraping, es recomendable obtener tráfico de un proveedor con un origen claro del pool. Para la mayoría de las tareas de recolección de datos, son adecuados proxies residenciales con pago por gigabyte, donde el consumo por cada proxy es visible en el panel. Para tareas de servicio sin una fuerte protección contra bots, son suficientes proxies de centros de datos más económicos.

Conclusión

CARBONATO no utiliza vulnerabilidades de día cero ni exploits astutos. Entra por la puerta que los propietarios de servidores abrieron ellos mismos: TCP 2375 sin contraseña. Lo nuevo en él es que dentro opera un agente de IA, que tiene la tarea de recuperar primero las claves para los modelos. Para aquellos que recopilan datos en sus VPS, la conclusión es práctica. Cierre la API de Docker, publique los puertos de servicio en 127.0.0.1, divida las claves LLM y los proxies por tareas con límites de gastos. Esto toma 15 minutos de trabajo, y después de eso, su servidor se convierte en un objetivo poco interesante para tales botnets.