Абстрактная техническая иллюстрация на тёмно-синем фоне: светящийся входной порт на левой стенке полупрозрачного блока пропускает одну тонкую бледную линию, не несущую ни бейджа, ни ключа. Внутри блока линия встречает стопку небольших скруглённых панелей и разветвляется в короткий ряд угловатых штрихов, похожих на команды, которые уходят вниз в нижний отсек, где находится скопление светящихся токенов в форме ключа. Рядом с входным портом расположен второй закрытый шлюз, обведённый бледно-голубым. Без текста, без людей, без логотипов организаций.
AI Safety Incidents

Один неаутентифицированный POST-запрос к Management API Bifrost запустил команду от имени пользователя шлюза

Автор

Magnus Corvin

Дата публикации

Новые модели · 20Все модели →
Бенчмарки: Artificial Analysis · обновляется ежедневно
Назад ко всем статьям

14 сентября 2026 года JFrog Security Research опубликовала CVE-2026-90898, уязвимость выполнения команд в Bifrost — открытом AI-шлюзе, поддерживаемом Maxim, — и оценила её в 9,8 из 10. Атака представляла собой один HTTP-запрос. Неаутентифицированный POST /api/mcp/client регистрирует stdio-клиент MCP, который Bifrost интерпретирует как имя программы плюс список аргументов и запускает немедленно, до какого-либо рукопожатия MCP, от имени пользователя, запускающего шлюз; в официальном образе этот пользователь — appuser. Кроме того, в шлюзе хранятся ключи провайдеров и виртуальные ключи оператора, поэтому процесс, запущенный там, получает доступ к учётным данным. Анонимным запрос сделала поставляемая по умолчанию настройка: governance.auth_config.is_enabled имеет значение false, и, по формулировке JFrog, «отключённая аутентификация означает, что каждый вызывающий является локальным администратором». Ни один из цитируемых источников не сообщает, что уязвимость когда-либо эксплуатировалась. Собственные источники записи, оценка серьёзности и оценка уверенности находятся в архиве инцидентов ИИ, наряду с другими задокументированными уязвимостями AI-шлюзов и MCP за прошедший год.

Bifrost — это единый сервис, который располагается между приложением и языковыми моделями, к которым оно обращается. В его репозитории он описывается как шлюз с открытым исходным кодом под лицензией Apache-2.0, который «объединяет доступ к 23+ провайдерам ... через единый API, совместимый с OpenAI»; среди его возможностей — проксирование MCP, виртуальные ключи, бюджеты и балансировка нагрузки по каждому провайдеру, а по состоянию на 28 октября 2026 года у него было примерно 9 150 звёзд на GitHub. Именно это сочетание — главная причина, по которой оболочка на этом конкретном хосте заслуживает критической оценки: процесс, маршрутизирующий запросы, — это тот же процесс, который хранит учётные данные для каждого апстрима, к которому он направляет запросы. The Hacker News, сообщивший об уязвимости 22 сентября, прямо изложил последствие: поскольку шлюз хранит API-ключи для каждого подключённого провайдера, выполнение команд в процессе шлюза даёт атакующему доступ к этим учётным данным.

Вся атака представляла собой один POST-запрос.

Уведомление JFrog, опубликованное под идентификатором JFSA-2026-001686326, описывает конечную точку, предназначенную для того, чтобы администратор мог добавить MCP-сервер. stdio-клиент в спецификации MCP — это не URL, а команда и её аргументы, которые, как предполагается, должна запускать хост-программа. Bifrost принимает эту конфигурацию через management API и запускает программу сразу после добавления клиента. Ни согласование возможностей, ни проверка идентичности сервера, ни успешное рукопожатие не требуются предварительно — в уведомлении прямо указано, что процесс запускает не рукопожатие.

Proof of concept в уведомлении — это короткое JSON-тело, отправляемое на шлюз, при этом аутентификация оставлена со значением по умолчанию. Оно регистрирует клиента, команда которого — /bin/sh, а аргументы записывают файл-маркер, после чего маркер считывается обратно. JFrog добавляет операционную деталь, важную при реагировании на инциденты: HTTP-запрос может завершиться по тайм-ауту, пока Bifrost ждёт рукопожатия, которое так и не происходит, и тайм-аут в журналах не означает, что команда не запустилась. К этому моменту процесс уже выполняется.

