Wygenerowana abstrakcyjna ilustracja na ciemnogranatowym tle: zarys okna przeglądarki po lewej stronie wysyła wstęgę bladych kształtów przypominających glify przez wąskie świecące gniazdo do panelu terminala po prawej. Druga, ciemniejsza wstęga dołącza do tego samego gniazda od dołu, a jej glify trafiają do terminala nie do odróżnienia od pierwszych. Nie pojawiają się żadne teksty, osoby ani logotypy organizacji. Logo OrcaRouter umieszczono w białym pasku stopki w prawym dolnym rogu.
AI Safety Incidents

Tencent BrowserSkill sprawdzał tylko, czy identyfikator rozszerzenia wygląda na prawdziwy — dowolne rozszerzenie mogło dostarczać agentowi sfałszowane strony

Autor

Rowan Sterling

Data publikacji

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

W dniu 17 września 2026, użytkownik GitHub występujący jako carfeii otworzył zgłoszenie #273 przeciwko open-source'owemu projektowi Tencent BrowserSkill z wąskim twierdzeniem: lokalny demon, który łączy rozszerzenie przeglądarki z agentem AI, nie sprawdzał, które rozszerzenie się podłączyło, tylko czy połączenie w ogóle wyglądało jak rozszerzenie. Trzy dni później, 20 września, VulnCheck opublikował to znalezisko jako CVE-2026-94111, opisując demona, który „akceptuje dowolny origin chrome-extension złożony z 32 znaków z zakresu a-p”, klasyfikuje go jako CWE-346 (błąd walidacji origin) i przypisuje odkrycie osobie George Chen. W biuletynie oceniono ją na CVSS 4.0 6.9, poziom średni, przy czym wpływ na integralność oceniono jako wysoki, a na poufność i dostępność jako niski. Nic w zapisie nie sugeruje, że podatność została wykorzystana: archiwum nie odnotowuje eksploatacji i przyznaje wpisowi ocenę pewności A, z polem real_harm ustawionym na false. To jeden z wpisów w archiwum incydentów AI, gdzie każdy wpis zawiera własne źródła, poziom ważności, ocenę pewności i flagę sporności.

Kontrola formatu, która nigdy nie sprawdzała tożsamości

Kod w środku rekordu jest krótki. W pliku crates/bsk-cli/src/daemon/ws.rs funkcja origin_allowed() pobiera nagłówek Origin uzgadniania WebSocket, usuwa ciąg chrome-extension:// i zwraca true, jeśli to, co pozostaje, ma dokładnie 32 znaki i każdy znak mieści się między a a p. To jest cały test. Jest uruchamiany raz, wewnątrz wywołania zwrotnego uaktualnienia HTTP, a niepowodzenie zwraca 403 z komunikatem „Origin not in chrome-extension allow-list” — choć w opublikowanej wersji nie było żadnej listy, do której miałby należeć.

Wzorzec nie jest przypadkowy. Identyfikatory rozszerzeń Chrome w standardzie Manifest V3 mają 32 znaki złożone dokładnie z tych szesnastu liter, więc sprawdzenie poprawnie rozpoznaje kształt prawdziwego źródła rozszerzenia. Wadą jest to, że każde rozszerzenie ma ten kształt. Sprawdzenie pyta, czy wywołujący wygląda jak rozszerzenie przeglądarki, a nigdy nie sprawdza, czy jest tym rozszerzeniem przeglądarki tego projektu, a identyfikator, z którym powinno było je porównać, był już publiczny: README projektu zawiera link do listingu w Chrome Web Store, którego ID to hhcmgoofomhgciiibhipgmgkgnoenaoi, a obok niego ID w Edge Add-ons. Żaden z tych ciągów nie występuje nigdzie w bramce.

Demon nasłuchuje na stałym porcie lokalnym, 52800, zadeklarowanym jako DEFAULT_WS_PORT w CLI i używanym bez żadnych zmian w lokalnej ścieżce połączenia. Port jest udokumentowany, stały i osiągalny dla wszystkiego, co już działa w profilu przeglądarki użytkownika. To właśnie lokalizacja sprawia, że wektor w komunikacie doradczym zaczyna się od AV:L — atakujący potrzebuje punktu zaczepienia na maszynie, a PR:L odnotowuje, że wystarczy punkt zaczepienia o niskich uprawnieniach. Rozszerzenie zainstalowane w tym samym profilu jest dokładnie takim punktem zaczepienia.

Ile warte jest to miejsce i w jakim stopniu jest to ustalone.

