
Kod znika z pamięci, ale procesor przez cały czas pamięta, dokąd wcześniej prowadził skok. Badacze zamienili tę drobną niespójność w nowy atak Spectre.
Program wygenerował fragment kodu, procesor kilka razy go wykonał, a później kod został usunięty. Dla systemu niby przestał istnieć, tyle iż jeden element procesora najwyraźniej nie dostał tej informacji. Mechanizm przewidywania skoków przez cały czas pamiętał, pod jaki adres wcześniej warto było skierować wykonanie programu.
Gdy pod tym samym adresem pojawił się później zupełnie inny kod, to procesor mógł na ułamek chwili skorzystać ze starego przewidywania i rozpocząć wykonanie od miejsca, do którego normalnie nigdy nie powinien trafić. To wystarczyło, żeby zostawić ślad pozwalający odzyskać chronione dane.
Badacze z Vrije Universiteit Amsterdam i Scuola Superiore Sant’Anna nazwali nową technikę Branch Target Reuse, czyli ponownym wykorzystaniem celu skoku. Pełne badanie Branch Target Reuse: Practical Spectre-v2 Attacks in JIT Engines via Stale Branch Prediction Entries zostało przyjęte na konferencję ACM CCS 2026.
Procesor zgaduje, dokąd program za chwilę pójdzie
Żeby zrozumieć sam atak, trzeba wrócić do pomysłu, który w 2018 r. doprowadził do odkrycia Spectre. Współczesne procesory są szybkie również dlatego, iż nie zawsze czekają na wynik poprzedniej operacji. Próbują przewidzieć, co program zrobi za chwilę, i zaczynają wykonywać kolejne instrukcje z wyprzedzeniem. jeżeli zgadną dobrze, oszczędzają czas. jeżeli źle, wynik spekulacyjnych obliczeń zostaje odrzucony.
Problem odkryty przy Spectre polegał na tym, iż ślady takiej błędnej spekulacji mogą pozostać m.in. w pamięci podręcznej procesora. Atakujący jest później w stanie je zmierzyć i pośrednio wyciągnąć informacje, do których normalnie nie powinien mieć dostępu.
O konsekwencjach Spectre i Meltdown pisaliśmy na Spider’s Web już w 2018 r. Przez kolejne lata procesory i systemy operacyjne dostały wiele zabezpieczeń utrudniających podobne ataki. Branch Target Reuse znajduje jednak inną drogę do tego samego mechanizmu.
Kod JIT pojawia się i znika cały czas
Kluczowe są tutaj kompilatory JIT, czyli just-in-time. Używają ich przeglądarki internetowe, maszyny wirtualne, środowiska uruchomieniowe, a choćby jądro Linuksa. Ich zadanie polega na tworzeniu kodu maszynowego podczas działania programu. jeżeli jakiś fragment JavaScriptu jest często wykonywany, silnik przeglądarki może przetłumaczyć go na kod zrozumiały bezpośrednio dla procesora. Gdy przestaje być potrzebny, kod może zostać wyrzucony, a zwolniony fragment pamięci wykorzystany ponownie.
System wie wtedy, iż starego kodu już po prostu nie ma. Procesor również widzi nową zawartość pamięci. Nie zawsze znika jednak historia zapisana w buforze przewidywania celów skoków, czyli BTB. Procesor może przez cały czas pamiętać: kiedy wcześniej wykonywałem ten skok, trafiałem pod taki adres. I właśnie w tym momencie zaczyna się adekwatny atak.
Branch Target Reuse składa się z kilku kroków. Atakujący najpierw doprowadza do wygenerowania przez JIT fragmentu kodu i wielokrotnie wykonuje odpowiedni skok. W ten sposób uczy mechanizm przewidywania określonego celu. Następnie stary fragment kodu zostaje usunięty.
JIT wykorzystuje później ten sam obszar pamięci do przechowania nowego programu. Kiedy odpowiedni skok zostaje uruchomiony ponownie, procesor może sięgnąć do starego wpisu BTB i spekulacyjnie skoczyć pod zapamiętany adres. Tyle iż pod tym adresem znajduje się już zupełnie inny kod.
Co gorsza, procesor może wylądować w środku instrukcji i zinterpretować jej bajty w zupełnie inny sposób. Badacze byli w stanie tak przygotować nowy kod, żeby z tego nieprawidłowego punktu wejścia powstał użyteczny dla nich fragment wykonujący operacje potrzebne do wycieku danych. Autorzy określają to obrazowo jako spekulacyjny odpowiednik błędu use-after-free: stary cel przetrwał dłużej niż kod, do którego pierwotnie prowadził.
Wyciągnęli hash hasła roota w kilka minut
Chyba najbardziej efektowną demonstrację przeprowadzono w Linuksie z wykorzystaniem klasycznego BPF. Badacze stworzyli dwa programy cBPF działające jako filtry seccomp. Jeden służył do wytrenowania przewidywania procesora. Później był usuwany, a jego miejsce zajmował drugi, przygotowany tak, aby stare przewidywanie pozwoliło spekulacyjnie uruchomić kod służący do wycieku informacji.
Atak odczytywał pamięć z szybkością około 8 bajtów na sekundę. Brzmi niby mizernie, dopóki jednak nie przypomnimy sobie, iż hasła czy klucze kryptograficzne nie zajmują gigabajtów. Badacze uruchomili su root, dzięki czemu hash hasła znalazł się w pamięci procesu, a później wykorzystali BTR do jego odnalezienia i odczytania. Na rdzeniach Intel Raptor Cove zajęło to około 3 min, a na nowszych Lion Cove około 5 min. Nie wykradziono więc w ten sposób samego hasła w czystym tekście. Wyciekł jego hash znajdujący się w pamięci, który następnie może stać się celem dalszego ataku.
Kompletny exploit dla Linuksa badacze zbudowali na procesorach Intela, ale sam mechanizm sprawdzili również szerzej. Nieprawidłowe zachowanie pojawiło się na każdym testowanym przez nich procesorze reprezentującym układy Intela, AMD i Arm. Autorzy twierdzą, iż żaden współczesny CPU nie synchronizuje automatycznie stanu kodu z zapisanymi wcześniej przewidywaniami celów skoków.
Sprawdzono również SpiderMonkey, czyli silnik JavaScriptu i WebAssembly używany przez Firefoksa. Tam udało się pokazać, iż stare wpisy BTB rzeczywiście przeżywają usunięcie i ponowne przydzielenie pamięci. Badacze szacują możliwość wycieku dziesiątek bajtów na sekundę, ale nie zbudowali jeszcze pełnego ataku działającego ze złośliwej strony internetowej.
Trzecim celem był Oracle GraalVM. Tam BTR pozwalał spekulacyjnie przeskoczyć zabezpieczenie ograniczające dostęp programu do pamięci jego piaskownicy. Pełny exploit utrudniały jednak inne operacje środowiska, które przypadkiem usuwały potrzebne wpisy z predyktora.
Łatki już są, ale problem jest głębiej
Miejmy na uwadze, iż to nie jest sytuacja, w której właśnie ujawniono niezałataną lukę i każdy komputer czeka teraz atak. Badacze zgłosili problem już wcześniej producentom. Linux dostał poprawki oznaczone CVE-2026-64507 i CVE-2026-64508. Gdy ponownie używany jest obszar pamięci przeznaczony dla kodu BPF, system może teraz wyczyścić odpowiednie przewidywania procesora dzięki mechanizmu IBPB.
Oracle utrudnił atak przez losowanie lokalizacji pamięci używanej przez kod JIT. Mozilla pracuje natomiast nad dalszym wzmacnianiem izolacji stron i analizuje dodatkowe zabezpieczenia. Co robić, jak żyć? Przede wszystkim aktualizować system i przeglądarkę. Znacznie ciekawszy jest jednak wniosek dla projektantów procesorów. Osiem lat po pierwszym Spectre znowu okazuje się, iż błędne przewidywanie wykonania może pozostawić coś, czego programista w ogóle nie widzi.
*Grafika wprowadzająca wygenerowana przez AI













