← Volver a todas las publicaciones
Publicado el · por Renaud Deraison

La actualización añadió una clave

Durante once horas, el 29 de octubre, el marketplace de extensiones de Cursor sirvió una extensión troyanizada de Claude Code con el propio nombre de editor de Anthropic. Al cargarse, añadía la clave SSH de un atacante, instalaba un LaunchAgent e intentaba abrir un túnel inverso, en las máquinas de hasta 5000 desarrolladores. En Bromure Agentic Coding, el agente no viene de un marketplace de editor, y una carga como esta se ejecuta en una VM a la que nadie puede llegar: el cortafuegos de salida rechaza su túnel y rebobinar el home elimina su persistencia.

Nadie tuvo que abrir un chat ni aprobar una llamada a una herramienta. El editor cargó la extensión, y 66 líneas de JavaScript se ejecutaron como el desarrollador antes que el propio código de Claude Code.

El 29 de octubre, entre aproximadamente las 2:27 y las 13:35, hora del Pacífico, el marketplace de extensiones de Cursor sirvió una versión troyanizada de la extensión Claude Code de Anthropic. El aviso de seguridad de Cursor señala las versiones 2.1.312 y 2.1.313 de anthropic.claude-code, publicadas con la identidad de editor de Anthropic, y dice que Anthropic no las compiló. BleepingComputer informa de que hasta 5000 desarrolladores estuvieron expuestos, y The Hacker News apunta que cualquiera con la actualización automática activada pudo recibirla sin hacer nada.

Investigadores de Phorion y OpenSourceMalware publicaron sus análisis el 30 de octubre. Cursor retiró la extensión, cerró su marketplace para auditarlo y rotar claves, y consiguió que el servidor del atacante quedara fuera de línea. Nadie ha explicado todavía cómo se publicó esa versión con el nombre de Anthropic.

Qué hacían las 66 líneas

El atacante no tocó el código de Claude Code y puso delante un cargador de 66 líneas, al principio de extension.js. OpenSourceMalware describe el patrón: “place a compact backdoor before a large legitimate bundle, suppress errors, and let the expected application continue.” El cargador se ejecutaba cuando el editor cargaba la extensión y abría su webview. El desarrollador no tenía que empezar ninguna conversación. Después, la extensión real se cargaba y funcionaba.

Mientras tanto, el cargador:

  1. Hacía inventario. Nombre de host, nombre de usuario, sistema operativo, directorios home y de trabajo, el email global de git, la IP pública y los nombres de hasta 40 variables de entorno que contuvieran KEY, TOKEN, SECRET, PASS, AWS o GCP. Lo enviaba todo como JSON, por HTTP sin cifrar, a 134.209.82.225:8099/beacon.
  2. Añadía una puerta. Agregaba una clave Ed25519 con el comentario svc-telemetry a ~/.ssh/authorized_keys.
  3. Intentaba abrirla. En macOS ejecutaba sudo -n systemsetup -setremotelogin on, en Linux intentaba arrancar sshd y en Windows instalaba OpenSSH Server.
  4. Llamaba a casa. Escribía una clave privada en un archivo temporal y se conectaba a [email protected] con -N -R '*:0:localhost:22', un túnel inverso que reenvía un puerto del servidor del atacante al puerto SSH del propio desarrollador. Se reconectaba cada 60 segundos.
  5. Se quedaba para siempre. Un LaunchAgent de usuario llamado com.apple.sysmond en macOS, una entrada @reboot en el crontab en Linux y una tarea programada llamada SysMon en Windows.

Según BleepingComputer, errores de programación impidieron que SSH se activara en muchas víctimas. La clave, la persistencia y la baliza llegaron también a esas máquinas. Cursor recomienda buscar la clave svc-telemetry, eliminar la persistencia, plantearse reconstruir la máquina porque desinstalar la extensión puede no bastar, y rotar todas las credenciales a las que la máquina tuviera acceso, “using a trusted system.”

De una actualización de extensión a la clave de un atacante en la máquinaEl editor la carga2.1.312 / 2.1.313,como Anthropic,autoactualizada1 · Inventariohost, user, email git,IP pública, nombresde vars KEY/TOKEN2 · Clavesvc-telemetry añadidaa authorized_keys,SSH activado3 · Persistenciacom.apple.sysmond,crontab @reboot,tarea SysMon4 · Llamar a casatúnel inversoen :22, balizaen :8099La clave de un atacante y una vía de vuelta, en la cuenta del propio desarrolladorjunto a ~/.ssh, ~/.aws, la sesión de gh y cada repositorio; Cursor aconseja reconstruir la máquina
Lo que hacía la extensión troyanizada de Claude Code al cargarse, según el aviso de Cursor y el análisis de OpenSourceMalware. Un cargador de 66 líneas delante de la extensión real hacía inventario, añadía la clave SSH de un atacante, instalaba persistencia y abría un túnel inverso y una baliza HTTP hacia 134.209.82.225.