Zdanie dotyczące wpływu w biuletynie doradczym jest szerokie: atakujący, który zarejestruje złośliwe rozszerzenie jako klienta przeglądarki, może „przechwytywać i manipulować zawartością strony, DOM oraz zrzutami ekranu zwracanymi do agenta AI”. To opis, który zawierają CVE, zapis biuletynu GitHub GHSA-x6f2-qp26-2xrh oraz archiwum, i wyjaśnia on wysoką ocenę integralności w CVSS. Jest to również podsumowanie napisane przez podmiot, który przypisał CVE, a nie raport z wykazanego ataku.

W tym samym tygodniu, w samym wątku zgłoszenia, pojawiła się bardziej wnikliwa relacja. W dniu 20 września komentator występujący jako bb6894 poinformował, że ponownie sprawdził kod względem main na commicie fa953dc6 (18 września) i prześledził, do czego obcy klient może faktycznie dotrzeć po handshake'u. Większość przychodzących metod narzędziowych — jak twierdzi komentator — zwraca UnknownMethod klientowi WebSocket; żądania audytu są ograniczone do własnych rekordów klienta; a działania międzysesyjne, takie jak window_closed, user_interrupt i update_interaction, są chronione przed sesjami należącymi do innej przeglądarki. To, co przechodzi przez ten filtr, jest węższe i, dla agenta, prawdopodobnie gorsze niż przechwycenie: obce rozszerzenie może zająć slot przeglądarki pod wybranym przez atakującego instance_id, a jeśli jest jedyną zarejestrowaną przeglądarką — przy zimnym starcie lub restarcie demona — otrzymuje żądania narzędzi agenta i odpowiada na nie sfabrykowaną treścią strony, DOM lub zrzutami ekranu. Komentator nazywa to „fałszowaniem wyników wobec agenta” i „kanałem do percepcji agenta”. Drugim skutkiem jest odmowa usługi: gdy prawdziwa przeglądarka jest również online i nie ma selektora, uruchomienie sesji może zakończyć się niepowodzeniem z MultipleBrowsersOnline.

Żadna z tych wersji nie została publicznie zademonstrowana. Reporter opisał proof of concept jako „dostępny na żądanie”; nikt go nie opublikował i nie odnotowano żadnego wykorzystania. Różnica między tymi dwiema wersjami to więc różnica między dwiema interpretacjami tego samego pliku źródłowego, a nie między teorią a działającym exploitem — a tekst przedstawia obie, zamiast wybierać. Bez zastrzeżeń można stwierdzić, że po handshake nigdy nie było drugiej weryfikacji tożsamości, więc cokolwiek brama dopuściła, pozostawało dopuszczone przez cały czas życia połączenia.

Kod już wiedział

Najbardziej niewygodnym dokumentem w tym zapisie nie jest biuletyn bezpieczeństwa. Jest nim komentarz bezpośrednio nad funkcją, dostarczony w oznaczonym wydaniu, na które wskazuje biuletyn. Oznacza lukę jako TODO(M10/M12), wymienia listę dozwolonych, którą zamierza zbudować — zestaw DaemonConfig::allowed_extension_ids wypełniany z pliku konfiguracyjnego lub przepływu parowania — a następnie podaje powód odroczenia: „akceptowalna na razie luka w obronie w głąb, ponieważ parowanie odbywa się przez wyskakujące okno, ale rozszerzenie załadowane z boku na tej samej maszynie obecnie przechodzi przez bramkę”.

Czytając to wprost, opiekunowie wiedzieli, jakiego wywołującego bramka przepuściła, odnotowali, że o tym wiedzą, i wydali wersję. Nie sprawia to, że narażenie jest większe, niż wynika z komunikatu doradczego; sprawia, że jest mniej zaskakujące, i przesuwa pytanie z „jak to przeszło przez przegląd” na „co sprawiłoby, że odłożona poprawka stałaby się pilna”. Łagodzące założenie projektowe jest prawdziwe i również udokumentowane: połączenie lokalne wymaga świadomego włączenia. Zgodnie z README użytkownik instaluje rozszerzenie, otwiera jego popup i włącza połączenie lokalne, a demon musi działać, aby cokolwiek z tego było osiągalne. Profil z zainstalowanym rozszerzeniem, ale wyłączonym przełącznikiem, lub bez działającego demona bsk nie jest celem nasłuchującym.

Pięć tygodni później brama pozostaje bez zmian

