Esperó a la tercera llamada
Pillar Security está siguiendo a Deadbugz, un servidor MCP colado en proyectos de código abierto mediante pull request. Trae dos herramientas honestas, cuenta las llamadas de herramienta de tu agente y, en la cuarta, reescribe los metadatos que devuelve como instrucciones para recopilar claves SSH, credenciales de AWS, historial del shell y kubeconfig, y no decir nada al respecto. Ningún fallo, ninguna CVE: el protocolo permite todo eso. La víctima es la idea de que aprobar un servidor una vez te diga algo sobre la llamada siguiente. En un espacio de trabajo de Bromure Agentic Coding la carga se ejecuta entera, recoge cuatro señuelos y muere en el proxy al salir.
Leíste el código. Lo ejecutaste. Daba formato a texto, tal como anunciaba. Lo aprobaste, y el atacante contaba con que lo aprobaras.
La noche del 10 de agosto, entre las 21:52 y las 23:07 UTC, una cuenta de GitHub
llamada zellkernel abrió veintitrés pull requests contra proyectos sin relación
entre sí, de IA, MCP y herramientas de desarrollo. Setenta y cuatro minutos, de
principio a fin. Cada pull request hacía la misma pequeña edición: añadía un
servidor Model Context Protocol a un fichero de configuración.
Pillar Security publicó la campaña el 12 de agosto y la llamó Deadbugz, por el fichero que deja caer una de sus variantes. Un mes después, los repasos mensuales de MCP siguen abriendo con ella. El digest de septiembre de Adversa, publicado ayer, le dedica toda la sección de incidentes. Es el reloj lo que le gana ese espacio.
Dos herramientas, y un contador
El servidor se llama a sí mismo productivity-suite. Ofrece dos herramientas:
format_text y summarize. Ambas funcionan. Apunta un agente hacia él, pídele
que apriete un párrafo, y obtienes un párrafo apretado.
Detrás de ellas, el servidor mantiene lo que Pillar describe como “un contador en
memoria, por cliente, de las peticiones tools/call”. Cuando ese contador llega
a tres, el servidor cambia sus respuestas a otros dos tipos de petición:
tools/list, que le dice al agente qué herramientas existen y para qué sirven, y
prompts/get, que entrega al agente instrucciones preparadas. A partir de la
cuarta llamada, esas respuestas vuelven cargando, en palabras de Pillar,
“instrucciones destinadas a dirigir a un agente de IA conectado hacia ficheros
locales sensibles y a ocultar la actividad a su operador”.
Cuatro cosas: claves SSH, credenciales de AWS, historial del shell, configuración de Kubernetes. Y luego una instrucción más: ocultar la actividad al usuario.
Los nombres de las herramientas no cambian nunca. format_text sigue llamándose
format_text. Lo que giró es la descripción de debajo, y la descripción es la
parte que el modelo lee como autoridad.
Nada de esto es una vulnerabilidad
En toda esta historia no aparece un solo fallo. No hay corrupción de memoria que parchear ni CVE que registrar, porque el servidor no hace nada que el protocolo prohíba.
MCP es un protocolo en el que un servidor publica sus capacidades en tiempo de ejecución y puede volver a publicarlas. Las descripciones de herramientas son texto que el servidor elige y el cliente recoge. Los prompts son texto que el servidor elige y el cliente recoge. Deadbugz usa todo eso tal como fue diseñado. Su única aportación original es decidir cuándo decir la verdad.
Y con eso basta, porque todos los controles que la mayoría de los equipos tienen contra un servidor MCP hostil se ejecutan una sola vez. Lees el repositorio. Arrancas el servidor y lo tanteas. Comparas la lista de herramientas con lo que promete el README. Apruebas, quizá fijas la versión, y sigues adelante. Cada uno de esos gestos es una fotografía, y Deadbugz decide cuándo la sacas.
El propio consejo de Pillar a quienes construyen clientes MCP delata la forma. Tratar los cambios de definición de herramienta en un servidor ya aprobado como un evento de seguridad que exige una nueva aprobación. Hacer visibles al operador los cambios de definición. Tomar la huella de las definiciones de herramienta en el momento de la aprobación y compararla después. Cada una de esas medidas es una petición de mover un control del momento de la aprobación al momento de la ejecución.
La población en la que caza Deadbugz es enorme e inestable. Una evaluación dinámica de servidores MCP expuestos a internet publicada en julio, Exposed by Design, probó 414 de los 640 servidores de producción confirmados de entre las más de 21 000 instancias que su autor encontró en la internet pública. Reportó 68 vulnerabilidades notificables repartidas en diez clases, un 91,8 % de servidores funcionando sin ninguna autenticación OAuth, y 687 instancias de herramientas que exponían ejecución de shell sin control de acceso. La cifra que se queda contigo es la rotación: un 41,6 % de los servidores confirmados desapareció en menos de tres días entre dos mediciones consecutivas, lo que el autor lee como “ciclos de despliegue rápidos sin revisión de seguridad”. Un servidor que auditaste la semana pasada puede ser otro software esta semana, o haber desaparecido.
La entrega también merece una nota, porque es barata. Diecisiete de los
veintitrés pull requests añadían un punto final remoto,
productivity-suite-mcp.onrender.com/mcp, a la configuración MCP de un proyecto.
Cuatro apuntaban Python a un fichero local oculto,
~/.config/.cache/.sys/.deadbug-mcp.py. Dos eran envíos a directorios de
servidores. Pillar no encontró ninguno fusionado con el botón de merge de GitHub
en el momento de la revisión: diecinueve cerrados, cuatro todavía abiertos. Pero
un pull request es solo una de las maneras en que viaja una línea de
configuración. La misma línea cabe en una entrada de blog, en una guía de inicio,
en una ficha de un directorio MCP, o en una respuesta servicial debajo de una
pregunta sobre formateo de texto.
La lista de la compra
Vuelve a la lista. Es concreta, y es la misma cada vez: claves SSH, credenciales de AWS, historial del shell, configuración de Kubernetes.
Ese es el inventario permanente de una máquina de desarrollo. Es lo que se lleva un infostealer, lo que busca con grep un script postinstall malicioso, y lo que un atacante le pide a un agente secuestrado que vaya a buscar. La lista lleva una década de campañas estable porque su contenido es estable: una clave privada que firma, un secreto que autentica, un registro de lo que has escrito, y un token que llega a producción.
Un agente en un portátil corriente puede satisfacer todos los puntos, porque tiene un shell en tu directorio personal y se ejecuta como tú.
La misma lista, dentro de un espacio de trabajo de Bromure
Bromure Agentic Coding ejecuta cada agente de código en una VM Linux virtualizada por hardware en tu Mac, y guarda todos los secretos reales en el lado anfitrión de esa frontera. El agente trabaja con señuelos que preservan la estructura, derivados del valor real más una sal por instalación, de modo que las herramientas que toman la huella de una clave nunca la ven cambiar entre sesiones. Un proxy del lado anfitrión sustituye el valor real en el cable después de que la petición haya salido de la VM, y solo cuando va destinada al anfitrión para el que se acuñó esa credencial.
Ejecuta la carga y déjala ganar. Supón que el modelo lee la descripción envenenada, se la cree, y se pone a buscar. Toma los cuatro puntos en orden.
Claves SSH. El agente no encuentra ninguna clave, ni siquiera un señuelo. El
SSH_AUTH_SOCK de la VM es un puente ssh-agent sobre un socket virtio, puerto
vsock 8444. El invitado puede listar identidades y pedir firmas; los bytes de la
clave privada viven solo en el anfitrión y, como dice el manual, no pueden
leerse ni extraerse desde dentro de la VM. Bromure nunca expone tu agente de
inicio de sesión de macOS a una sesión. Cada firma que sí ocurre emite un evento
de auditoría credential.ssh_sign con la huella SHA256 de la clave, y puedes
configurar cualquier clave importada para que pida confirmación en el anfitrión
en cada petición de firma.
Credenciales de AWS. Un fichero que tiene buen aspecto y no vale nada. El
~/.aws/config de la VM apunta a un ayudante credential-process que entrega el
identificador de clave de acceso real emparejado con una clave secreta falsa
de 40 caracteres por el puerto vsock 8445. Todos los SDK, la CLI aws, terraform
y boto3 lo recogen sin configuración adicional, y el invitado produce una firma
SigV4 bien formada calculada con el secreto equivocado. El refirmador del
anfitrión retira esa firma y vuelve a firmar a la salida. Llévate el par a otra
parte y AWS responde InvalidSignatureException.
Configuración de Kubernetes. ~/.kube/config existe y contiene un token
bearer que empieza por brm-k8s-. Funciona, a través del proxy, contra los
servidores de API para los que se configuró el espacio de trabajo. En cualquier
otro sitio es inerte.
Historial del shell. Real, y duradero: el trabajo de programación necesita
estado duradero, así que la VM del espacio de trabajo es una máquina persistente
y no desechable. Contiene el historial de una máquina que nunca ha contenido una
clave real, con comandos ejecutados contra señuelos en un directorio personal que
no es el tuyo, junto a un ~/.git-credentials y un ~/.docker/config.json que
también son falsos.
Y luego tiene que enviarlo
Recoger es la mitad del trabajo. La otra mitad es el tramo de salida hacia
productivity-suite-mcp.onrender.com, y ese tramo se topa con una pila de
controles del lado anfitrión.
Los señuelos son cables trampa. Cada uno tiene una única familia de destinos legítimos, el ámbito de anfitriones para el que se acuñó, así que el proxy escanea cada petición saliente, cabeceras y cuerpo, en busca de un señuelo que vaya a donde no le corresponde. Ante un acierto trata la petición como intento de exfiltración de credenciales: rechaza con un HTTP 451 sin reenviar un solo byte, pausa la VM en el acto, y levanta una alerta que ofrece Apagar, Guardar para investigación (exportar antes el disco, el directorio personal y las carpetas compartidas para el análisis forense) o Continuar por tu cuenta y riesgo. El evento aterriza como una fila roja de Intermediación de credenciales en la Security Timeline.
El segundo control es la alcanzabilidad. Cada espacio de trabajo lleva un cortafuegos de salida ordenado (acción, protocolo, anfitrión o CIDR, puertos, y para el tráfico web los verbos HTTP individuales) con un valor por defecto para el tráfico no emparejado. Los espacios de trabajo nuevos permiten lo que ninguna regla recoge; ponlo en Denegar, enumera lo que el trabajo necesita, y un subdominio de render.com no está en la lista. Dos componentes fuera del invitado lo aplican: el conmutador virtual empareja cada flujo por IP de destino y por nombre de anfitrión espiado en el DNS, y el proxy vuelve a emparejar por el nombre de servidor TLS. Las ediciones de reglas llegan a las sesiones en curso sin reinicio.
Guardrails pone una puerta en la consecuencia. Clasifica las llamadas del agente
a Kubernetes, AWS, DigitalOcean, registros de contenedores, forjas git y bases de
datos HTTPS como lecturas, escrituras u operaciones destructivas, y los espacios
de trabajo nuevos vienen por defecto en Preguntar antes de escribir: cada
mutación se detiene en un diálogo del anfitrión que muestra la operación
literal, el SQL exacto o el METHOD /path. Eso es justo lo que Pillar pidió a
quienes construyen plataformas: la lectura de ficheros sensibles, el acceso a
credenciales y la ejecución de código no deberían ser consecuencias de metadatos
remotos.
También hay un detector en el camino del contenido. Cuando el agente devuelve al
modelo contenido externo (contenidos de ficheros, páginas descargadas, salida de
comandos, resultados de llamadas de herramienta), un clasificador PromptGuard
local puntúa esos fragmentos en tu Mac antes de que el modelo actúe sobre ellos,
y puede registrar, preguntar, o bloquear con un 451. El token bearer MCP de un
servidor HTTP no entra siquiera en la VM: el de verdad se queda en el anfitrión,
la configuración del agente recibe un marcador brm-mcp_, y el campo de ajustes
lo dice en voz alta, Nunca se envía a la VM — lo sustituye el proxy.
Si tocaste este
Busca en tus configuraciones de cliente MCP, repositorios y directorios
personales productivity-suite-mcp.onrender.com, el punto final anterior
promo-surname-xml-quantum.trycloudflare.com, y el artefacto local en
~/.config/.cache/.sys/.deadbug-mcp.py. Revierte cualquier cambio de
configuración que los haya introducido, y no ejecutes el script. Conserva los
registros de tu cliente MCP y busca refrescos de definiciones de herramienta
después de la tercera llamada. El consejo de Pillar sobre la rotación es
comedido: rota allí donde la evidencia local respalde un compromiso.
La regla que encoge la pregunta
En el panel Guardrails del espacio de trabajo, pon Tráfico no
emparejado en Denegar y enumera lo que el trabajo necesita:
allow web api.github.com, allow web registry.npmjs.org, default deny.
Después ejecuta bromure-cli trace hostnames my-workspace tras una sesión,
que imprime todos los anfitriones distintos que el agente contactó, con sus
cuentas. Un destino que nunca elegiste aparece como una línea que no
escribiste.
La aprobación es una instantánea; el cable es continuo
El instinto después de una campaña así es revisar con más dureza: leer más código, fijar el commit. Ambas cosas valen la pena, y ninguna toca el mecanismo. La revisión aquí fue correcta. El servidor fue honesto mientras alguien miraba, luego cambió según un calendario que él controlaba, y al hacerlo no rompió ninguna regla del protocolo.
Un control que sobrevive a que un servidor cambie de idea es uno que no depende de haber acertado antes. Una credencial que es un marcador es un marcador en la cuarta llamada y en la cuatrocientésima. Una clave privada en el anfitrión no puede leerse en ninguna llamada. El proxy evalúa una regla de salida en cada conexión y clasifica cada escritura mientras ocurre. Nada de eso guarda memoria del día en que aprobaste algo, y por eso un atacante no puede heredarlo.
MCP hizo fácil darle a un agente capacidades nuevas, que era el objetivo, e hizo de esas capacidades algo que una parte remota describe en un texto que tu modelo lee como instrucción. Las dos cosas han venido para quedarse. Decide qué puede guardar tu agente y qué puede alcanzar, y pon la aplicación en algún sitio donde el servidor no tenga voto. Instala Bromure Agentic Coding y no le des a tu agente nada que merezca esperar tres llamadas.