El código de más confianza de la máquina

Una extensión de editor se ejecuta con la cuenta completa del desarrollador. Puede leer todos los archivos del home, lanzar procesos y escribir un LaunchAgent que sobrevive al editor. Los marketplaces instalan actualizaciones sin preguntar, y el nombre de editor de esta era el de Anthropic. Un desarrollador prudente que comprobó el editor antes de instalar recibió la puerta trasera igualmente: llegó como actualización de una extensión que había instalado semanas antes.

Los agentes de código suben la apuesta. La extensión que aloja al agente es la que más permisos recibe de los desarrolladores: lee todo el repositorio, ejecuta comandos y vive en un proceso con las claves de API y los tokens de nube del entorno. Este cargador se llevó los nombres de esas variables. Leer sus valores cuesta las mismas pocas líneas.

La misma mañana en Bromure Agentic Coding

Bromure Agentic Coding ejecuta Claude Code, Codex, Grok, Kimi y omp en VM Ubuntu en tu Mac. Cada máquina es una VM propia, y el agente, sus repositorios, sus instalaciones de paquetes y sus comandos viven dentro de ella. El chat y los terminales en los que escribes los dibuja la app en tu Mac. Las credenciales reales, el proxy por el que pasa el tráfico de la VM y el registro de lo que ocurrió también se quedan en el Mac.

El agente no viene de un marketplace de editor

No hay ningún marketplace de extensiones en el camino: Bromure dibuja su propia ventana de chat en el Mac. Claude Code se instala en tu copia de la imagen base de Bromure mediante un paso de configuración que figura en un catálogo firmado con la misma clave que las actualizaciones de la app de Bromure. Ese paso instala @anthropic-ai/claude-code desde npm, envuelto en el escáner de cadena de suministro de Socket.dev. OpenSourceMalware incluye el paquete de npm entre las distribuciones que no se vieron afectadas.

Todo lo que el agente instala después pasa por el proxy del Mac, que aplica la política de cadena de suministro de la máquina. El filtro de antigüedad viene activado, con un mínimo de dos días: las versiones de npm, PyPI, Cargo, RubyGems y Packagist más recientes que eso se eliminan del listado del registro, así que latest apunta a la versión más nueva que lleve dos días publicada. La versión maliciosa de Cursor estuvo disponible once horas. Una versión secuestrada en npm con la misma vida nunca aparece en una máquina Bromure. Repasamos ese caso con la versión envenenada de LiteLLM en PyPI en La versión tenía dos horas.

La clave abre una puerta a la que nadie llega

Supón que una carga como esta entra igualmente en una máquina, a través de un repositorio que el agente clona o de una herramienta que ejecuta. Se ejecuta en Ubuntu, como el usuario de la máquina. No obtiene tu cuenta de macOS, ni tu Llavero, ni el ~/.ssh de tu Mac.

Su clave svc-telemetry va al propio authorized_keys de la VM. Las máquinas están en una red privada dentro de la app: tu Mac puede llegar a ellas, tu LAN física no, y nada de fuera puede conectarse salvo que publiques un servicio tú mismo. La única vía de entrada era el túnel inverso.

El túnel nunca se abre

El túnel necesita una conexión SSH saliente a 134.209.82.225, y la baliza, HTTP sin cifrar al puerto 8099 de la misma dirección. Cada conexión que hace la VM pasa por el Mac, donde se aplica el cortafuegos Outbound connections de la máquina. Sus reglas comparan host, rango de IP, protocolo y puerto, y con Unmatched traffic en Deny solo pasan los hosts que has listado. Una IP de DigitalOcean en el puerto 22 no está en la lista de nadie. El cortafuegos rechaza el túnel, cada reintento de 60 segundos y la baliza.

Cada rechazo queda en la Security Timeline de tu Mac, con el host, el puerto, el protocolo y el veredicto. Un deny a la misma IP cada minuto cuesta pasarlo por alto, y lo ves mientras el atacante sigue esperando una conexión.

El inventario no vale nada

La lista de nombres de variables de la baliza habría encontrado bastante en una máquina Bromure: ANTHROPIC_API_KEY, un token de GitHub, ajustes de AWS. Cada valor es un sustituto. Las credenciales reales están cifradas en el Mac, y el proxy cambia un sustituto por su valor real solo en las peticiones al host al que pertenece. Las claves privadas SSH no están en la VM en absoluto. SSH_AUTH_SOCK es un puente hacia el Mac que devuelve firmas, y el ssh-agent de tu Mac nunca queda expuesto. Un cargador que hubiera ido más allá y leído los valores habría obtenido cadenas que ningún servidor acepta. Si hubiera enviado una a su propio servidor, el proxy habría bloqueado la petición, pausado la VM y mostrado This environment may have been compromised, con el nombre de la credencial y el destino.

