Ken Thompson Hack (1984)
Ken Thompson przedstawił metodę uszkodzenia binarnego kompilatora (i innego skompilowanego oprogramowania, takiego jak skrypt logowania w systemie * nix) w 1984 roku. Byłem ciekawy, czy współczesna kompilacja usunęła tę lukę w zabezpieczeniach, czy nie.
Krótki opis:
Ponownie napisz kod kompilatora, aby zawierał 2 błędy:
- Podczas kompilacji własnego pliku binarnego kompilator musi skompilować te wady
- Kompilując jakiś inny wybrany kod (funkcja logowania) musi skompilować dowolny dowolny backdoor
W ten sposób kompilator działa normalnie - kiedy kompiluje skrypt logowania lub podobny, może stworzyć backdoor bezpieczeństwa, a kiedy kompiluje swoje nowsze wersje w przyszłości, zachowuje poprzednie wady - a wady będą istnieć tylko w kompilatorze binarne, więc są niezwykle trudne do wykrycia.
Pytania:
W sieci nie mogłem znaleźć odpowiedzi na te pytania:
- Jak to się ma do kompilacji just-in-time?
- Czy funkcje takie jak program obsługujący logowanie w systemie * nix są kompilowane po uruchomieniu?
- Czy wciąż jest to uzasadnione zagrożenie, czy też od 1984 r. Pojawiły się zmiany w bezpieczeństwie kompilacji, które uniemożliwiają poważny problem?
- Czy to wpływa na wszystkie języki?
Dlaczego chcę wiedzieć?
Natknąłem się na to podczas odrabiania lekcji i wydawało mi się to interesujące, ale brakuje mi tła, aby zrozumieć w konkretny sposób, czy jest to bieżący problem, czy rozwiązany problem.
Odpowiedzi:
Ten hack należy rozumieć w kontekście. Został opublikowany w czasie i kulturze, w której Unix działający na różnego rodzaju sprzęcie był dominującym systemem.
Co sprawiło, że atak był tak straszny, że kompilator C był centralny kawałek oprogramowania dla tych systemów. Niemal wszystko w systemie przeszło przez kompilator podczas jego pierwszej instalacji (dystrybucje binarne były rzadkie ze względu na heterogeniczny sprzęt). Wszyscy cały czas kompilowali. Ludzie regularnie sprawdzali kod źródłowy (często musieli wprowadzać poprawki, aby kompilować go w ogóle), więc kompilowanie backdoora kompilatora wydawało się rodzajem scenariusza „idealnego przestępstwa”, w którym nie można Cię złapać.
W dzisiejszych czasach sprzęt jest znacznie bardziej kompatybilny, a zatem kompilatory odgrywają znacznie mniejszą rolę w codziennej pracy systemu. Zaatakowany kompilator nie jest już najbardziej przerażającym scenariuszem - rootkity i skompromitowany BIOS są jeszcze trudniejsze do wykrycia i pozbycia się.
źródło
Celem tego przemówienia nie było podkreślenie podatności, którą należy usunąć, ani nawet zaproponowanie teoretycznej podatności, o której musimy wiedzieć.
Chodziło o to, że jeśli chodzi o bezpieczeństwo, nie chcielibyśmy nikomu ufać, ale niestety jest to niemożliwe. Ty zawsze musisz zaufać komuś (stąd tytuł: „Refleksje ufność Trust”)
Nawet jeśli jesteś typem paranoika, który szyfruje dysk twardy komputera i odmawia uruchomienia oprogramowania, którego sam nie skompilowałeś, nadal musisz zaufać swojemu systemowi operacyjnemu. Nawet jeśli sam skompilujesz system operacyjny, nadal musisz zaufać używanemu kompilatorowi. Nawet jeśli skompilujesz własny kompilator, nadal musisz zaufać temu kompilatorowi! I nie wspominając nawet o producentach sprzętu!
Po prostu nie można uciec od ufania nikomu . Właśnie o to starał się przejść.
źródło
Nie
Atak, jak pierwotnie opisano, nigdy nie stanowił zagrożenia. Chociaż teoretycznie kompilator mógłby to zrobić, faktyczne przeprowadzenie ataku wymagałoby zaprogramowania kompilatora
Wymaga to zrozumienia, jak działa kompilator na podstawie kodu źródłowego, aby mógł go modyfikować bez uszkodzenia.
Wyobraź sobie na przykład, że format łączenia przechowuje długości danych lub przesunięcia skompilowanego kodu maszynowego gdzieś w pliku wykonywalnym. Kompilator musiałby sam ustalić, które z nich wymagają aktualizacji i gdzie, przy wstawianiu ładunku exploita. Kolejne wersje kompilatora (wersja nieszkodliwa) mogą dowolnie zmieniać ten format, więc kod exploita musiałby skutecznie rozumieć te pojęcia.
Jest to samodzielne programowanie na wysokim poziomie, trudny problem sztucznej inteligencji (ostatnio sprawdziłem, że stan techniki generuje kod, który jest praktycznie określony przez jego typy). Spójrz: niewielu ludzi może to zrobić; musisz nauczyć się języka programowania i najpierw zrozumieć bazę kodu.
Nawet jeśli problem sztucznej inteligencji zostanie rozwiązany, ludzie zauważą, że kompilacja ich małego kompilatora daje plik binarny z ogromną biblioteką sztucznej inteligencji.
Analogiczny atak: zaufanie ładowania początkowego
Jednak uogólnienie ataku jest istotne. Podstawową kwestią jest to, że Twój łańcuch zaufania musi gdzieś zacząć, a w wielu domenach jego pochodzenie może obalić cały łańcuch w trudny do wykrycia sposób.
Przykład, który można łatwo wyciągnąć w prawdziwym życiu
Twój system operacyjny, powiedzmy Ubuntu Linux, zapewnia bezpieczeństwo (integralność) aktualizacji poprzez sprawdzanie pobranych pakietów aktualizacji względem klucza podpisu repozytorium (przy użyciu kryptografii klucza publicznego). Ale to gwarantuje autentyczność aktualizacji tylko wtedy, gdy możesz udowodnić, że klucz do podpisywania jest własnością legalnego źródła.
Skąd masz klucz do podpisu? Po pierwszym pobraniu dystrybucji systemu operacyjnego.
Musisz ufać, że źródło twojego łańcucha zaufania, ten klucz do podpisywania, nie jest złe.
Każdy, kto może MITM połączyć się z Internetem między tobą a serwerem pobierania Ubuntu - może to być twój dostawca usług internetowych, rząd kontrolujący dostęp do Internetu (np. Chiny) lub dostawca hostingu Ubuntu - mógł przejąć ten proces:
Odtąd będziesz otrzymywać swoje aktualizacje bezpiecznie z serwera atakującego. Aktualizacje działają jako root, więc atakujący ma pełną kontrolę.
Możesz zapobiec atakowi, upewniając się, że oryginał jest autentyczny. Wymaga to jednak sprawdzenia poprawności pobranego obrazu płyty CD za pomocą skrótu ( niewiele osób to robi ) - a sam skrót musi zostać bezpiecznie pobrany, np. Przez HTTPS. A jeśli osoba atakująca może dodać certyfikat na komputerze (często w środowisku korporacyjnym) lub kontrolować urząd certyfikacji (np. Chiny), nawet HTTPS nie zapewnia ochrony.
źródło
Po pierwsze, mój ulubiony opis tego hacka to Strange Loops .
Ten konkretny hack mógłby z pewnością (*) zostać dzisiaj wykonany w jednym z głównych projektów systemów operacyjnych typu open source, szczególnie Linux, * BSD i tym podobne. Spodziewałbym się, że zadziała prawie identycznie. Na przykład pobierasz kopię FreeBSD, która ma wyzyskiwany kompilator do modyfikacji openssh. Odtąd problem będzie kontynuowany przy każdej aktualizacji openssh lub kompilatora według źródła. Zakładając, że atakujący wykorzystał system użyty do spakowania FreeBSD w pierwszej kolejności (prawdopodobnie, ponieważ sam obraz jest uszkodzony, lub atakujący jest w rzeczywistości programem pakującym), to za każdym razem, gdy system odbudowuje pliki binarne FreeBSD, ponownie pojawi się problem. Istnieje wiele sposobów niepowodzenia tego ataku, ale nie różnią się one zasadniczo od tego, w jaki sposób atak Kena mógł się nie udać (**). Świat naprawdę niewiele się zmienił.
Oczywiście podobne ataki mogą być równie łatwo (lub łatwiej) wstrzyknięte przez ich właścicieli do systemów takich jak Java, iOS SDK, Windows lub inny system. Niektóre rodzaje błędów bezpieczeństwa można nawet wprowadzić w sprzęcie (szczególnie osłabienie generowania liczb losowych).
(*) Ale przez „z pewnością” mam na myśli „w zasadzie”. Czy należy oczekiwać, że tego rodzaju dziura istnieje w jakimś konkretnym systemie? Nie. Uważam to za mało prawdopodobne z różnych praktycznych powodów. Z biegiem czasu, wraz ze zmianami i kodem, wzrasta prawdopodobieństwo, że tego rodzaju włamanie spowoduje dziwne błędy. A to zwiększa prawdopodobieństwo, że zostanie to odkryte. Mniej pomysłowe backdoory wymagałyby konspiracji do utrzymania. Oczywiście wiemy na pewno, że backdoory „zgodne z prawem” zostały zainstalowane w różnych systemach telekomunikacyjnych i sieciowych, więc w wielu przypadkach taki skomplikowany hack nie jest konieczny. Włamanie jest instalowane jawnie.
Tak więc zawsze głęboka obrona.
(**) Zakładając, że atak Kena kiedykolwiek istniał. Właśnie przedyskutował, jak można to zrobić. Nie powiedział, że faktycznie to zrobił, o ile wiem.
źródło
Czy to wpływa na wszystkie języki?
Ten atak dotyczy przede wszystkim języków, które same się hostują. To są języki, w których kompilator jest napisany w samym języku. C, Squeak Smalltalk i interpreter PyPy Python będą miały na to wpływ. Perl, JavaScript i interpreter języka Python CPython nie.
Jak to się ma do kompilacji just-in-time?
Nie bardzo. Jest to kompilator, który pozwala na ukrywanie włamań. Nie znam żadnych samodzielnych kompilatorów JIT. (Może LLVM?)
Czy funkcje takie jak program obsługujący logowanie w systemie * nix są kompilowane po uruchomieniu?
Zazwyczaj nie. Ale pytanie nie brzmi, kiedy jest kompilowane, ale według jakiego kompilatora . Jeśli program logowania zostanie skompilowany przez skażony kompilator, zostanie on skażony. Jeśli zostanie skompilowany przez czysty kompilator, będzie czysty.
Czy wciąż jest to uzasadnione zagrożenie, czy też od 1984 r. Pojawiły się zmiany w bezpieczeństwie kompilacji, które uniemożliwiają poważny problem?
To wciąż teoretyczne zagrożenie, ale mało prawdopodobne.
Jedną z rzeczy, które możesz zrobić, aby to złagodzić, jest użycie wielu kompilatorów. Na przykład kompilator LLVM, który sam jest kompilowany przez GCC, nie przejdzie tylnymi drzwiami. Podobnie GCC skompilowana przez LLVM nie przejdzie tylnymi drzwiami. Jeśli więc martwisz się tego rodzaju atakiem, możesz skompilować kompilator z innym rodzajem kompilatora. Oznacza to, że zły haker (u twojego dostawcy systemu operacyjnego?) Będzie musiał skazić oba kompilatory, aby się rozpoznali; Znacznie trudniejszy problem.
źródło
Istnieje teoretyczna szansa na to. Istnieje jednak sposób sprawdzenia, czy jakiś kompilator (z dostępnym kodem źródłowym) nie został naruszony, dzięki podwójnej kompilacji Diverse Davida A. Wheelera .
Zasadniczo użyj zarówno podejrzanego kompilatora, jak i innego niezależnie opracowanego kompilatora, aby skompilować źródło podejrzanego kompilatora. To daje SC SC SC T . Teraz skompiluj podejrzane źródło, używając obu tych plików binarnych. Jeśli wynikowe pliki binarne są identyczne (z wyjątkiem wielu rzeczy, które mogą się legalnie różnić, takich jak różne znaczniki czasu), podejrzany kompilator nie nadużywał zaufania.
źródło
Jako konkretny atak stanowi tak samo duże zagrożenie, jak nigdy dotąd, co w zasadzie nie stanowi żadnego zagrożenia.
Nie jestem pewien, co przez to rozumiesz. Czy JITter jest na to odporny? Nie. Czy to jest bardziej wrażliwe? Nie całkiem. Jako programista Twoja aplikacja jest bardziej podatna na ataki po prostu dlatego, że nie możesz sprawdzić, czy nie zostało to zrobione. Zauważ, że twoja jeszcze nierozwinięta aplikacja jest w zasadzie odporna na tę i wszystkie praktyczne odmiany, musisz tylko martwić się o kompilator, który jest nowszy niż twój kod.
To nie jest tak naprawdę istotne.
Nie ma prawdziwego bezpieczeństwa kompilacji i nie może być. To był właśnie sens jego rozmowy, że w pewnym momencie musisz komuś zaufać.
Tak. Zasadniczo w pewnym momencie instrukcje muszą zostać przekształcone w coś, co komputer wykonuje, a tłumaczenie może być wykonane niepoprawnie.
źródło
David Wheeler ma dobry artykuł: http://www.dwheeler.com/trusting-trust/
Ja bardziej martwię się atakami sprzętowymi. Myślę, że potrzebujemy łańcucha narzędzi do projektowania w całości VLSI z kodem źródłowym FLOSS, który sami możemy modyfikować i kompilować, co pozwala nam budować mikroprocesor, w którym narzędzia nie wstawiają tylnych drzwi. Narzędzia powinny również pozwolić nam zrozumieć cel każdego tranzystora w układzie. Następnie moglibyśmy otworzyć próbkę gotowych układów i sprawdzić je pod mikroskopem, upewniając się, że mają ten sam zespół obwodów, co narzędzia, które według nich powinny mieć.
źródło
Systemy, w których użytkownicy końcowi mają dostęp do kodu źródłowego, są tymi, dla których musiałbyś ukryć ten rodzaj ataku. W dzisiejszym świecie byłyby to systemy typu open source. Problem polega na tym, że chociaż istnieje zależność od jednego kompilatora dla wszystkich systemów Linux, atak musiałby dostać się na serwery kompilacji dla wszystkich głównych dystrybucji Linuksa. Ponieważ nie pobierają one plików binarnych kompilatora bezpośrednio dla każdej wersji kompilatora, źródło ataku musiałoby znajdować się na ich serwerach kompilacji w co najmniej jednej poprzedniej wersji kompilatora. Zarówno ta, jak i pierwsza wersja kompilatora pobranego jako plik binarny musiałaby zostać zagrożona.
źródło
Jeśli ktoś ma kod źródłowy dla kompilatora / systemu kompilacji, którego dane wyjściowe nie powinny zależeć od niczego poza zawartością dostarczonych plików źródłowych, i jeśli ktoś ma kilka innych kompilatorów i wie, że nie wszystkie zawierają ten sam hack kompilatora, można upewnij się, że otrzymujesz plik wykonywalny, który nie zależy od niczego innego niż kod źródłowy.
Załóżmy, że ktoś ma kod źródłowy pakietu kompilatora / linkera (powiedzmy Groucho Suite) napisany w taki sposób, że jego wynik nie będzie zależał od żadnych nieokreślonych zachowań, ani od niczego innego niż zawartość wejściowych plików źródłowych, a jeden kompiluje / łączy ten kod w różnych niezależnie produkowanych kompilatorach / pakietach łączących (np. Harpo Suite, Chico Suite i Zeppo Suite), uzyskując dla każdego inny zestaw ekwiwalentów (nazywaj je G-Harpo, G-Chico i G-Zeppo). Nie byłoby niespodzianką, że te pliki wykonywalne zawierają różne sekwencje instrukcji, ale powinny być funkcjonalnie identyczne. Jednak udowodnienie, że są one funkcjonalnie identyczne we wszystkich przypadkach, prawdopodobnie stanowiłoby trudny problem.
Na szczęście taki dowód nie będzie konieczny, jeśli użyje się wynikowych plików wykonywalnych tylko w jednym celu: ponownej kompilacji pakietu Groucho. Jeśli jeden kompiluje pakiet Groucho za pomocą G-Harpo (uzyskując GG-Harpo), G-Chico (GG-Chico) i G-Zeppo (GG-Zeppo), wówczas wszystkie trzy wynikowe pliki, GG-Harpo, GG-Chico i GG-Zeppo, wszystkie bajty po bajcie powinny być identyczne. Jeśli pliki się zgadzają, oznaczałoby to, że każdy „wirus kompilatora”, który istnieje w jednym z nich, musi istnieć identycznie we wszystkich (ponieważ wszystkie trzy pliki są identyczne bajt po bajcie, nie ma możliwości, aby ich zachowanie mogło się różnić droga).
W zależności od wieku i pochodzenia innych kompilatorów może być możliwe zapewnienie, że taki wirus nie będzie w nich istniał. Na przykład, jeśli ktoś użyje antycznego Macintosha, aby nakarmić kompilator napisany od zera w 2007 r. Za pomocą wersji MPW napisanej w latach 80., kompilatory z lat 80. nie będą wiedziały, gdzie wstawić wirusa w kompilatorze z 2007 r. Możliwe, że dziś kompilator może wykonać wystarczająco wymyślną analizę kodu, aby to rozgryźć, ale poziom obliczeń wymagany do takiej analizy znacznie przekroczyłby poziom obliczeń wymagany do zwykłego skompilowania kodu i nie mógłby nie zostać niezauważony na rynku, gdzie szybkość kompilacji była głównym punktem sprzedaży.
Zakładam, że jeśli ktoś pracuje z narzędziami do kompilacji, w których bajty w pliku wykonywalnym, który ma zostać wygenerowany, nie powinno w żaden sposób zależeć od niczego poza zawartością przesłanych plików źródłowych, możliwe jest uzyskanie względnie dobrej odporności na Thompson wirus typu. Niestety z jakiegoś powodu niedeterminizm w kompilacji wydaje się być uważany za normalny w niektórych środowiskach. Rozumiem, że w systemie wieloprocesorowym kompilator może działać szybciej, jeśli pewne aspekty generowania kodu mogą się różnić w zależności od tego, który z dwóch wątków kończy pracę jako pierwszy.
Z drugiej strony nie jestem pewien, czy widzę jakiś powód, dla którego kompilatory / konsolidatory nie powinny zapewniać trybu „kanonicznego wyjścia”, w którym dane wyjściowe zależą tylko od plików źródłowych i „daty kompilacji”, która może zostać zastąpiona przez użytkownika . Nawet jeśli kompilacja kodu w takim trybie zajęła dwa razy więcej czasu niż normalna kompilacja, sugerowałbym, że istotną wartością byłoby odtworzenie „kompilacji wydania”, bajt po bajcie, całkowicie z materiałów źródłowych, nawet jeśli oznaczałoby to, że kompilacje wersji zajęłyby dłużej niż „normalne kompilacje”.
źródło