Ilustración técnica abstracta sobre un fondo azul marino oscuro: un puerto de entrada resplandeciente en la pared izquierda de una caja translúcida admite una única línea fina y pálida que no lleva ninguna insignia ni ninguna llave. Dentro de la caja, la línea se encuentra con una pila de pequeños paneles redondeados y se ramifica en una breve fila de trazos angulares similares a comandos que descienden hasta un compartimento inferior que contiene un grupo de fichas resplandecientes en forma de llave. Junto al puerto de entrada hay una segunda puerta cerrada delineada en azul pálido. Sin texto, sin personas, sin logotipos de organizaciones.
AI Safety Incidents

Una única solicitud POST no autenticada a la API de administración de Bifrost inició un comando como el usuario de la puerta de enlace

Autor

Magnus Corvin

Fecha de publicación

Últimos modelos · 20Ver todos los modelos →
Benchmarks: Artificial Analysis · actualizado a diario
Volver a todas las publicaciones

El 14 de septiembre de 2026, JFrog Security Research publicó CVE-2026-90898, una falla de ejecución de comandos en Bifrost —la puerta de enlace de IA de código abierto mantenida por Maxim— y le asignó una puntuación de 9,8 sobre 10. El ataque fue una sola solicitud HTTP. Una solicitud POST /api/mcp/client no autenticada registra un cliente MCP stdio, que Bifrost interpreta como un nombre de programa más una lista de argumentos y lo inicia de inmediato, antes de cualquier handshake MCP, como el usuario que ejecuta la puerta de enlace; en la imagen oficial, ese usuario es appuser. Una puerta de enlace también es donde viven las claves de proveedor y las claves virtuales de un operador, por lo que un proceso que se ejecute allí llega a las credenciales. Lo que hacía anónima la solicitud era un valor predeterminado incluido de fábrica: governance.auth_config.is_enabled es false y, en palabras de JFrog, «con la autenticación desactivada, todo llamador es un administrador local». Ninguna fuente citada informa que la falla haya sido explotada. Las propias fuentes del registro, la calificación de gravedad y la evaluación de confianza se encuentran en el archivo de incidentes de IA, junto con las demás vulnerabilidades documentadas de puertas de enlace de IA y MCP del último año.

Bifrost es un único servicio que se sitúa entre una aplicación y los modelos de lenguaje a los que llama. Su repositorio lo describe como una puerta de enlace de código abierto con licencia Apache-2.0 que "unifica el acceso a 23+ proveedores ... mediante una única API compatible con OpenAI", con proxy MCP, claves virtuales, presupuestos y equilibrio de carga por proveedor entre sus funciones, y tenía aproximadamente 9.150 estrellas en GitHub el 28 de octubre de 2026. Esa combinación es exactamente la razón por la que una shell en este host en particular merece una puntuación crítica: el proceso que enruta las solicitudes es también el proceso que guarda las credenciales de cada upstream al que enruta. The Hacker News, al informar del fallo el 22 de septiembre, expuso la consecuencia sin rodeos: como la puerta de enlace almacena claves de API de cada proveedor conectado, ejecutar comandos en el proceso de la puerta de enlace otorga al atacante acceso a esas credenciales.

Todo el ataque fue un solo POST

El aviso de JFrog, publicado con la referencia JFSA-2026-001686326, describe un endpoint que existe para permitir a un administrador añadir un servidor MCP. Un cliente stdio en la especificación MCP no es una URL; es un comando y sus argumentos, que el programa anfitrión debe ejecutar. Bifrost acepta esa configuración a través de la API de administración e inicia el programa en cuanto se añade el cliente. No tiene que completarse primero ninguna negociación de capacidades, ninguna comprobación de identidad del servidor ni ningún handshake; el aviso es explícito en que el handshake no es lo que inicia el proceso.