La limpieza lleva un minuto

Cursor indica a los desarrolladores afectados que eliminen la clave y la persistencia, se planteen reconstruir la máquina y roten las credenciales desde un sistema de confianza. En Bromure, la reconstrucción son dos opciones de menú. Bromure guarda un punto de control copy-on-write del home de cada máquina cada vez que arranca. Rewind home… devuelve /home/ubuntu a un arranque anterior a la ejecución de la carga, y se lleva con él la clave sobrante y el lanzador ~/.config/.smt. Reset disk vuelve a clonar el disco del sistema desde la imagen base y se lleva el crontab. Los ajustes, las credenciales y las claves SSH de la máquina sobreviven a ambas.

El sistema de confianza que pide Cursor es el Mac, que nunca ejecutó la carga. No hay nada que rotar, porque la VM nunca tuvo una credencial real.

Editor en un portátil: el cargador corre como túLa extensión se actualiza y se cargaen tu cuenta de macOS, junto a tus claves realesClave y persistencia en el Macsvc-telemetry en ~/.ssh/authorized_keys,LaunchAgent com.apple.sysmondDirecto a 134.209.82.225túnel inverso en :22 cada 60 s, baliza en :8099;arreglo: reconstruir y rotarlo todoBromure: la carga corre en una VMVM de la máquina (Ubuntu)clave en el authorized_keys de la VM; solo sustitutoscada conexión pasa por el MacCortafuegos de salida en tu Mac134.209.82.225:22 túnel: deny134.209.82.225:8099 baliza: denycada intento en la Security Timelinedespués, con la máquina apagadaRewind home… y Reset diskclave, lanzador y crontab fuera; ajustes y claves quedan
La misma carga en una máquina Bromure. Se ejecuta en la VM Ubuntu de la máquina, añade su clave al propio authorized_keys de la VM en una red a la que nadie de fuera puede llegar, y solo encuentra credenciales sustitutas. Su túnel y su baliza pasan por el Mac, donde el cortafuegos de salida los rechaza y la Security Timeline registra cada intento. Rewind home y Reset disk eliminan la persistencia; el Mac y sus credenciales reales quedan intactos.

Dale al trabajo arriesgado su propia máquina

Las credenciales y las reglas de cortafuegos se configuran por máquina, y crear máquinas cuesta poco. Una máquina que prueba herramientas, plugins y kits de inicio nuevos puede no tener credenciales de producción y un cortafuegos que lo rechace todo salvo sus registros de paquetes. La máquina que despliega conserva sus credenciales, con aprobación obligatoria antes de cada uso. Para un experimento puntual, bromure-cli vm run --rm crea una máquina que se borra al detenerse.

Sin marketplace en el camino

El chat forma parte de la app. Claude Code viene del paquete de npm de Anthropic mediante un paso de configuración firmado, escaneado por Socket.dev.

Dos días antes de cualquier novedad

El filtro de antigüedad oculta las versiones de paquetes con menos de dos días. Una versión secuestrada que vive once horas nunca se resuelve.

Una puerta a la que nadie llega

Una clave plantada acaba en el propio authorized_keys de la VM, en una red privada a la que ni tu LAN ni internet pueden conectarse.

Rechaza el host desconocido

Con Unmatched traffic en Deny, el túnel inverso y la baliza se rechazan, y cada intento queda registrado en el Mac.

Sustitutos en la VM

Cada clave de API y cada token de la VM es un sustituto, y las claves privadas SSH nunca entran. El inventario no vale nada.

Rebobinar en vez de reconstruir

Rewind home y Reset disk eliminan los archivos y la persistencia de la carga. Ajustes, credenciales y claves se quedan.

La herramienta que ejecuta al agente es parte de la superficie de ataque

Ni Cursor ni Anthropic han dicho cómo salió esa versión con el nombre de Anthropic. Para tu propia máquina, el incidente deja una cosa clara. El software que aloja a tu agente de código se ejecuta con tu cuenta y se actualiza solo, y un nombre de editor de confianza en la actualización no prueba nada sobre su contenido.

En Bromure Agentic Coding, tu agente y todo lo que toca se ejecutan en una VM en tu Mac. Esa VM solo tiene sustitutos, solo llega a los hosts que permites y puede volver a un arranque anterior a que algo saliera mal. Las claves reales nunca salen del Mac.

Sobre los infostealers que ahora recogen los tokens y los historiales de los agentes de código, lee Añadieron tu agente a la lista. Sobre un compromiso que empezó en el portátil de un desarrollador y terminó en 6300 millones de dólares, lee El portátil era la puerta de entrada.