Aby zminimalizować ryzyko ujawnienia pamięci jądra lub pamięci międzyprocesowej ( atak Spectre ), jądro Linux 1 zostanie skompilowane z nową opcją , -mindirect-branch=thunk-extern
wprowadzoną w gcc
celu wykonywania wywołań pośrednich za pośrednictwem tak zwanej retpoliny .
To wydaje się być nowo wynalezionym terminem, ponieważ wyszukiwarka Google pojawia się tylko bardzo niedawno (ogólnie w 2018 r.).
Co to jest retpolina i jak zapobiega ostatnim atakom związanym z ujawnianiem informacji o jądrze?
1 Nie jest on jednak specyficzny dla Linuksa - podobny lub identyczny konstrukt wydaje się być wykorzystywany jako część strategii ograniczania ryzyka w innych systemach operacyjnych.
security
assembly
x86
cpu-architecture
BeeOnRope
źródło
źródło
gcc
wskazuje to w ten sposób! Nie rozpoznałem lkml.org/lkml/2018/1/3/780 jak na stronie z listą mailingową jądra Linuksa, nawet kiedy tam zajrzałem (i dostałem migawkę, ponieważ była offline).Odpowiedzi:
Artykuł wspomniany przez sgbj w komentarzach napisanych przez Paula Turnera z Google wyjaśnia znacznie bardziej szczegółowo, ale dam mu szansę:
O ile potrafię poskładać to razem z ograniczonymi informacjami w tej chwili, retpolina jest trampoliną powrotną, która wykorzystuje nieskończoną pętlę, która nigdy nie jest wykonywana, aby zapobiec spekulowaniu procesora na celu pośredniego skoku.
Podstawowe podejście można znaleźć w gałęzi jądra Andi Kleen zajmującej się tym problemem:
Wprowadza nowe
__x86.indirect_thunk
wywołanie, które ładuje cel wywołania, którego adres pamięci (który zadzwonięADDR
) jest przechowywany na górze stosu i wykonuje skok za pomocąRET
instrukcji. Sam thunk jest następnie wywoływany za pomocą makra NOSPEC_JMP / CALL , który został użyty do zastąpienia wielu (jeśli nie wszystkich) pośrednich wywołań i skoków. Makro po prostu umieszcza cel wywołania na stosie i ustawia adres zwrotny, jeśli to konieczne (zwróć uwagę na nieliniowy przepływ sterowania):Umieszczenie
call
na końcu jest konieczne, aby po zakończeniu wywołania pośredniego przepływ sterujący był kontynuowany za użyciemNOSPEC_CALL
makra, dzięki czemu można go używać zamiast zwykłegocall
Sam thunk wygląda następująco:
Przepływ kontroli może być tutaj nieco mylący, więc wyjaśnię:
call
wypycha bieżący wskaźnik instrukcji (etykieta 2) na stos.lea
dodaje 8 do wskaźnika stosu , skutecznie odrzucając ostatnio wypisane cztero słowo, które jest ostatnim adresem zwrotnym (do etykiety 2). Następnie górna część stosu ponownie wskazuje na prawdziwy adres zwrotny ADDR.ret
przeskakuje*ADDR
i resetuje wskaźnik stosu na początek stosu wywołań.W końcu całe to zachowanie jest praktycznie równoważne skokowi bezpośrednio
*ADDR
. Jedną z korzyści, jakie otrzymujemy, jest to, że predyktor gałęzi używany dla instrukcji return (Return Stack Buffer, RSB), podczas wykonywaniacall
instrukcji, zakłada, że odpowiedniaret
instrukcja przejdzie do etykiety 2.Część po etykiecie 2 tak naprawdę nigdy nie zostanie wykonana, jest to po prostu nieskończona pętla, która teoretycznie wypełniłaby potok
JMP
instrukcji instrukcjami. Za pomocąLFENCE
,PAUSE
lub bardziej ogólnie instrukcja powodując rurociąg instrukcja będzie stoisko zatrzymuje CPU od marnować energię i czas na wykonanie tego spekulacyjnego. Wynika to z tego, że w przypadku normalnego powrotu wywołania retpoline_call_targetLFENCE
następna instrukcja do wykonania. Tego też przewidzi predyktor gałęzi na podstawie oryginalnego adresu zwrotnego (etykieta 2)Cytat z podręcznika architektury Intela:
Zauważ jednak, że specyfikacja nigdy nie wspomina, że LFENCE i PAUZA powodują zatrzymanie potoku, więc czytam trochę między wierszami tutaj.
Wróćmy do pierwotnego pytania: Ujawnienie informacji o pamięci jądra jest możliwe dzięki połączeniu dwóch pomysłów:
Mimo że wykonywanie spekulacyjne powinno być wolne od skutków ubocznych, gdy spekulacja była błędna, wykonywanie spekulacyjne nadal wpływa na hierarchię pamięci podręcznej . Oznacza to, że gdy ładowanie pamięci jest wykonywane spekulacyjnie, mogło nadal powodować eksmisję linii pamięci podręcznej. Tę zmianę w hierarchii pamięci podręcznej można zidentyfikować, dokładnie mierząc czas dostępu do pamięci, który jest mapowany na ten sam zestaw pamięci podręcznej.
Możesz nawet wyciec niektóre fragmenty dowolnej pamięci, gdy adres źródłowy odczytanej pamięci został odczytany z pamięci jądra.
Pośredni predyktor rozgałęzienia procesorów Intel wykorzystuje tylko 12 najniższych bitów instrukcji źródłowej, dlatego łatwo jest otruć wszystkie możliwe historie prognoz 2 ^ 12 adresami pamięci kontrolowanymi przez użytkownika. Mogą one wówczas, gdy przewidywany skok pośredni w jądrze, zostać spekulacyjnie wykonany z uprawnieniami jądra. Korzystając z bocznego kanału synchronizacji pamięci podręcznej, możesz w ten sposób przeciekać dowolną pamięć jądra.
AKTUALIZACJA: Na liście mailingowej jądra trwa dyskusja, która prowadzi mnie do przekonania, że retpoliny nie łagodzą w pełni problemów prognozowania gałęzi, tak jak gdy bufor zwrotny stosu (RSB) jest pusty, nowsze architektury Intel (Skylake +) wycofują się do podatnego na oddział bufora docelowego (BTB):
źródło
push
/ret
że nie asymetrii na zwrot adresowej predyktora stosie. Jest jeden nieprzewidywalny (przejście dolfence
przed użyciem faktycznego adresu zwrotnego), ale zrównoważył tocall
modyfikator + .rsp
ret
push
/ret
(w moim ostatnim komentarzu). Odp: Twoja edycja: Niedopełnienie RSB powinno być niemożliwe, ponieważ retpolina zawieracall
. Gdyby uprzedzenie jądra spowodowało zmianę kontekstu, wznowilibyśmy wykonanie z RSB przygotowanym zcall
harmonogramu. Ale może procedura obsługi przerwań mogłaby zakończyć się wystarczającą ilościąret
s, aby opróżnić RSB.Retpoline jest przeznaczony do ochrony przed wstrzyknięciem docelowej gałęzi ( CVE 2017-5715 ) wykorzystania. Jest to atak polegający na użyciu pośredniej instrukcji rozgałęzienia w jądrze w celu wymuszenia spekulatywnego wykonania dowolnego fragmentu kodu. Wybrany kod jest „gadżetem”, który jest w pewien sposób przydatny dla atakującego. Na przykład można wybrać kod, który spowoduje wyciek danych jądra poprzez wpływ na pamięć podręczną. Retpolina zapobiega temu exploitowi, po prostu zastępując wszystkie instrukcje gałęzi pośredniej instrukcją return.
Myślę, że kluczem w retpolinie jest tylko część „ret”, która zastępuje gałąź pośrednią instrukcją return, dzięki czemu procesor używa predyktora stosu zwrotnego zamiast przewidywalnego gałęzi. Jeśli zamiast tego zostanie użyta prosta instrukcja wypychania i powrotu, kod, który zostałby wykonany spekulacyjnie, byłby kodem, który funkcja w końcu i tak wróci do normy, a nie jakiś gadżet przydatny dla atakującego. Główną zaletą części trampoliny wydaje się być utrzymanie stosu zwrotnego, więc gdy funkcja faktycznie powraca do swojego obiektu wywołującego, jest to poprawnie przewidywane.
Podstawowa idea zastrzyku celu gałęzi jest prosta. Wykorzystuje to, że CPU nie rejestruje pełnego adresu źródła i miejsca docelowego gałęzi w swoich buforach docelowych gałęzi. Atakujący może więc wypełnić bufor za pomocą skoków we własnej przestrzeni adresowej, które spowodują trafienia predykcyjne, gdy konkretny skok pośredni zostanie wykonany w przestrzeni adresowej jądra.
Pamiętaj, że retpolina nie zapobiega bezpośredniemu ujawnianiu informacji o jądrze, a jedynie zapobiega wykorzystywaniu instrukcji pośredniej gałęzi do spekulacyjnego wykonania gadżetu, który ujawniałby informacje. Jeśli atakujący może znaleźć inne sposoby spekulacyjnego wykonania gadżetu, retpolina nie zapobiega atakowi.
Artykuł Spectre Attacks: Exploiting Speculative Execution autorstwa Paula Kochera, Daniela Genkina, Daniela Grussa, Wernera Haasa, Mike'a Hamburga, Moritza Lippa, Stefana Mangarda, Thomasa Preschera, Michaela Schwarza i Yuvala Yaroma daje następujący przegląd tego, jak można wykorzystać gałęzie pośrednie :
Wpis na blogu zatytułowany Czytanie pamięci uprzywilejowanej z bocznym kanałem przez zespół Project Zero w Google stanowi kolejny przykład wykorzystania zastrzyku docelowego dla gałęzi do stworzenia działającego exploita.
źródło
To pytanie zostało zadane jakiś czas temu i zasługuje na nowszą odpowiedź.
Streszczenie :
Sekwencje „retpoliny” są konstrukcją programową, która umożliwia izolowanie pośrednich gałęzi przed spekulatywnym wykonywaniem. Można to zastosować w celu ochrony wrażliwych plików binarnych (takich jak system operacyjny lub implementacje hiperwizora) przed atakami wstrzyknięć docelowych oddziałów na ich pośrednie gałęzie.
Słowo „ ret poline ” to kontaminacja słów „powrót” i „trampolina”, podobnie jak Improvement „ rel poline ” powstał z połączenia „względnej” i „trampoliną”. Jest to konstrukcja trampoliny skonstruowana przy użyciu operacji powrotu, która również symbolicznie gwarantuje, że wszelkie powiązane spekulacyjne wykonania będą „odbijać się” w nieskończoność.
Użycie tej opcji kompilatora chroni tylko przed Spectre V2 w procesorach, których dotyczy problem, i wymagana jest aktualizacja mikrokodu dla CVE-2017-5715. Będzie „ działał ” na dowolnym kodzie (nie tylko na jądrze), ale warto zaatakować tylko kod zawierający „sekrety”.
Kompilator LLVM miał
-mretpoline
przełącznik ponieważ przed 4 stycznia 2018 r . Ta data jest datą pierwszego opublikowania luki w zabezpieczeniach . GCC udostępniło swoje łatki 7 stycznia 2018 r.Data CVE sugeruje, że luka została „ odkryta ” w 2017 r., Ale dotyczy niektórych procesorów wyprodukowanych w ciągu ostatnich dwóch dekad (prawdopodobnie została odkryta dawno temu).
Najpierw kilka definicji:
Trampolina - czasami nazywana wektorami skoków pośrednich, trampoliny to miejsca w pamięci, w których znajdują się adresy wskazujące na procedury obsługi przerwań, procedury we / wy itp. Wykonanie wskakuje do trampoliny, a następnie natychmiast wyskakuje lub odbija się, stąd nazwa trampolina. GCC tradycyjnie wspiera zagnieżdżone funkcje, tworząc wykonywalną trampolinę w czasie wykonywania, gdy pobierany jest adres zagnieżdżonej funkcji. Jest to mały fragment kodu, który zwykle znajduje się na stosie, w ramce stosu funkcji zawierającej. Trampolina ładuje statyczny rejestr łańcucha, a następnie przeskakuje na prawdziwy adres zagnieżdżonej funkcji.
Thunk - Thunk to podprogram używany do wstrzykiwania dodatkowych obliczeń do innego podprogramu. Klocki są używane przede wszystkim do opóźniania obliczeń, aż do uzyskania wyniku, lub do wstawiania operacji na początku lub na końcu innego podprogramu
Zapamiętywanie - Zapamiętana funkcja „zapamiętuje” wyniki odpowiadające niektórym zestawom określonych danych wejściowych. Kolejne wywołania z zapamiętanymi danymi wejściowymi zwracają zapamiętany wynik, zamiast go ponownie obliczać, eliminując w ten sposób pierwotny koszt wywołania z podanymi parametrami ze wszystkich oprócz pierwszego wywołania funkcji z tymi parametrami.
Z grubsza mówiąc , retpolina jest trampoliną ze zwrotem jako „ thunk” , aby „ zepsuć ” zapamiętywanie w pośrednim predyktorze gałęzi.
Źródło : Retpolina zawiera instrukcję PAUSE dla Intela, ale dla AMD konieczna jest instrukcja LFENCE, ponieważ na tym procesorze instrukcja PAUSE nie jest instrukcją serializacji, więc pętla pauzy / jmp zużyje nadmierną moc, ponieważ spekuluje się na oczekiwanie na powrót źle przewidzieć właściwy cel.
Arstechnica ma proste wyjaśnienie problemu:
Z dokumentu Intela: „ Retpoline: A Branch Target Injection Mitigation ” ( .PDF ):
źródło