La prueba de concepto del aviso es un cuerpo JSON corto enviado a una puerta de enlace con la autenticación dejada en su valor predeterminado. Registra un cliente cuyo comando es /bin/sh y cuyos argumentos escriben un archivo marcador, y luego vuelve a leer el marcador. JFrog añade un detalle operativo que importa durante la respuesta a incidentes: la solicitud HTTP puede expirar mientras Bifrost espera un handshake que nunca llega, y un tiempo de espera agotado en los registros no significa que el comando no se haya iniciado. Para ese momento, el proceso ya se está ejecutando.

El registro de CVE, asignado por JFrog como Autoridad de Numeración de CVE, asigna el defecto a CWE-306, falta de autenticación para una función crítica, y CWE-284, control de acceso inadecuado, y nombra los dos archivos que contienen el controlador y su middleware: transports/bifrost-http/handlers/mcp.go y transports/bifrost-http/handlers/middlewares.go. El módulo afectado es github.com/maximhq/bifrost/transports, en Linux y macOS, para todas las versiones anteriores a la 2.1.0.

El valor predeterminado que hizo anónima la solicitud

Nada de eso sería explotable sin una segunda condición, y la segunda condición es el propio valor predeterminado del producto. El middleware de autenticación de administración de Bifrost está condicionado por governance.auth_config.is_enabled, que se entrega como false. Con esa opción desactivada, el middleware trata a cada solicitante como administrador local —el propio resumen del aviso es que, con la autenticación desactivada, cada solicitante es un administrador local—, por lo que la comprobación que habría detenido la solicitud nunca estuvo en la ruta.

La corrección, el pull request #6757, se fusionó en el proyecto el 2 de septiembre de 2026, doce días antes de que se publicara la CVE y seis días antes de la versión que la incluye. Su descripción es inusualmente franca sobre la forma del problema: el pull request cierra dos primitivas de acceso no autenticado en la misma API de registro, no una. La primera es el cliente stdio. La segunda es un cliente MCP http o sse apuntado a una dirección de loopback o de red privada, que la solicitud denomina una primitiva SSRF contra servicios internos, «incluido el endpoint de metadatos en la nube 169.254.169.254». Ahora ambas se rechazan con HTTP 403 cuando la solicitud llega al controlador sin una comprobación real de credenciales. Los administradores autenticados conservan ambas capacidades, y los operadores todavía pueden aprovisionar clientes stdio mediante config.json.

Esa segunda primitiva no aparece en el texto de la CVE. Está en el parche, y es la razón por la que la corrección es una barrera en lugar de una eliminación: la funcionalidad nunca fue el error. Tratar a un llamador no autenticado como administrador sí lo era.

Dónde estaba escuchando realmente la API de administración

La alcanzabilidad es la parte del registro que las fuentes describen con mayor consistencia y miden menos. El aviso de JFrog indica que el valor predeterminado del binario estándar es localhost:8080, lo que confina a un atacante al propio host. El aviso del mismo equipo para el fallo complementario afirma que la imagen oficial de Docker se vincula a 0.0.0.0 en su lugar, y The Hacker News lo repite: la imagen hace que la API de administración sea accesible desde fuera del contenedor si se publica el puerto. El inicio rápido del propio README de Bifrost ejecuta «docker run -p 8080:8080 maximhq/bifrost», y la opción -p de Docker publica un puerto del contenedor en todas las interfaces del host a menos que se especifique una dirección. El aviso de GitHub sobre el fallo registra que la ruta reportada se verificó con la imagen publicada maximhq/bifrost:latest, donde una solicitud no autenticada lanzó /bin/sh y escribió un marcador como appuser.

Lo que ninguna fuente proporciona es una cifra. No hay un recuento de instancias de Bifrost expuestas a internet, ni resultados de escaneos, ni telemetría, ni víctimas. El registro no refleja ningún daño real, y esa ausencia es un dato sobre la información publicada más que una garantía tranquilizadora: la clase de vulnerabilidad solo es peligrosa cuando coinciden dos condiciones, un servicio a la escucha expuesto y un valor predeterminado sin modificar, y nadie ha publicado con qué frecuencia coincidieron.

Un endpoint, dos fallos, una causa raíz