An HTML timeline card titled 'From a filed issue to an unmerged patch', subtitled 'Nine dated entries in the BrowserSkill daemon origin-check record', listing: 16 September 2026 BrowserSkill extension 0.3.0 tagged; 17 September the bsk CLI 0.3.0 release, and issue #273 filed the same day at 15:14 UTC; 20 September CVE-2026-94111 published by VulnCheck and listed by NVD and GitHub, with a narrower impact analysis posted in the thread; 21 September CISA's SSVC assessment records exploitation none and automatable no; 23 September CLI 0.3.1 released with the same origin check; 28 September pull request #363 opened from a fork to pin the store extension IDs; 30 September CLI 0.3.2, same check; 15 October CLI 0.3.3, same check; 27 October issue still open and the patch still unmerged. A footer cites the GitHub issue, the VulnCheck advisory, NVD and the project's release tags, with the OrcaRouter logo in a white strip at the bottom right.

Zakres objęty rekordem to „do 0.3.0” i taki zakres przypisała CNA. To jednak nie koniec historii w repozytorium. Wydanie CLI 0.3.0 oznaczono tagiem 17 września o 02:22 UTC; zgłoszenie złożono tego samego dnia o 15:14 UTC, około trzynaście godzin później. Od tego czasu ukazały się trzy kolejne wydania CLI — 0.3.1 z 23 września, 0.3.2 z 30 września i 0.3.3 z 15 października — a funkcja origin_allowed() w każdym z tych tagów to ta sama kontrola opisana powyżej, z wciąż znajdującym się nad nią TODO. Według stanu na 27 października gałąź main również to zawiera.

Poprawka istnieje, ale tylko jako propozycja. W dniu 28 września kontrybutor publikujący jako dangzitou otworzył pull request #363 z osobistego forka, zatytułowany „fix(daemon): przypnij bramę pochodzenia WS do własnych identyfikatorów rozszerzeń BrowserSkill”. Zastępuje test kształtu porównaniem z dwoma identyfikatorami ze sklepu i dodaje BSK_ALLOWED_EXTENSION_IDS jako jawną ścieżkę dopuszczenia dla kompilacji ładowanych poza sklepem i deweloperskich, których identyfikatory w wersji rozpakowanej są wyprowadzane ze ścieżki instalacji. Treść pull requesta twierdzi, że zamyka zarówno ścieżkę fałszowania, jak i atak typu denial of service na MultipleBrowsersOnline. Na dzień 27 października pozostaje otwarty i nie został scalony, a zgłoszenie pozostaje otwarte bez dołączonego oświadczenia dostawcy.

Praktyczną konsekwencją jest to, że nie ma wersji, do której można by zaktualizować, zawierającej poprawkę. Każdy, kto radzi „zaktualizować do wersji powyżej 0.3.0”, radzi krok, który jeszcze nie istnieje: późniejsze wydania są późniejsze, ale nie są poprawione. Zakres w rekordzie należy rozumieć jako zakres oceniony przez CNA 20 września, a nie jako stwierdzenie, że 0.3.1 i późniejsze są wolne od problemu.

Co jest ustalone, a co nie

• Ustalono: wydany kod źródłowy w tagu cli-v0.3.0 zawiera bramkę pochodzenia, która akceptuje dowolne 32-znakowe pochodzenie chrome-extension złożone ze znaków a–p. Reporter, CNA i sam plik są zgodni, a kod jest publiczny.

• Ustalono: brama uruchamiała się tylko raz, podczas aktualizacji połączenia WebSocket, bez późniejszej weryfikacji tożsamości, na stałym porcie lokalnym 52800.

• Ustalono: własny komentarz w kodzie źródłowym projektu przyznawał, że rozszerzenie instalowane poza oficjalnym sklepem przejdzie, a wprowadzenie listy dozwolonych odłożono.

• Ustalono: CVSS 4.0 6.9 i CVSS 3.1 6.6 to oba wyniki VulnCheck, zapisane przez NVD jako drugorzędne. Własny status NVD dla tego wpisu to Deferred, co oznacza, że opublikował dane CNA bez dodania analizy autorstwa NVD.

• Ustalono: Ocena SSVC CISA z 21 września odnotowuje stopień wykorzystania jako „brak”, automatyzowalność jako „nie”, a wpływ techniczny jako „częściowy”. EPSS FIRST określa prawdopodobieństwo wykorzystania w ciągu najbliższych trzydziestu dni na 0,00135, co odpowiada 2,9. percentylowi, na dzień 26 października.

• Nie ustalono: żadnego rzeczywistego zastosowania. Brak eksploatacji, brak opublikowanego proof of concept, brak liczby dotkniętych instalacji. Metryki repozytorium projektu nie są liczbą instalacji i nikt takiej liczby nie opublikował.

• Nie ustalono: który opis wpływu jest właściwy. „Przechwytywanie i manipulowanie” w ujęciu CNA oraz „fałszowanie wyników plus odmowa usługi” w ujęciu wątku opisują różne górne granice, a żadnego z nich nie wykazano poza lekturą kodu.

