Abstrakcyjna ilustracja techniczna na ciemnogranatowym tle: świecący port wejściowy na lewej ścianie półprzezroczystego prostopadłościanu przepuszcza pojedynczą cienką, bladą linię, która nie ma żadnej plakietki ani klucza. Wewnątrz prostopadłościanu linia spotyka stos małych zaokrąglonych paneli i rozgałęzia się w krótki rząd kanciastych, przypominających polecenia kresek, które biegną w dół do dolnej przegrody mieszczącej skupisko świecących tokenów w kształcie kluczy. Obok portu wejściowego znajduje się druga, zamknięta brama obrysowana bladoniebieską linią. Bez tekstu, bez ludzi, bez logotypów organizacji.
AI Safety Incidents

Jeden nieuwierzytelniony POST do API zarządzania Bifrost uruchomił polecenie jako użytkownik bramy

Autor

Magnus Corvin

Data publikacji

Najnowsze modele · 20Zobacz wszystkie modele →
Benchmarki: Artificial Analysis · aktualizowane codziennie
Powrót do wszystkich wpisów

14 września 2026 r. JFrog Security Research opublikował CVE-2026-90898, lukę umożliwiającą wykonanie poleceń w Bifrost — otwartoźródłową bramę AI utrzymywaną przez Maxim — i ocenił ją na 9,8 na 10. Atak polegał na jednym żądaniu HTTP. Nieuwierzytelnione żądanie POST /api/mcp/client rejestruje klienta stdio MCP, który Bifrost odczytuje jako nazwę programu wraz z listą argumentów i uruchamia natychmiast, przed jakimkolwiek uzgadnianiem MCP, jako użytkownik uruchamiający bramę; w oficjalnym obrazie tym użytkownikiem jest appuser. Brama jest też miejscem, w którym znajdują się klucze dostawców i klucze wirtualne operatora, więc proces tam uruchomiony ma dostęp do poświadczeń. Anonimowość żądania zapewniało domyślne ustawienie dostarczane wraz z produktem: governance.auth_config.is_enabled ma wartość false, a według sformułowania JFrog „wyłączenie uwierzytelniania oznacza, że każdy wywołujący jest lokalnym administratorem”. Żadne z cytowanych źródeł nie podaje, by luka została kiedykolwiek wykorzystana. Źródła samego rekordu, ocena ważności i ocena pewności znajdują się w archiwum incydentów AI, obok innych udokumentowanych w ostatnim roku luk w bramach AI i MCP.

Bifrost to pojedyncza usługa, która znajduje się pomiędzy aplikacją a modelami językowymi, które wywołuje. Jej repozytorium opisuje ją jako bramę typu open source na licencji Apache-2.0, która „ujednolica dostęp do 23+ dostawców ... poprzez jedno API kompatybilne z OpenAI”, a wśród jej funkcji są proxowanie MCP, klucze wirtualne, budżety i równoważenie obciążenia dla poszczególnych dostawców; na dzień 28 października 2026 r. miała około 9 150 gwiazdek na GitHubie. Ta kombinacja jest całym powodem, dla którego powłoka na tym konkretnym hoście zasługuje na krytyczną ocenę: proces, który kieruje żądaniami, jest także procesem, który przechowuje poświadczenia dla każdego systemu upstream, do którego kieruje żądania. Hacker News, informując o tej wadzie 22 września, ujął konsekwencję wprost — ponieważ brama przechowuje klucze API dla każdego podłączonego dostawcy, wykonywanie poleceń w procesie bramy daje atakującemu dostęp do tych poświadczeń.

Cały atak był pojedynczym żądaniem POST