Ocho días antes del aviso de MCP, el 6 de septiembre de 2026, el mismo equipo de investigación publicó CVE-2026-86242, una segunda vía no autenticada hacia el mismo proceso. Descubierta por Or Peles y con una puntuación de 8.1, aprovecha POST /api/plugins: Bifrost acepta un plugin personalizado habilitado cuya ruta es una URL HTTP, el cargador de objetos compartidos trata una ruta con prefijo http como una descarga, escribe el cuerpo en un archivo .so temporal y entrega ese archivo a plugin.Open de Go. Si la carga se realiza correctamente, una función Init opcional se ejecuta inmediatamente con la configuración del atacante, como el usuario de la puerta de enlace.

La variante de compilación determina qué obtiene un atacante. En las compilaciones enlazadas dinámicamente que el proyecto documenta para plugins de Go personalizados, se espera que la carga tenga éxito, lo que el aviso denomina ejecución remota de código no autenticada. En la imagen Docker enlazada estáticamente publicada, plugin.Open falla con "Dynamic loading not supported", por lo que la misma solicitud se degrada a falsificación de solicitud del lado del servidor. Un plugin cargable también debe coincidir con la versión de Go, el sistema operativo, la arquitectura y el tipo de enlace del host, lo cual es una restricción real y no una formalidad. La corrección es transports/v2.0.0, mediante la pull request #5763, y el rango afectado es todo lo anterior a ella.

La trampa está en el orden de publicación. v2.0.0 corrige la ruta del plugin y no corrige la ruta de MCP; The Hacker News lo dice explícitamente, y el aviso de MCP de JFrog confirma que v2.0.0 todavía permite el registro stdio sin autenticación. Un operador que aplicó el parche con prontitud tras el aviso del 6 de septiembre seguía expuesto, solo por ese punto, hasta que se publicó transports/v2.1.0 el 8 de septiembre. Ambos fallos se reducen a la misma frase en ambos avisos y en la propia pull request del proveedor: la autenticación deshabilitada es la configuración predeterminada.

Lo que las fuentes establecen y lo que no

El mecanismo, el rango de versiones, la corrección y el reconocimiento coinciden en el aviso de seguridad, el registro CVE que asignó JFrog, la solicitud de incorporación de cambios y las notas de la versión del propio proyecto, y The Hacker News. La discrepancia restante es sobre la gravedad, y es real.

El 9,8 es la puntuación de JFrog como CNA asignante, y es la puntuación que mantiene NVD: el registro de NVD incluye reefs@jfrog.com como fuente de la métrica y, a fecha de 28 de octubre de 2026, sigue mostrando la entrada en estado «Awaiting Analysis», lo que significa que NVD ha publicado el vector del asignador en lugar de su propio análisis. La evaluación SSVC del Coordinador de CISA adjunta al mismo registro, con marca temporal de 14 de septiembre de 2026, indica explotación «none», automatizable «yes» e impacto técnico «total». El modelo EPSS de FIRST situó la probabilidad de explotación en los próximos 30 días en 2,3 por ciento el 27 de octubre de 2026, en el percentil 83 entre todas las CVE puntuadas, y ni esta CVE ni CVE-2026-86242 aparecen en el catálogo Known Exploited Vulnerabilities de CISA con fecha de 27 de octubre de 2026.

Frente a ello, Maxim cuestiona la calificación. En una declaración a The Hacker News, Akshay Deo, director de tecnología y cofundador de la empresa, afirmó que el riesgo práctico es menor de lo que sugiere la puntuación crítica, porque la explotación requiere que se pueda acceder a la interfaz de gestión desde una red no confiable y que el operador haya dejado la autenticación sin configurar; la implementación predeterminada y documentada, dijo, coloca la puerta de enlace dentro de una red privada. The Hacker News informa que Maxim considera que la falla es de baja gravedad y está trabajando con JFrog, que asignó el CVE, para revisar la evaluación. Esa declaración aparece en ese único medio; no se encontró ninguna otra publicación que citara la postura de Maxim, y no se había publicado ninguna revisión hasta el 28 de octubre de 2026 —el registro CVE se actualizó por última vez el 14 de septiembre y la entrada de NVD el 18 de septiembre, ambos todavía en 9.8.