Запись CVE, назначенная JFrog как органом по нумерации CVE, относит дефект к CWE-306, отсутствие аутентификации для критической функции, и CWE-284, неправильный контроль доступа, и указывает два файла, содержащие обработчик и его промежуточное ПО: transports/bifrost-http/handlers/mcp.go и transports/bifrost-http/handlers/middlewares.go. Затронутый модуль — github.com/maximhq/bifrost/transports, на Linux и macOS, для всех версий ниже 2.1.0.

Значение по умолчанию, которое сделало запрос анонимным

Ничто из этого не было бы эксплуатируемым без второго условия, а второе условие — это собственное значение продукта по умолчанию. Промежуточное ПО аутентификации управления Bifrost управляется параметром governance.auth_config.is_enabled, который поставляется со значением false. Когда он отключён, это промежуточное ПО рассматривает каждого вызывающего субъекта как локального администратора — в самом резюме рекомендации сказано, что при отключённой аутентификации каждый вызывающий субъект является локальным администратором — поэтому проверка, которая должна была остановить запрос, вообще не находилась на пути выполнения.

Исправление, pull request #6757, было включено в проект 2 сентября 2026 года — за двенадцать дней до публикации CVE и за шесть дней до выпуска, в который оно вошло. Его описание на удивление откровенно говорит о сути проблемы: pull request закрывает два примитива неаутентифицированного доступа в одном и том же API регистрации, а не один. Первый — это stdio-клиент. Второй — это http- или sse-клиент MCP, направленный на loopback- или частный сетевой адрес, который в запросе назван SSRF-примитивом против внутренних сервисов, «включая конечную точку метаданных облака 169.254.169.254». Оба теперь отклоняются с HTTP 403, когда запрос доходит до обработчика без настоящей проверки учетных данных. Аутентифицированные администраторы сохраняют обе возможности, а операторы по-прежнему могут создавать stdio-клиенты через config.json.

Этот второй примитив не упоминается в тексте CVE. Он есть в патче, и именно поэтому исправление — это ограничитель, а не удаление: сама функция никогда не была ошибкой. Ошибкой было считать неаутентифицированного вызывающего администратором.

Где на самом деле прослушивался API управления

Достижимость — это та часть сведений, которую источники описывают наиболее согласованно, а измеряют меньше всего. В уведомлении JFrog по умолчанию для штатного бинарного файла указан localhost:8080, что ограничивает атакующего самим хостом. В уведомлении той же команды о сопутствующей уязвимости говорится, что официальный образ Docker вместо этого привязан к 0.0.0.0, и The Hacker News повторяет это: образ делает management API доступным извне контейнера, если порт опубликован. В собственном быстром старте из README Bifrost используется команда "docker run -p 8080:8080 maximhq/bifrost", а параметр -p в Docker публикует порт контейнера на всех интерфейсах хоста, если адрес не указан. В уведомлении GitHub об этой уязвимости указано, что описанный сценарий был проверен на опубликованном образе maximhq/bifrost:latest, где неаутентифицированный запрос запустил /bin/sh и записал маркер от имени appuser.

Чего не приводит ни один источник, так это числа. Нет ни подсчёта экземпляров Bifrost, доступных из интернета, ни результатов сканирования, ни телеметрии, ни пострадавшего. В этой записи нет реального ущерба, и это отсутствие — факт о характере сообщений, а не успокоение: этот класс уязвимостей опасен только там, где совпадают два условия — открытый слушатель и нетронутая настройка по умолчанию, — и никто не публиковал, как часто они совпадали.

Один эндпоинт, две уязвимости, одна первопричина

За восемь дней до публикации рекомендательного бюллетеня MCP, 6 сентября 2026 года, та же исследовательская группа опубликовала CVE-2026-86242 — второй неаутентифицированный путь в тот же процесс. Обнаруженная исследователем Or Peles и получившая оценку 8,1, она эксплуатирует POST /api/plugins: Bifrost принимает включённый пользовательский плагин, путь которого является HTTP-URL; загрузчик разделяемых объектов воспринимает путь с префиксом http как загрузку, записывает тело во временный файл .so и передаёт этот файл функции plugin.Open из Go. Если загрузка проходит успешно, необязательная функция Init немедленно запускается с конфигурацией атакующего от имени пользователя шлюза.

