Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Reverse Engineering w praktyce - część 6

ghost666 25 Lut 2017 16:27 2385 0
  • Reverse Engineering w praktyce - część 6
    W https://www.elektroda.pl/rtvforum/viewtopic.php?p=16301939#16301939" target="_blank" class="postlink ">przedostatniej części cyklu próbowaliśmy odnaleźć w kodzie firmware, algorytmu generacji domyślnego hasła i SSID sieci Wi-Fi routera. Kody źródłowe firmware pozskalismy częściowo ze strony producenta (gdyż są to elementy na licencji GPL) a częściowo wykorzystując deasembler IDA, dostosowany do kodu dl procesorów MIPS, jaki znajduje się w badanym przez nas urządzeniu. Niestety odnaleźć algrytmu nam się nie udało, jednakże skoro mamy już przed sobą dezasemblowane źródla binarne firmware urządzenia, warto jest zajrzeć do nich aby zobaczyć co jeszcze ciekawego się ta kryje.

    W szóstej, ostatniej części artykułu skupimy się na analizie kodów źródłowych pod kątem znalezienia w nim – głównie – dziur w zabezpieczeniach. Mając zdekompilowane binarki możemy także poszukać i innych informacji.

    Podatność na wstrzykiwanie komend

    Jedną z częstszych i łatwych do odnalezienia dziur jest podatność na wstrzykiwanie komend poprzez np. interfejs użytkownika. Sama idea tego ataku jest prosta – załóżmy, że podajemy gdzieś tekst, który następnie wykorzystywany jest do skonstruowania komeny dla powłoki systemu. Jeśli teraz podamy jako ten tekst jakąś komendę, to zostanie ona wykonana z pominięciem wszystkich, zaimplementowanych przez developerów zabezpieczeń w interfejsie użytkownika. Tego rodzaju ataki pozwalają, zwłaszcza w systemach wbudowanych, bardzo łatwo uzyskać uprawnienia Roota w systemie.

    Dlaczego to systemy wbudowane są tak podatne na te ataki? Z uwagi na ograniczoną pamięć, która uniemożliwia zaimplementowanie bardziej rozbudowanych zabezpieczeń. Załóżmy taki przykład – projektujemy system wbudowany konfigurowany poprzez interfejs webowy; aby ułatwić debugowanie jego pracy w Siecie chcemy dodać możliwość wysłania pinga na zadany adres IP z poziomu interfejsu webowego. Dodajemy więc okienko gdzie wpisać możemy adres IP, jaki ma być fingowany. Taka funkcja jest niezwykle przydatna i prosta do implementacji, bo sama funkcja ping wbudowana jest w system. Tak może wyglądać przykładowa implementacja:

    Reverse Engineering w praktyce - część 6






    Oczywiście wbudowana w system funkcja ping to nie jedyna opcja; możem wykorzystać bibliotekę z zapisaną obsugą protokoło ICMP i wywoływać ją z poziomu back-endu interfejsu webowego. Jednakże skorzystanie z komendy w systemie jest o wiele prostsze – wystarcz jedna linijka, aby przesłać do Shella komendę ping wraz z dołączonym adresem, jaki wpisaliśmy w okienku. Właśnie z uwagi na prostotę, mniejsze zużycie pamięci itp. to drugie rozwiązanie będzie o wiele popularniejsze. Sprawdźmy, czy aplikacja webowa w systemie tego routera korzysta z tego rozwiązania. Jeśli tak, to router może być podatny na wstrzykiwanie kodu poprzez to narzędzie webowe – sprytnie wpisana w okienko dla numeru IP komenda zostanie także wykonana przez powłokę systemu, Spójrzmy na aplikację znajdujące się w /bin/web:

    Reverse Engineering w praktyce - część 6


    Odwołanie do komendy system() biblioteki libc (nie mylić z syscallem – wywołaniem systemowym) jest najprostszym sposobem na uruchomienie polecenia z linii komend. Czasami developerzy ubierają samo system() w dodatkowe funkcje, co ma pozwolić na dodatkową filtrację wejścia, ale bardzo często – znając kod programu – wiemy jak te filtry obejść. A może okazać się też, że w tej implementacji takich filtrów nie ma w ogóle.

    Poszukiwanie odniesienia do biblioteki systemowej w binarce jest doskonałm sposobem na odnalezienie miejsc, będących wektorami ataku, który potem możemy wykorzystać. Poniżej znajdują się informacje o wszelkich odniesieniach do system() w binarium /bin/Web:

    Reverse Engineering w praktyce - część 6


    Nawet nazwy funkcji mogą dać nam wskazówki, czy dana funkcja będąca odniesieniem do system() przekazuje jakieś informacje użytkownika. Możemy także pomiędzy tymi referencjami odnaleźć odniesienia także do danych takich jak numery PIN, PUK czy karty SIM… wydawać by się mogło, że ta aplikacja działa także w jakimś produkcie mobilnym.

    Filtracja wejścia do funkcji systemowych zrealizowana jest poprzez funkcję atp_gethostbyname – funkcja ta zwróci błąd gdy na wejściu jej pojawi się nieprawidłowa wartość. Niestety analiza tej funkcji nie pomogła wiele w zakresie znalezienia możliwości wstrzyknięcia kodu do polecenia systemowego. Może dalsza analiza kodów źródłowyc pozwoli nam odnaleźć inną metodę na realizację takiego ataku.

    Reverse Engineering w praktyce - część 6


    Jeśli udało by się zrealizować wpisane powyżej komendy to system wykonałby:

    [source=bash]ping google.com -c 1; reboot; ping 192.168.1.1 > /dev/null[/source]

    Jeśli router zresetuje się po podaniu tej komendy, to znaczy że znaleźliśmy sposób na atak. Niestety nie zadziałało to tak, jakbyśmy chcieli. Moglibyśmy weryfikować w ten każde wejście tekstowe w interfejsie webowym routera, ale raczej nie da nam to dostępu do systemu.

    Innym miejscem, które pozwolić może na zdalne wykonywanie komend systemowych jest protokół „LAN-Side DSL CPE Configuration”, czyli TR-064. Jakkolwiek zaprojektowany został on do konfiguracji routerów po sieci wewnętrznej, to w przeszłości wykorzystywany był do konfiguracji ich po seci. Dziury w niektórych implementacjach tego protokoły były wykorzystywane do wyciągnięcia z routera danych do logowania do sieci Wi-Fi z wykorzystaniem zaledwie kilku pakietów.

    W tym routerze znaleźć możemy aplikację /bin/tr064; możemy bliżej przyjrzeć się jej działaniu, w jej funkcji main() znaleźć możemy następującą funkcję:

    Reverse Engineering w praktyce - część 6


    Jest to prywatny klucz RSA, ten sam który odnaleźliśmy w części drugiej i który wykorzystywany jest do uwierzytelniania SSL. Wykorzystując tą wiedzę możemy sprawdzić w jaki sposób router mogą być zdalnie konfigurowane, szczególnie że namy potrzebny do tego klucz.

    Poszukiwanie bardziej skomplikowanych dzur w zabezpieczeniach

    Nawet, jeżeli nie uda nam się znaleźć żadnego miejsca w kodzie, które wskazywałoby na podatność na wstrzyknięcie komendy przez standardowe wejścia interfejsu routera, to istnieje szereg innych potencjalnych wektorów ataku na system tego urządzenia. Jednym z popularniejszych jest mechanizm związany z przepełnieniem bufora. Niezależnie od zastosowania każdy string wchodzący do systemu – komenda, parametr ustawień etc – jest przetwarzana i modyfikowana w kodzie programu. Błąd developerów, polegający na niepoprawnym obliczeniu długości potrzebnego bufora, nie sprawdzaniu jego poprawności etc może zaskutkować przepełnieniem bufora, co atakujący system może wykorzystać do zdobycia kontroli nad systemem operacyjnym.

    Pomysł stojący za mechanizmem ataku związanym z przepełnieniem bufora systemu jest dosyć prosty. Jeśli uda nam się przekazać do systemu jakikolwiek string zawierający wykonywalny kod, to może on zostać wykonany – jak? Konieczne jest nadpisanie kilku adresów, tak, że kod, który właśnie wstrzyknęliśmy, znajdzie się pod nowym adresem do którego skakać będzie system. W takiej sytuacji możemy zrobić wszystko co możliwe z kodem w postaci binarnej. W systemie wbudowanym, takim jak ten, wszystko pracuje w zasadzie z prawami roota, co daje nam niemalże nieograniczony dostęp do wszystkiego w systemie.

    Reverse Engineering w praktyce - część 6


    Opracowanie tego rodzaju exploita nie jest tak proste, jak wstrzyknięcie komendy na wejście dowolnej funkcji, która nie jest wyposażona w filtr weściowy. Istnieje wiele potencjalnych scenariusz, tak więc potrzebne jest wiele różnych technik, aby wykorzystać tego rodzaju błąd. W wielu wypadkach wymaga to wykorzystania jezcze bardziej skomplikowanych mechanizmów ataków.

    Pocieszający jednakże jest fakt, że urządzenia wbudowane, zwłaszcza domowe, są wiele wiele lat za komputerami, jeśli chodzi o zabezpieczenia przed tego rodzaju atakami, co oznacza, że technologie takie jak na przykład Losowanie Rozkładu Przestrzeni Adresowej (ASLR) w systemach wbudowanych nie występują lub są wyłączone. Istotnie ułatwia to opracowanie explitów na tego rodzaju urządzenia.

    Jeśki chcecie zagłębić się w opracowywanie tego rodzaju ataków na urządzenia na własną rękę wystarczy wykorzystać techniki zaprezentowane powyżej: wybrać potencjalny wektor ataku, odnaleźć kod związany z danym wejściem i zanalizować jego działanie, nazwy funkcji, zapisane wewnątrz programu stringi etc. Posiadając tą wiedzę możliwe jes takie skontruowanie zapytania, które spowoduje niepoprawne zadziałanie urządzenia, a jeśli amy trochę szczęścia to pozwoli nam to także na atak, jeśi dany bug możliwy będzie do wykorzystania.

    Po zlokalizowaniu fragmentu deasemblowanego kod, odpowiedzialnego za ‘obróbkę’ wejściowego stringa rozpocząć naszą analizę możemy od sprawdzenia jakie operacje są wykonywane na wejściu. Najpewniej string ten jest przetwarzany z wykorzystaniem komend takich jak strcpy, strat czy sprintf, albo też ich bezpieczniejszym odmianami, takimi strncpy, strncat itp. Niezależnie od wykorzystanych funkcji wszystkie z nich posiadają jakieś luki, które można wykorzystać, wszystko jest jedynie kwestią poziomu skomplikowania opracowywanego exploita.

    Reverse Engineering w praktyce - część 6


    Nawet pomimo faktu, że nie wiadomo nam która funkcja wyciągnięta z /bin/tr065 przekazywana jest jako wejście użytkownika to nadal jest dobry kod, który można wykorzystać w celu zrozumienia czego powinniśmy poszukiwać podczas naszych analiz. Kiedy odnajdziey już operację na stringu, która nie jest w pełni zabezpieczona, wystarczy tylko zanalizować czy ten bug nadaje się do wykorzystania podczas jakiegoś ataku.

    Jeśli odnaleźliśmy takie miejsce to zacznijmy od najprostszego – wyślijmy string który zawiesi system (albo przynajmniej powinien). Jeśi tak się stało, to już jesteśmy na dobrej drodze. Ile znaków da się zmieścić, bez powodowania zawieszenia systemu? Co można zmieścić w tych znakach? Gdzie do pamięci trafiają te znaki? etc. Można by pisać o tym książki, ale sama idea jest dosyć oczywista. W Internecie odnaleźć można wiee informacji na ten temat, jeśli jesteście zainteresowani.

    Nie spędzajmy jednakże zbyt wiele czasu na jedynie oczywistymi wejściami. One będą najpewniej dobrze chronione. Istnieje szeroka gama narzędzi do ataków poprzez sieć Web, proxy etc. Możemy z wykorzystaniem takowych modyfikować pola ciasteczek itp. aby sprawdzić jak system reaguje na przepełnianie się bufora.

    Jeśli wykorzystujemy interfejs webowy urządzenia możey także zanalizować go pod kątem podatności na inne ataki. Istnieje wiele metod i wektorów ataków opisanych dla różnych systemów. Pozwalają one często na np. zapisanie czegoś w pamięci, wykonania komendy bez konieczności uwierzytelnianai się czy nawet na zupełne przejęcie kontroli nad urządzenie (szczególnie, gdy możemy jednocześnie wstrzykiwać komendy z wykorzystaniem jakiegoś innego wektora.

    Z uwagi na fakt, że istnieje możliwość obejścia konieczności uwierzytelniania się podczas wstrzykiwania komend do routera, przez interfejs webowy, który widoczny jest od strony sieci, t bardzo istotny wektor ataku. Pozwala on zdobyć prawa do routera zdalnie, z sieci, albo też wykorzystać go do ataku typu Man-in-the-middle, gdzie właśnie router będzie tym człowiekiem w środku. Nie będziemy omawiać dokładniej jak zrealizować tego rodzaju atak, ale warto zaznajomić się z pewnymi podstawami.

    Dekompilowanie plików binarnych (trochę teorii)



    When you decompile a binary, instead of simply translating Machine Code to Assembly Code, the decompiler uses algorithms to identify functions, loops, branches, etc. and replicate them in a higher level language like C or Python.

    That sounds like a brilliant idea for anybody who has been banging their head against some assembly code for a few hours, but an additional layer of abstraction means more potential errors, which can result in massive wastes of time.

    In my (admittedly short) personal experience, the output just doesn’t look reliable enough. It might be fine when using expensive decompilers (IDA itself supports a couple of architectures), but I haven’t found one I can trust with MIPS binaries. That being said, if you’d like to give one a try, the RetDec online decompiler supports multiple architectures- including MIPS.

    Reverse Engineering w praktyce - część 6


    Even as a ‘high level’ language, the code is not exactly pretty to look at.

    Kolejne kroki

    Na tym kończy się ten dosyć długi cykl artykułów. W przyszłości spodziewać można się kolejnych częściach, zważywszy, że jego autorzy pracują nad kolejnymi urządzeniami, m.in. Amazon Echo i routerem z interfejsem JTAG.

    Dodatkowe porady

    Niezależne czy chcemy analizować zdobyte kody źródłowe, aby dowiedzieć się jak działa dane urządzenie, debugować je czy szukać dziur w zabezpieczeniach, możliwość wykonywania zdobytego kodu jest niezwykle przydatna. Szczególności, jeśli mamy jeszcze możliwość wykorzystać debuger. Niestety w większości przypadków uruchamianie i debugwanie kodu na źródłowym urządzeniu nie jest możliwe, więc pracować musimy na emulatorze danego CPU na komputerze osobistym.

    Błędne xref i jak się ich pozbyć[/b]

    Czasami adresy ładowane są do rejestru do dostosowania pomiędzy 16 a 32 bitami. Awartość tego adres nie ma wpływu na resztę kodu, to tylko zwykłe przeliczenie danych. Jeśli zdarzy się, że adres jaki przypisany zostanie do tego rejestru będzie także wskazywał na dane w pamięci do IDA zmieni nazwę adresu w pliku a assemblerem i wyświetli w komentarzy zawartość tego rejestru,

    To w gestii użytkownika leży określenie czy dany xref ma sens. Jeśli nie ma to wystarczy wybrać zmienną i nacisnąć w środowisku IDA przycisk, nakazujący mu zignorować zawartość rejestru i wyświetlić edynie adres. Uczyni to kod o wiele czytelniejszym.

    Konfiguracja prototypów funkcji w IDA

    Jeśli każemy środowisku IDA zaczytać prototypy poszczególnych funkcji to program wyświetlać będzie nam komentarze odnoszące się do argumentów funkcji przy każdym odniesieniu do funkcji.

    Aby uruchomić tą opcję wystarczy ustawić kursor na fanej funkcji i nacisnąć klawisz y. Skonfiguruje to prototyp funkcji w pamięci, dzięki temu widzieć będziemy za każdym razem np. int memcpy(void *restrict dst, const void *restrict src, int n);.Uwaga – IDA rozumie tylko wbudowane typy, więc takie typy jak size_t nie zostaną wykorzystane.

    I za każdym razem, gdy odwoływać się będziemy do jakiejś funkcji np. z zewnętrznego kodu na licencji GPL, to IDA wczytywać będzie prototyp tej funkcji i prezentować nam typy funkcji wejściowych.

    Wykorzystywanie otwartych źródeł na licencji GPL

    Jeśli chcemy zrozumieć co jest pierwszym lub drugim parametrem funkcji takiej jak na przykład ATP_DBSetPara to odnieść się możemy o kodu źródłowego tej funkcji, opublikowanego na licencji GPL. Wiele funkcji nie jestem zaimplementowane w jądrze systemu czy innych jego składowych, ale są one składowymi całego systemu. Oznacza to, że nie możemy zobaczyć kodu źródłowego danej funkcji, ale mamy dostęp do prototypu funkcji, jaki zapisany jest w kodzie źródłowym. W ten sposób możemy przy odrobinie starania się odnaleźć całe źródło wraz z komentarzami. Może to być niezwykle przydatne podczas analizy zdesaemblowanego kodu.

    Reverse Engineering w praktyce - część 6


    Często jest tak, że w otrzymanych funkjach nie ma zbyt wiele dokumentacji. W przypadków plików urządzenia od Huawei problemy z kodowaniem pliku sprawiły, że komentarze – napisane po chińsku – po prostu zostały skasowane.

    Metody deasemblacji – przemiatanie liniowe vs zejście rekurencyjne

    Struktura pliku binarnego zależeć może od kompilatora, toolchainu etc. To jak wywoływane są funkcje etc nie jest zawsze tak łatwe do wywnioskowania z kodu z deassemblera. Oznacza to, że będzie mogło istnieć np. sporo funkcji-sierot, które istnieją w binarce, ale nikt się do nich nie odwołuje.

    To jaki wykorzystaliśmy de assembler będzie miało wpływ na to czy zobaczymy te opuszczone funkcje czy nie. Niektóre z nich będą bardzo ważne, na przykład funkcja odpowiedzialna za ping w biharkach interfejsu webowego. To wynika z faktu w jaki sposób różne programy skanują pliki binarne w poszukiwaniu wykonywalnej zawartości. Istnieję dwie główne metody wczytywania zawartości pliku binarnego:

    [u]Przemiatanie liniowe:
    Polega na odczytywaniu danych z binarki bajt po bajcie w sposób liniowy; wszystko co przypomina funkcje zostanie wtedy zaprezentowane użytkownikowi programu
    Zejście rekurencyjne: Ten algorytm zakłada, że znamy punkt ‘wejścia’ do programu z którego to zaczynamy odczyt. Odnajdujemy wszystkie funkcje main() następnie poszukujemy poszczególnych funkcji, jakie są w nich wywoływane. Następnie poszukujemy funkcji, które były wywoływane w wywołwycnyc funkacje etc. W ten sposób znajdujemy rekurencyjnie wszystkie wykorzystane funkcje. Ten algorytm jest bardzo wydajny i działa dobrze dla dowolnego typu binarek, jednakże ma powaną wadę – jeśli jakaś funkcja nie została użyta, mimo że znajduje się w kodzie, to de asembler działający w ten sposób jej nie dostrzeże.

    Najlepiej korzystać jest z programu wspierającego aba algorytmy. W większości wypadków zejście rekurencyjne jest o wiele lepsze, ale jeżeli wydaje nam się, że w kodzie czegoś brakuje, lub nie widzimy pewnych danych lub funkcji, dobrze jest przeanalizować skompilowane źródło jeszcze raz z wykorzystaniem przemiatania liniowego podczas dezasemblacji.

    Źródło: http://jcjc-dev.com/2016/12/14/reversing-huawei-5-reversing-firmware/


    Fajne! Ranking DIY
    Potrafisz napisać podobny artykuł? Wyślij do mnie a otrzymasz kartę SD 64GB.