Biuletyn bezpieczeństwa JFrog, opublikowany pod numerem referencyjnym JFSA-2026-001686326, opisuje punkt końcowy, którego celem jest umożliwienie administratorowi dodania serwera MCP. Klient stdio w specyfikacji MCP nie jest adresem URL; to polecenie i jego argumenty, które program hosta ma uruchomić. Bifrost przyjmuje tę konfigurację przez API zarządzania i uruchamia program, gdy tylko klient zostanie dodany. Żadne uzgadnianie możliwości, żadna weryfikacja tożsamości serwera, żaden handshake nie musi się najpierw zakończyć sukcesem — biuletyn wyraźnie stwierdza, że to nie handshake uruchamia proces.

Dowód koncepcji w biuletynie to krótkie ciało JSON wysyłane do bramy z uwierzytelnianiem pozostawionym w ustawieniu domyślnym. Rejestruje ono klienta, którego poleceniem jest /bin/sh, a którego argumenty zapisują plik znacznikowy, po czym odczytuje ten znacznik z powrotem. JFrog dodaje szczegół operacyjny istotny podczas reagowania na incydenty: żądanie HTTP może zakończyć się przekroczeniem limitu czasu, gdy Bifrost czeka na handshake, który nigdy nie nadchodzi, a przekroczenie limitu czasu w logach nie oznacza, że polecenie nie zostało uruchomione. Proces już wtedy działa.

Rekord CVE, przypisany przez JFrog jako CVE Numbering Authority, przyporządkowuje defekt do CWE-306, brak uwierzytelniania dla funkcji krytycznej, oraz CWE-284, nieprawidłowa kontrola dostępu, oraz wskazuje dwa pliki zawierające handler i jego middleware: transports/bifrost-http/handlers/mcp.go i transports/bifrost-http/handlers/middlewares.go. Dotknięty moduł to github.com/maximhq/bifrost/transports, w systemach Linux i macOS, dla wszystkich wersji poniżej 2.1.0.

Domyślne ustawienie, które sprawiło, że żądanie stało się anonimowe

Nic z tego nie byłoby możliwe do wykorzystania bez drugiego warunku, a drugi warunek to domyślne ustawienie samego produktu. Oprogramowanie pośredniczące uwierzytelniania zarządzania Bifrost jest ograniczane przez governance.auth_config.is_enabled, które domyślnie ma wartość false. Gdy jest wyłączone, oprogramowanie pośredniczące traktuje każdego wywołującego jako lokalnego administratora — streszczenie samego biuletynu mówi, że wyłączenie uwierzytelniania oznacza, iż każdy wywołujący jest lokalnym administratorem — więc sprawdzenie, które powstrzymałoby żądanie, nigdy nie znalazło się na ścieżce.

Poprawka, pull request #6757, została scalona z projektem 2 września 2026 r., dwanaście dni przed publikacją CVE i sześć dni przed wydaniem, które ją zawiera. Jej opis jest niezwykle szczery co do charakteru problemu: pull request zamyka dwa mechanizmy nieuwierzytelnionego dostępu w tym samym API rejestracji, a nie jeden. Pierwszy to klient stdio. Drugi to klient MCP http lub sse wskazujący na adres pętli zwrotnej lub w sieci prywatnej, który pull request nazywa prymitywem SSRF wymierzonym w usługi wewnętrzne, „w tym w punkt końcowy metadanych chmury 169.254.169.254”. Oba są teraz odrzucane z kodem HTTP 403, gdy żądanie dociera do handlera bez rzeczywistej weryfikacji poświadczeń. Uwierzytelnieni administratorzy zachowują obie możliwości, a operatorzy nadal mogą aprowizować klientów stdio przez config.json.

Ten drugi prymityw nie pojawia się w tekście CVE. Jest w łatce i to dlatego poprawka działa jak bramka, a nie jak usunięcie: funkcja nigdy nie była błędem. Błędem było traktowanie nieuwierzytelnionego wywołującego jako administratora.

Gdzie faktycznie nasłuchiwał interfejs API zarządzania