• Nie ustalono: że wada kończy się na wersji 0.3.0. Ciąg wersji jest oceną CNA; kod pozostaje niezmieniony w trzech wydaniach po niej i żadne oświadczenie producenta ich nie oczyszcza.

• Sprzeczne, nierozstrzygnięte: jeden agregator, DevGuard, wyświetla dla tego CVE sygnał „wykryto próby wykorzystania”, nie podając źródła, IOC ani dostawcy telemetrii. Kłóci się to z odnotowaną przez CISA oceną o braku wykorzystania oraz z brakiem jakiegokolwiek publicznego raportu; archiwum również nie odnotowuje wykorzystania. Oznaczenie agregatora należy traktować jako niepotwierdzone, a nie jako drugi punkt danych.

An HTML card titled 'Established, and not', split into two columns. The left column, headed 'Established', lists: an origin gate that accepts any 32-character a-p chrome-extension origin, in the released cli-v0.3.0 source; checked once at the WebSocket upgrade on local port 52800; the project's own comment conceded sideloaded extensions pass; CVSS 4.0 6.9 and 3.1 6.6 are VulnCheck's scores, NVD status Deferred; CISA SSVC records exploitation none and automatable no; EPSS 0.00135 as of 26 October. The right column, headed 'Not established', lists: any real-world use or published proof of concept; which impact account holds, interception or result forgery; that the flaw stops at 0.3.0, since 0.3.1 to 0.3.3 carry the same check; an installation count; an unsourced aggregator claim of exploitation attempts. A footer cites VulnCheck, NVD, CISA, FIRST and the GitHub issue, with the OrcaRouter logo in a white strip at the bottom right.

Dlaczego transport jest interesującym celem

Większość wytycznych dotyczących wstrzykiwania promptów wyznacza tę samą granicę: tekst pobranej strony traktuj jako niezaufany, a mechanizm, który go dostarcza, jako zaufany. Zakłada się, że narzędzie czytające stronę internetową czyta tę stronę; pytanie dotyczy tego, co mówi strona. Ten zapis odwraca to założenie. To, co agent otrzymuje jako zawartość strony, DOM i zrzuty ekranu, jest tym, co klient po drugiej stronie lokalnego gniazda zdecyduje się wysłać, a jedyna bramka na tym gnieździe zadawała pytanie, na które każdy kandydat mógł odpowiedzieć poprawnie. Jeśli klient jest obcy, „zweryfikuj, co mówi strona” nie ma już względem czego weryfikować.

To także wyjaśnia, dlaczego waga podatności jest taka, a nie wyższa. Atak jest lokalny, zakłada, że w profilu jest już zainstalowane rozszerzenie lub istnieje inny lokalny kod, i wymaga uruchomionego demona z włączonym połączeniem lokalnym. Te warunki są zwyczajne dla zamierzonych użytkowników narzędzia, a niezwyczajne dla wszystkich innych. Średnia waga w archiwum i odnotowany brak szkód są spójne z tym kształtem: realne poszerzenie zaufania dokładnie na granicy, którą projekt miał chronić, bez dowodów, że ktokolwiek przez nią przeszedł.

Wąska praktyczna porada, która następuje, nie jest środkiem zaradczym, który ktokolwiek opublikował, lecz jedynie granicą, którą kod pozostawia. Ponieważ dopuszczenie jest ustalane jednorazowo podczas uzgadniania połączenia na opublikowanym porcie, osiągalny obszar wyznacza to, które rozszerzenia są zainstalowane w profilu uruchamiającym demona, oraz to, czy ten demon i lokalne połączenie rozszerzenia są w danym momencie włączone. Dopóki nie zostanie wydana lista dozwolonych identyfikatorów rozszerzeń, te dwa fakty stanowią całość kontroli.

A generated abstract illustration on a dark navy field: two identical hexagonal tokens of the same size and colour approach a narrow blue aperture from opposite sides; both pass through, and beyond it their outputs merge into a single stream of pale glyph shapes entering a terminal panel. A faint dotted outline of a browser window sits above. No text, people or organisation logos appear. The OrcaRouter logo is set in the white footer strip at the bottom right.

CVE-2026-94111 zestarzeje się na jeden z dwóch sposobów. Albo pull request zostanie scalony, a wpis stanie się krótką pozycją o znanej luce, której rozwiązanie zajęło trochę czasu, albo ta sama kontrola kształtu za rok nadal będzie w drzewie, a wpis będzie cytowany jako wczesny przykład klasy, do której należy: nie strony zawierającej wrogą instrukcję, lecz potoku, który decyduje, którą stronę czyta agent.