Вариант сборки определяет, что получит атакующий. В динамически слинкованных сборках, которые проект документирует для пользовательских плагинов Go, загрузка, как ожидается, должна успешно выполняться, что в уведомлении называется неаутентифицированным удалённым выполнением кода. В опубликованном статически слинкованном Docker-образе plugin.Open завершается с ошибкой "Dynamic loading not supported", поэтому тот же запрос превращается в подделку запроса на стороне сервера. Загружаемый плагин также должен соответствовать версии Go хоста, операционной системе, архитектуре и типу компоновки, что является реальным ограничением, а не формальностью. Исправление — transports/v2.0.0, через pull request #5763, а затронутый диапазон — всё, что ниже него.

Ловушка — в порядке выпуска. v2.0.0 исправляет путь плагина и не исправляет путь MCP — The Hacker News прямо об этом говорит, а уведомление JFrog по MCP подтверждает, что v2.0.0 по-прежнему допускает неаутентифицированную регистрацию через stdio. Оператор, который оперативно установил патч после уведомления от 6 сентября, только на этом основании оставался уязвим до выхода transports/v2.1.0 8 сентября. Обе уязвимости сводятся к одной и той же фразе в обоих уведомлениях и в собственном pull request вендора: аутентификация отключена по умолчанию.

Что источники устанавливают, а что — нет

Механизм, диапазон версий, исправление и указание авторства согласованы между рекомендацией по безопасности, записью CVE, назначенной JFrog, собственным пул-реквестом и примечаниями к выпуску проекта, а также The Hacker News. Оставшееся разногласие касается уровня серьёзности, и оно действительно существует.

9,8 — это оценка JFrog как CNA, назначившего идентификатор, и именно её приводит NVD: в записи NVD в качестве источника метрики указан reefs@jfrog.com, и по состоянию на 28 октября 2026 г. запись всё ещё имеет статус «Awaiting Analysis», то есть NVD опубликовала вектор назначившего органа, а не собственный анализ. Оценка SSVC координатора CISA, приложенная к той же записи с отметкой времени 14 сентября 2026 г., указывает: эксплуатация — «none», автоматизируемость — «yes», техническое воздействие — «total». Модель EPSS организации FIRST оценила вероятность эксплуатации в следующие 30 дней в 2,3 процента на 27 октября 2026 г., что соответствует 83-му процентилю среди всех оценённых CVE; ни эта CVE, ни CVE-2026-86242 не фигурируют в каталоге известных эксплуатируемых уязвимостей CISA по состоянию на 27 октября 2026 г.

В противовес этому компания Maxim оспаривает такую оценку. В заявлении для The Hacker News Акшай Део, технический директор и соучредитель компании, сказал, что практический риск ниже, чем предполагает критическая оценка, поскольку для эксплуатации необходимо, чтобы интерфейс управления был доступен из недоверенной сети, а оператор оставил аутентификацию не настроенной; по его словам, при развертывании по умолчанию и в соответствии с документацией шлюз размещается внутри частной сети. The Hacker News сообщает, что Maxim считает эту уязвимость низкоопасной и работает с JFrog, которая назначила CVE, над пересмотром оценки. Это заявление приводится только в этом одном издании; не найдено второго издания, цитирующего позицию Maxim, и по состоянию на 28 октября 2026 года пересмотренная оценка не была опубликована — запись CVE последний раз обновлялась 14 сентября, а запись NVD — 18 сентября, и обе по-прежнему содержат оценку 9,8.

Два дальнейших пробела стоит назвать, а не сглаживать. В опубликованных 23 сентября 2026 года рекомендациях проекта на GitHub, где в качестве сообщившего указан исследователь из JFrog, по-прежнему стоят оценки 9,8 для уязвимости MCP и 8,1 для уязвимости плагина, причём никаких признаков пересмотра, о котором говорил Максим, нет. А затронутые диапазоны в этих рекомендациях записаны применительно к версиям модулей Go — ниже 1.5.27 и ниже 1.6.3, — а не к тегам релизов транспортов, которые используются в рекомендациях JFrog, поэтому оператору, сопоставляющему один документ с другим, приходится согласовывать две схемы нумерации, прежде чем решить, затронута ли конкретная сборка.

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.