Osiągalność to ta część zapisu, którą źródła opisują najspójniej, a mierzą najrzadziej. Biuletyn JFrog podaje, że domyślnym ustawieniem standardowego pliku binarnego jest localhost:8080, co ogranicza atakującego do samego hosta. Biuletyn tego samego zespołu dotyczący towarzyszącej luki stwierdza, że oficjalny obraz Dockera wiąże zamiast tego 0.0.0.0, a The Hacker News to powtarza: obraz udostępnia API zarządzania z zewnątrz kontenera, jeśli port zostanie opublikowany. Szybki start w README samego Bifrosta uruchamia „docker run -p 8080:8080 maximhq/bifrost”, a opcja -p Dockera publikuje port kontenera na wszystkich interfejsach hosta, chyba że podano adres. Biuletyn GitHub dotyczący luki odnotowuje, że zgłoszona ścieżka została zweryfikowana na opublikowanym obrazie maximhq/bifrost:latest, gdzie nieuwierzytelnione żądanie uruchomiło /bin/sh i zapisało znacznik jako appuser.

Żadne źródło nie podaje liczby. Nie ma licznika instancji Bifrost wystawionych do internetu, żadnego wyniku skanowania, żadnej telemetrii ani żadnej ofiary. W materiale nie ma rzeczywistej szkody, a ten brak jest faktem dotyczącym sposobu raportowania, a nie pocieszeniem — ta klasa podatności jest niebezpieczna tylko tam, gdzie zbiegają się dwa warunki: wystawiony nasłuchujący punkt końcowy i nietknięte ustawienie domyślne, i nikt nie opublikował, jak często występowały one jednocześnie.

Jeden endpoint, dwie wady, jedna przyczyna źródłowa

Osiem dni przed biuletynem bezpieczeństwa MCP, 6 września 2026 r., ten sam zespół badawczy opublikował CVE-2026-86242, drugą nieuwierzytelnioną ścieżkę do tego samego procesu. Odkryta przez Or Peles i oceniona na 8,1, wykorzystuje POST /api/plugins: Bifrost akceptuje włączoną wtyczkę niestandardową, której ścieżka jest adresem URL HTTP; program ładujący obiekty współdzielone traktuje ścieżkę z prefiksem http jako pobieranie, zapisuje treść do tymczasowego pliku .so i przekazuje ten plik do plugin.Open języka Go. Jeśli ładowanie się powiedzie, opcjonalna funkcja Init uruchamia się natychmiast z konfiguracją atakującego, jako użytkownik bramy.

Wariant kompilacji decyduje o tym, co otrzyma atakujący. W przypadku kompilacji z dynamicznym linkowaniem, które projekt dokumentuje dla niestandardowych wtyczek Go, oczekuje się, że ładowanie się powiedzie, co biuletyn określa jako nieuwierzytelnione zdalne wykonanie kodu. W opublikowanym statycznie linkowanym obrazie Docker plugin.Open kończy się niepowodzeniem z komunikatem „Dynamic loading not supported”, więc to samo żądanie degraduje się do fałszowania żądań po stronie serwera. Ładowalna wtyczka musi również odpowiadać wersji Go, systemowi operacyjnemu, architekturze i sposobowi linkowania hosta, co jest rzeczywistym ograniczeniem, a nie formalnością. Poprawka to transports/v2.0.0, wprowadzona przez pull request #5763, a zakresem objętym problemem jest wszystko poniżej.

Pułapka tkwi w kolejności wydań. v2.0.0 naprawia ścieżkę pluginu, ale nie naprawia ścieżki MCP — The Hacker News mówi to wprost, a biuletyn doradczy JFrog dotyczący MCP potwierdza, że v2.0.0 nadal pozwala na nieuwierzytelnioną rejestrację stdio. Operator, który załatał system szybko po biuletynie z 6 września, był przez sam ten fakt nadal narażony aż do wydania transports/v2.1.0 8 września. Obie wady sprowadzają się do tego samego zdania w obu biuletynach i we własnym pull requeście dostawcy: uwierzytelnianie jest domyślnie wyłączone.