Hay otros dos vacíos que merecen nombrarse en lugar de disimularlos. Los avisos de GitHub del proyecto, publicados el 23 de septiembre de 2026 y que acreditan al investigador de JFrog como informante, siguen otorgando 9.8 al fallo de MCP y 8.1 al fallo del complemento, sin rastro de la revisión que describió Maxim. Y los rangos afectados de esos avisos están expresados en función de versiones de módulo de Go —inferiores a 1.5.27 e inferiores a 1.6.3— en lugar de las etiquetas de versión de transports que utilizan los avisos de JFrog, así que un operador que coteje un documento con el otro tiene que conciliar dos esquemas de numeración antes de decidir si una compilación se ve afectada.

Rendered two-column card with the kicker 'BIFROST · CVE-2026-90898' and the title 'What the sources establish — and what they do not', subtitled 'Sources: JFrog JFSA-2026-001686326 (14 September 2026), the CVE record assigned by JFrog, NVD, FIRST EPSS, the CISA KEV catalogue, and the project's pull request 6757 and release tags.' The left column, headed 'ESTABLISHED' in blue, lists eight items: score 9.8 with the vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H supplied by the CNA; CWE-306 missing authentication with CWE-284 access control; unauthenticated POST /api/mcp/client starting a program as the gateway user; affected below transports 2.1.0 when management auth is disabled, the default; fixed in transports 2.1.0, PR 6757 merged 2 September, released 8 September 2026; CISA SSVC of 14 September 2026 with exploitation none, automatable yes, impact total; EPSS 2.3 percent at the 83rd percentile on 27 October 2026; and not in the CISA KEV catalogue dated 27 October 2026. The right column, headed 'NOT ESTABLISHED, OR CONTESTED' in amber, lists eight items: no exploitation reported, and the archive records real harm as false; no count of exposed or affected deployments in any cited source; severity contested, the vendor's CTO telling The Hacker News it is low; no published revision to the score as of 28 October 2026; NVD still Awaiting Analysis, so the 9.8 is the assigner's and not NVD's; affected ranges stated in two numbering schemes across the advisories; no detection or triage guidance in either advisory; and no evidence tying the flaw to any intrusion. A footer reads 'Card rendered by OrcaRouter'. The OrcaRouter logo is composited in the bottom-right corner.

Qué cambió y qué se les dijo a los operadores que hicieran

La cronología va hacia atrás desde la divulgación, que es la parte menos intuitiva del registro. La pull request #6757 se fusionó el 2 de septiembre de 2026. transports/v2.1.0, la versión que el aviso señala como la primera que contiene la corrección, se publicó el 8 de septiembre — seis días antes de que CVE-2026-90898 se publicara el 14 de septiembre. La corrección del plugin del proyecto precedió a su propio aviso de la misma manera: transports/v2.0.0 se lanzó el 26 de agosto, once días antes del CVE del 6 de septiembre. Los operadores que siguen las versiones en lugar de los feeds tenían los parches antes de tener las advertencias.

Lo que ofrece la actualización es acotado y específico. En 2.1.0 y versiones posteriores, un registro stdio no autenticado devuelve 403 y el comando no se inicia; los administradores autenticados conservan la capacidad y el aprovisionamiento mediante config.json sigue funcionando. La medida provisional del aviso para quienes no puedan actualizar es establecer governance.auth_config.is_enabled en true, usar credenciales de administrador robustas y mantener el listener de administración fuera de redes no confiables. La instrucción de JFrog para las instancias que se ejecutaron expuestas con la autenticación deshabilitada no es investigar y esperar que todo esté bien: hay que tratarlas como comprometidas y rotar las claves virtuales y las credenciales del proveedor.