Что изменилось и что операторам велели делать

Хронология разворачивается назад от раскрытия — это наименее интуитивная часть записи. Запрос на слияние #6757 был слит 2 сентября 2026 года. transports/v2.1.0 — версия, которую бюллетень называет первой содержащей исправление, — была опубликована 8 сентября, за шесть дней до публикации CVE-2026-90898 14 сентября. Исправление плагина проекта аналогичным образом предшествовало собственному бюллетеню: transports/v2.0.0 вышел 26 августа, за одиннадцать дней до CVE от 6 сентября. Операторы, которые отслеживают релизы, а не ленты уведомлений, получили патчи раньше, чем предупреждения.

То, что дает обновление, узко и конкретно. В версии 2.1.0 и новее неаутентифицированная регистрация через stdio возвращает 403, и команда не запускается; аутентифицированные администраторы сохраняют эту возможность, а подготовка через config.json по-прежнему работает. Временная мера из рекомендации для всех, кто не может перейти на новую версию, — установить governance.auth_config.is_enabled в true, использовать надежные учетные данные администратора и не размещать управляющий прослушиватель в недоверенных сетях. Инструкция JFrog для экземпляров, которые работали в открытом доступе с отключенной аутентификацией, состоит не в том, чтобы проводить расследование и надеяться: считайте их скомпрометированными и выполните ротацию виртуальных ключей и учетных данных провайдера.

Арифметика версий — это та часть, которую чаще всего понимают неправильно. v2.0.0 не устраняет этот недостаток. В ветке 1.6.x вплоть до 1.6.11 исправления тоже нет, а самый новый тег серии 1.6 в проекте — transports/v1.6.11, опубликованный 14 августа 2026 года; по состоянию на 28 октября 2026 года ветки 1.7 в репозитории нет — так что исправленного релиза внутри этой серии нет, и оператору на 1.6.x придётся перейти на 2.1.0 или более позднюю версию, а не ждать бэкпорта. Текущая линейка релизов уже давно миновала эту точку: transports/v2.3.2 был опубликован 20 октября 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.

После публикации произошли две вещи, которые может использовать защитник. Рекомендации по безопасности GitHub — GHSA-86gf-xh3g-rvxq для критической уязвимости MCP и GHSA-2qp8-4xgm-fw6g для высокоопасной уязвимости плагина — были опубликованы 23 сентября 2026 года, что дало лентам зависимостей и рекомендаций второй идентификатор для сопоставления. А 13 октября 2026 года в публичный репозиторий nuclei-templates был добавлен шаблон обнаружения для CVE-2026-90898, а значит, проверка попытки регистрации без аутентификации теперь доступна для запуска в конвейере сканирования, а не является тем, что каждой команде приходится писать самостоятельно.

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.

Открытым остается вопрос степени серьезности, и он будет урегулирован или нет двумя организациями, которые все еще обсуждают его. Позиция Maxim, согласно сообщениям, состоит в том, что оценка 9.8 завышает серьезность уязвимости, для значимости которой нужны доступный слушатель и незаданный параметр по умолчанию, — утверждение, которое в принципе проверяемо, но публично не проверено, поскольку никто не опубликовал, сколько развертываний соответствовали этим двум условиям. Позиция JFrog — это оценка, которую она присвоила как CNA; она по-прежнему указана в записи CVE и в NVD. Пока одна из сторон не опубликует пересмотр, операционная трактовка в любом случае одна: уязвимость реальна и была проверена от начала до конца на опубликованном образе контейнера, исправление вышло до предупреждения, в ветке 1.6 исправления нет вообще, а любой экземпляр, который работал с отключенной аутентификацией на доступном слушателе, следует рассматривать как инцидент с учетными данными, а не как исправление, которое можно запланировать.

© 2026 OrcaRouter

Провайдерам

Управляете инференс-платформой? Разместите свои модели на OrcaRouter.

providers@orcarouter.ai

Присоединяйтесь к сообществу

Discordsupport@orcarouter.aiXLinkedInGitHubYouTube