Co źródła ustalają, a czego nie

Mechanizm, zakres wersji, poprawka i uznanie zasług są zgodne w biuletynie zabezpieczeń, rekordzie CVE przypisanym przez JFrog, własnym pull requeście i informacjach o wydaniu projektu oraz w The Hacker News. Pozostała rozbieżność dotyczy poziomu istotności i jest realna.

Wartość 9.8 to ocena JFrog jako przydzielającego CNA i jest to ocena, którą podaje NVD — rekord NVD wskazuje reefs@jfrog.com jako źródło metryki i, na dzień 28 października 2026 r., nadal wyświetla wpis ze statusem „Awaiting Analysis”, co oznacza, że NVD opublikowało wektor przydzielającego, a nie własną analizę. Ocena SSVC koordynatora CISA dołączona do tego samego rekordu, z datą 14 września 2026 r., wskazuje eksploatację „none”, automatyzowalność „yes” i wpływ techniczny „total”. Model EPSS FIRST określił prawdopodobieństwo eksploatacji w ciągu najbliższych 30 dni na 2,3 procent na dzień 27 października 2026 r., co plasuje je w 83. percentylu wśród wszystkich ocenionych CVE, a ani to CVE, ani CVE-2026-86242 nie występuje w katalogu CISA Known Exploited Vulnerabilities z datą 27 października 2026 r.

W przeciwieństwie do tego Maxim kwestionuje tę ocenę. W oświadczeniu dla The Hacker News Akshay Deo, dyrektor ds. technologii i współzałożyciel firmy, powiedział, że praktyczne ryzyko jest niższe, niż sugeruje krytyczna ocena, ponieważ wykorzystanie wymaga, aby interfejs zarządzania był osiągalny z niezaufanej sieci, a operator pozostawił uwierzytelnianie nieustawione; jego zdaniem domyślne i udokumentowane wdrożenie umieszcza bramę w sieci prywatnej. The Hacker News donosi, że Maxim uznaje tę lukę za niskiej wagi i współpracuje z JFrog, które przypisało CVE, nad zmianą oceny. To oświadczenie pojawia się w tym jednym serwisie; nie znaleziono drugiej publikacji cytującej stanowisko Maxima, a do 28 października 2026 r. nie opublikowano żadnej rewizji — rekord CVE ostatnio zaktualizowano 14 września, a wpis NVD 18 września, oba nadal na poziomie 9,8.

Dwie kolejne luki zasługują na nazwanie, a nie wygładzanie. Biuletyny doradcze projektu na GitHubie, opublikowane 23 września 2026 r. i wskazujące badacza z JFrog jako zgłaszającego, nadal podają 9,8 dla luki w MCP i 8,1 dla luki w pluginie, bez śladu rewizji opisanej przez Maxima. A zakresy podatności w tych biuletynach są opisane względem wersji modułów Go — poniżej 1.5.27 i poniżej 1.6.3 — a nie względem tagów wydań transports używanych w biuletynach JFrog, więc operator zestawiający jeden dokument z drugim musi uzgodnić dwa schematy numeracji, zanim zdecyduje, czy dana kompilacja jest objęta podatnością.

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.

Co się zmieniło i co polecono robić operatorom

Chronologia biegnie wstecz od ujawnienia, co jest najmniej intuicyjnym elementem zapisu. Pull request #6757 scalono 2 września 2026 r. transports/v2.1.0 — wydanie, które biuletyn wskazuje jako pierwsze zawierające poprawkę — opublikowano 8 września, sześć dni przed publikacją CVE-2026-90898 14 września. Poprawka wtyczki w projekcie poprzedziła jego własny biuletyn w ten sam sposób: transports/v2.0.0 wydano 26 sierpnia, jedenaście dni przed CVE z 6 września. Operatorzy, którzy śledzą wydania, a nie kanały informacyjne, mieli poprawki, zanim otrzymali ostrzeżenia.