La aritmética de versiones es la parte que más probablemente se malinterprete. v2.0.0 no corrige este defecto. La línea 1.6.x hasta 1.6.11 tampoco contiene la corrección, y la etiqueta más reciente de la serie 1.6 del proyecto es transports/v1.6.11, publicada el 14 de agosto de 2026, sin ninguna línea 1.7 en el repositorio a fecha de 28 de octubre de 2026 — por lo que no hay ninguna versión parcheada dentro de esa serie, y un operador de 1.6.x tiene que pasar a 2.1.0 o posterior en lugar de esperar un backport. La línea de versión actual está muy por delante de ese punto: transports/v2.3.2 se publicó el 20 de octubre de 2026.

Rendered release-timeline card with the kicker 'BIFROST · RELEASE TIMELINE', the title 'The fix shipped six days before the advisory' and the subtitle 'Dates from the project's release tags and pull request metadata, the JFrog advisories and The Hacker News. Every date below is a single day.' A horizontal timeline carries seven dated markers: 26 Aug 2026, transports 2.0.0 published; 2 Sep 2026, PR 6757 merged; 6 Sep 2026, CVE-2026-86242 published, 8.1; 8 Sep 2026, transports 2.1.0, the fix; 14 Sep 2026, CVE-2026-90898 published, 9.8; 22 Sep 2026, vendor statement reported; 23 Sep 2026, GitHub advisories published. Below it, three bars show release lines: a hatched amber bar labelled '1.6.x line' reading 'affected — no patched release in the series; newest tag 1.6.11, 14 August 2026'; a bar labelled '2.0.0' split between a pale blue segment reading 'plugin flaw fixed' and a red segment reading 'MCP flaw still open'; and a solid blue bar labelled '2.1.0 and later' reading 'MCP fix — unauthenticated stdio registration returns 403'. A foot band headed 'THE PART THAT IS EASY TO GET WRONG' notes that 2.0.0 was the patched release on 6 September and did not close the MCP path, that for the two days before 2.1.0 an operator who patched on the day of the warning was still exposed, and that the 1.6.x series has no fixed release at all. A footer reads 'Card rendered by OrcaRouter'. The OrcaRouter logo is composited in the bottom-right corner.

Después de la publicación ocurrieron dos cosas que un defensor puede aprovechar. Los avisos de seguridad de GitHub del proyecto —GHSA-86gf-xh3g-rvxq para la falla de MCP, de gravedad crítica, y GHSA-2qp8-4xgm-fw6g para la falla del plugin, de gravedad alta— se publicaron el 23 de septiembre de 2026, lo que da a los feeds de dependencias y de avisos un segundo identificador con el que hacer coincidencias. Y el 13 de octubre de 2026 se integró una plantilla de detección para CVE-2026-90898 en el repositorio público nuclei-templates, lo que significa que la comprobación de un intento de registro no autenticado ahora es algo que un pipeline de escaneo puede ejecutar, en lugar de algo que cada equipo tiene que escribir.

Abstract illustration on a dark navy field: a wide translucent slab representing a credential store, filled with evenly spaced glowing key-shaped tokens, stands behind a low wall. A narrow gateway aperture in the wall holds a fresh, brighter set of tokens, a blank card-shaped slip lies pushed aside on the ground in front of it, and a thin amber arc sweeps from the aperture around to the store. A second gateway aperture in the background remains closed and unlit. No text, no people, no organisation logos.

Lo que queda abierto es la gravedad, y se resolverá o no por las dos organizaciones que todavía siguen hablando del asunto. La posición de Maxim, según lo informado, es que un 9.8 sobreestima una falla que necesita un listener expuesto y un valor predeterminado sin configurar para tener relevancia — una afirmación que es comprobable en principio y no se ha probado en público, porque nadie ha publicado cuántas implementaciones cumplían esas dos condiciones. La posición de JFrog es la puntuación que asignó como CNA, que sigue vigente en el registro CVE y en el NVD. Hasta que uno de ellos publique una revisión, la lectura operativa es la misma en cualquier caso: la falla es real y se verificó de extremo a extremo en la imagen de contenedor publicada, la corrección se publicó antes de la advertencia, la serie 1.6 no tiene ninguna corrección, y cualquier instancia que se haya ejecutado con la autenticación deshabilitada en un listener accesible debe tratarse como un incidente de credenciales en lugar de un parche para programar.