To, co daje aktualizacja, jest wąskie i konkretne. W wersji 2.1.0 i późniejszych nieuwierzytelniona rejestracja stdio zwraca 403 i polecenie nie zostaje uruchomione; uwierzytelnieni administratorzy zachowują tę możliwość, a udostępnianie przez config.json nadal działa. Doraźnym rozwiązaniem z biuletynu dla każdego, kto nie może dokonać migracji, jest ustawienie governance.auth_config.is_enabled na true, użycie silnych danych uwierzytelniających administratora i trzymanie listenera zarządzania poza niezaufanymi sieciami. Instrukcja JFrog dla instancji, które były uruchomione w sposób wystawiony na dostęp przy wyłączonym uwierzytelnianiu, nie polega na prowadzeniu dochodzenia i liczeniu na szczęście: należy traktować je jako przejęte oraz wymienić klucze wirtualne i dane uwierzytelniające dostawców.

Arytmetyka wersji to część najbardziej narażona na błędną interpretację. v2.0.0 nie naprawia tej wady. Linia 1.6.x aż do 1.6.11 również nie zawiera poprawki, a najnowszy tag serii 1.6 w projekcie to transports/v1.6.11, opublikowany 14 sierpnia 2026 r.; na dzień 28 października 2026 r. w repozytorium nie ma linii 1.7 — w tej serii nie ma więc wydania z poprawką, a operator 1.6.x musi przejść na 2.1.0 lub nowszą wersję, zamiast czekać na backport. Bieżąca linia wydań znacznie wykracza poza ten punkt: transports/v2.3.2 opublikowano 20 października 2026 r.

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.

{{1}}Po publikacji wydarzyły się dwie rzeczy, z których obrońca może skorzystać.{{/1}} {{2}}Poradniki bezpieczeństwa projektu na GitHubie — GHSA-86gf-xh3g-rvxq dotyczący luki w MCP o wadze krytycznej i GHSA-2qp8-4xgm-fw6g dotyczący luki we wtyczce o wadze wysokiej — pojawiły się 23 września 2026 r., dając kanałom zależności i biuletynów drugi identyfikator do dopasowania.{{/2}} {{3}}A 13 października 2026 r. szablon wykrywania dla CVE-2026-90898 został włączony do publicznego repozytorium nuclei-templates, co oznacza, że sprawdzenie próby nieuwierzytelnionej rejestracji jest teraz czymś, co może uruchomić potok skanowania, a nie czymś, co każdy zespół musi napisać samodzielnie.{{/3}}

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.

Otwarta pozostaje kwestia wagi i zostanie ona rozstrzygnięta lub nie przez dwie organizacje, które wciąż o tym rozmawiają. Stanowisko Maxima, jak podano, jest takie, że 9.8 zawyża ocenę luki, która ma znaczenie tylko wtedy, gdy występuje wystawiony listener i nieustawiona wartość domyślna — twierdzenie, które da się w zasadzie przetestować, ale nie zostało publicznie przetestowane, ponieważ nikt nie opublikował, ile wdrożeń spełniło te dwa warunki. Stanowisko JFrog to ocena, którą przypisał jako CNA, wciąż widniejąca w rekordzie CVE i w NVD. Dopóki jedna z nich nie opublikuje korekty, interpretacja operacyjna jest taka sama w obu przypadkach: luka jest rzeczywista i została zweryfikowana od początku do końca na opublikowanym obrazie kontenera, poprawka została wydana przed ostrzeżeniem, seria 1.6 nie ma żadnej poprawki, a każdą instancję, która działała z wyłączonym uwierzytelnianiem na osiągalnym listenerze, należy traktować jako incydent związany z poświadczeniami, a nie jako poprawkę do zaplanowania.