Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Europejski lider sprzedaży techniki i elektroniki.
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Reverse Engineering w praktyce - część 5

ghost666 23 Lut 2017 19:47 1956 0
  • Reverse Engineering w praktyce - część 5
    W poprzedniej części artykułu skupiliśmy się na ekstrakcji firmware z pamięci Flash naszego routera i jej dekompresji. Jednocześnie, jak zaznaczył autor, cała ta operacja niekoniecznie jest wymagana, ponieważ bardzo często firmware routera lub jego część, dostępne jest na stronie producenta pod postacią aktualizacji oprogramowania urządzenia do pobrania.

    W poniższej, przedostatniej części dalej zagłębiać będziemy się w firmware routera poszukując interesujących algorytmów i potencjalnych dziur w systemie. Jednocześnie autor wyjaśnia sporo teorii, jeśli chodzi o architekturę systemu Linux, deasemblację binarek i inne związane z tymi zagadnieniami koncepcje. Oczywiście można pominąć akapity oznaczone jako teoretyczne.

    Cała analiza kodów i binarek systemu zaczęła się od pomysłu, aby odnaleźć algorytm generacji domyślnego hasła WiFi routera, ale i tak finalnie wszystko sprowadza się po prostu do przeglądania wszystkich danych zawartych w systemie ? tak w postaci tekstowej jak i skompilowanych źródeł. Można tam odnaleźć, korzystając z prostych narzędzi, wiele interesujących danych, które mogą pomóc nam nie tylko zrozumieć zasadę działania systemu tego urządzenia, ale także że odnaleźć np. braki w jego zabezpieczeniach, które następnie można wykorzystać. Ale wszystko krok po kroku?

    Wyszukiwanie, zbieranie i analizowanie otwartych komponentów systemu

    Najpierw zacznijmy od wyjaśnienia co to jest oprogramowanie otwarte czy też dostępne na licencji GPL (trochę teorii). Narzędzia wykorzystane w routerze, takie jak Linux, U-Boot i inne dostępne są na licencji GPL, co oznacza z jednej strony, że są darmowe, ale z drugiej strony, że ich kody źródłowe muszą zostać udostępnione publicznie każdemu, kto tego zechce. W naszym przypadku oznacza to, że kody źródłowe tych narzędzi, jakie zainstalowane są na routerze, są w dużej mierze dostępne w sieci, co daje nam dużą przewagę podczas analizowania binarek, jakie zgraliśmy z routera. Szczególnie interesujące ? pod względem potencjalnych dziur w zabezpieczeniach ? są źródła jądra systemu oraz bootloadera.

    Gdy poszukujemy kodu źródłowego jakiejś aplikacji dostępnej na licencji GPL istnieją trzy możliwości:

    1. Są one publicznie i bezproblemowo dostępne w sieci.
    2. Kody źródłowe dostępne są na wniosek, co oznacza, że firma może albo wysłać je na e-mail na prośbę, albo też zarządzać ?skromnej zapłaty? za dostarczenie nam płyty CD z tymi źródłami.




    3. Firma nie chce udostępnić kodów źródłowych, co jest nielegalne. I niestety w tej sytuacji zrobić można niewiele.

    W przypadku omawianego routera kody źródłowe były dostępne na stronie Huawei. Niełatwo było je znaleźć, ale udało się je pobrać poprzez mobilną wersję strony:

    * Deklaracja zgodności z GPL[/b]
    * [url=http://download-c.huawei.com/download/downloadCenter?downloadId=17643&siteCode=worldwide]Źródła dla HG533

    * Mirror źródeł.

    Reverse Engineering w praktyce - część 5


    Mamy teraz źródła? ale skąd wiedzieć czy firma nic nie zataiła? Czy te źródła to na pewno źródła skompilowanych plików, jakie są na naszym routerze?

    Weryfikacja plików binarnych (trochę teorii)

    W teorii moglibyśmy skompilować samodzielnie źródła i porównać je z tymi, jakie wydobyliśmy ze sprzętu. Jednakże w praktyce jest to rozwiązanie o wiele bardziej skomplikowane niż się początkowo wydaje. Wynika to z faktu, że dokładna zawartość skompilowanego pliku zależy w dużej mierze od środowiska, gdzie plik był kompilowany, wykorzystanego kompilatora, toolchainu etc. Odnalezienie tej samej wersji kompilatora i innych narzędzi z jakich korzystali oryginalni programiści może być bardzo trudne, a nawet jeżeli uda się to nam to skompilowane binarki mogą się od siebie różnić z uwagi na kompilacje na innej (fizycznie) maszynie, w innym czasie etc. nie mówiąc już o innych ścieżkach zależności pomiędzy źródłami itp. Możemy śmiało wykluczyć ten sposób. Jak więc upewnić się można co do prawdziwości źródeł? Nie jest to proste. Jeśli kogoś fascynuje dokładniej ten rozległy temat, to warto zapoznać się z tym artykułem.

    Wprowadzenie do architektury Linuxa (teoria cd.)

    W wielu miejscach cyklu artykułów omawialiśmy różne składowe systemu routera: bootloader, jądro (kernel), system plików i chronione rejony pamięci. Aby zrozumieć gdzie szukać jakich informacji (i dlaczego) dobrze jest poświęcić trochę czasu i pracy na zrozumienie architektury systemu operacyjnego ? Linuxa ? jaki działa na analizowanym przez nas urządzeniu. Spójrzmy na ten uproszczony schemat, pokazujący zależności pomiędzy poszczególnymi komponentami firmware routera:

    Reverse Engineering w praktyce - część 5


    Bootloader jest pierwszym fragmentem kodu, jaki wykonuje się podczas załączenia się routera. Jego zadaniem jest przygotowanie jądra systemu do pracy, uruchomienie go i zatrzymanie swojego własnego wykonywania. Od tego momentu pracę nad systemem przejmuje właśnie jądro, które zajmuje się kontrolowaniem tak sprzętu jak i interakcją z użytkownikiem. Aby lepiej zrozumieć jego działanie musimy wiedzieć trochę o poszczególnych elementach tej ?układanki?, czyli:

    Hardware: Procesor (CPU), pamięci RAM i Flash i inne fizyczne komponenty w układzie, które są ze sobą połączone.
    Kernel Systemu operacyjnego: Ten element ?umie? kontrolować sprzęt. Developerzy piszą dla otwartego jądra sterowniki, dedykowane dla konkretnie tych urządzeń, jakie obecne są w danym systemie i wkompilowywują wszystko w jądro. Kernel zajmuje się zarządzaniem pamięcią, odczytami i zapisami do rejestrów sprzętowych etc. W bardziej rozbudowanych systemach tzw. moduły jądra zajmują się dokładnie tym samym, ale dzięki modułowej konstrukcji można je aktywować i zmieniać w dosyć dowolny sposób bez konieczności rekompilacji samego kernela. Takie rozwiązanie jest jednakże bardziej kosztowne obliczeniowo, więc z oszczędności mocy przetwarzania, zwłaszcza w systemach wbudowanych, developerzy kompilują sterowniki sprzętu wraz z jądrem.
    libc ? ?biblioteka C?: Element ten jest wrapperem ogólnego przeznaczenia dla API odwołań do systemu, włączając w to tak podstawowe funkcje jak printf, malloc czy system. Developerzy mają pełną dowolność oczywiście, aby korzystać z systemowego API bezpośrednio, ale w większości wypadków o wiele wygodniej jest korzystać właśnie z libc lub ? w przypadku mocniejszych urządzeń, z bardzo popularnego GNU libc (glibc). W tym routerze wykorzystano zoptymalizowaną i zminiaturyzowaną wersję tego wrappera uClibc, dedykowanego do małych systemów wbudowanych.

    Aplikacje użytkownika: Są to wszystkie pliki wykonywalne, znajdujące się w folderze /bin/ oraz współdzielone obiekty z folderu /lib/, czyli biblioteki zawierające funkcje, wykorzystywane przez wiele plików wykonywalnych. Pliki te składają się na większość wysokopoziomowej obsługi urządzenia. Wykorzystanie współdzielonych funkcji w bibliotekach pozwala na zaoszczędzenie zasobów urządzenia ? dzięki temu funkcje te się nie dublują.

    Kod źródłowy bootloadera

    Jak w wielu miejscach artykułu już wspominaliśmy jako bootloader tego urządzenia wykorzystany jest U-Boot, dostępny na licencji GPL, jednakże Huawei nie udostępnia kodów źródłowych tej aplikacji na swojej stronie.

    Posiadanie kodu źródłowego bootloadera byłoby przydatne, gdybyśmy chcieli uruchomić na naszym routerze własny firmware lub zmodyfikować istniejący. Niektóre bootloadery wyposażone są w wiele funkcji, jednakże U-Boot jest dosyć ubogi i nie ma w nim w zasadzie nic, co by dla nas było wartościowe, dlatego nie będziemy zajmować się kodem źródłowym tej aplikacji.

    Kod źródłowy jądra systemu

    Spójrzmy teraz na kod źródłowy kernela i zobaczmy, czy jest tam coś, co może nam pomóc. Pamiętacie przycisk resetu do ustawień fabrycznych? To element warstwy sprzętowej, zwykłe GPIO, obsługiwane przez odpowiedni sterownik w jądrze. Podczas resetowania urządzenia do ustawień fabrycznych na porcie UART widzieliśmy w jednej z poprzednich części następujące informacje:

    Reverse Engineering w praktyce - część 5


    Jeśli teraz wykorzystamy te informacje do skonstruowanie prostego zapytania do grepa, którym przeszukamy kody źródłowe, możemy wydobyć ze źródeł fragment systemu, odpowiedzialny za ? przynajmniej ? generację tych informacji, jakie zobaczyliśmy na UARTcie:

    Reverse Engineering w praktyce - część 5


    Mając kody źródłowe jądra jesteśmy w stanie znaleźć wiele algorytmów, które są słabo zabezpieczone. Wiele z tych słabości zabezpieczeń są uwzględniane przez producenta jako ?akceptowalne ryzyko?. Dodatkowo, mając te źródła jesteśmy w stanie napisać i kompilować własny system operacyjny na to urządzenie:

    Kody źródłowe przestrzeni użytkownika

    Jak widzimy w nocie producent, część innych komponentów wykorzystanych w systemie to także aplikacje dostępne na licencji GPL. Są to m.in. busybox oraz iptables. Jeśli programy te są w (nie)odpowiedniej wersji to możemy odnaleźć w nich znane publicznie dziury w zabezpieczeniach ? wystarczy poszukać w dedykowanych do tego bazach danych dostępnych w sieci.

    Wiedząc o tym możemy chcieć szukać bugów zerowego dnia, backdoory czy innych danych wrażliwych. Niestety otwarte projekty to nie jest najlepsze miejsce do szukania tego rodzaju dziur ? są one dobrze zdebugowane i przetestowane. Tego rodzaju rzeczy szukać możemy raczej w programach napisanych przez wewnętrznych developerów producenta urządzenia ? nie zawsze są one dostatecznie przetestowane przez programistów i czasami wprost roi się w nich od bugów. Większość z tych aplikacji znajduje się w przestrzeni użytkownika w systemie plików w skompilowanej formie. Jak pamiętamy udało nam się te binarki zgrać z systemu plików routera. Jedyne co nam teraz pozostaje to znaleźć jakiś sposób, który pozwoli nam na odczytanie kodu maszynowego i przedstawienie go w sposób zrozumiały dla człowieka. Tutaj przyda nam się deasembler.

    Deasemblacja plików binarnych (trochę teorii)

    Wewnątrz każdej binarki ? aplikacji ? znajduje się wygenerowana przez kompilator lista instrukcji w języku maszynowym, które przetwarzane są przez procesor. Jeśli zajrzymy do karty katalogowej procesora, jaki zawarty jest w systemie, odnaleźć bez problem możemy reprezentację w kodzie maszynowym poszczególnych mnemoników assemblera. Program do deasemblacji wbudowany ma w siebie tego rodzaju słownik, i jedyne co robi to tłumaczy po kolei mnemoniki kodu maszynowego w pliku binarnym na zrozumiały dla człowieka kod w assemblerze. Oczywiście, nie jest to najczytelniejszy język programowania dla człowieka, jeśli porównamy go do języków wyższego poziomu, ale zawsze to coś ? da się taki kod analizować.

    Z uwagi na bardzo niskopoziomową naturę jądra systemu i to jak blisko związane jest ono ze sprzętem, które kontroluje, nie jest łatwo w pełni zrozumieć znaczenie analizowanego kodu. Kody źródłowe aplikacji wyższego poziomu, które znajdują się w przestrzeni użytkownika, działają już na wyższym poziomie abstrakcji ? nie komunikują się one bezpośrednio z sprzętem, są od niego zupełnie niezależne; przestrzegają one UNIXowej konwencji wywoływania funkcji, mają dobrze znany format binarny etc. Dzięki temu te aplikacje są idealnym kandydatem do deasemblacji i analizy kodu.

    Popularne deasemblery

    Istnieje na rynku sporo programów do deasemblacji programów napisanych na popularne architektury, takie jak MIPS ? jedne lepsze, inne gorsze, ale nadal spełniające swoje zadanie. Możemy wyróżnić spośród nich trzy topowe programy, które posiadają największe możliwości i są najwygodniejsze w obsłudze:

    IDA Pro: Zdecydowanie najpopularniejszy obecnie deasembler/debugger jaki jest dostępny. Posiada bardzo duże możliwości, szerokie grono użytkowników; działa na wielu platformach, a w sieci dostępne jest wiele tutoriali i pluginów. Niestety, jest też bardzo drogi. Wersja Pro, jakiej potrzebujemy do deasemblacji kodu napisanego na procesory z rdzeniem MIPS to koszt ponad 1000 dolarów za pojedynczą licencję.

    Radare2: Jest to w pełni otwarty program, który wykorzystuje prosty interfejs obsługiwany z linii komend. Wokół programu wyrosło spore środowisko, chętne udzielać porad, które bardzo się przydają ? obsługa programu jest dosyć skomplikowana z uwagi na wykorzystanie do kontroli linii komend i ogromną ilość wbudowanych funkcjonalności.

    Binary Ninja: Ten program nie jest otwarty i darmowy ? kosztuje około 100 dolarów za licencję i plasuje się pomiędzy IDA a Radare2. Jest to dosyć nowe narzędzie na rynku ? pojawił się w zeszłym roku, ale uzyskuje on coraz większą popularność. Dla wielu architektur procesorów sprawdza się on bardzo dobrze, jednakże ? na nasze nieszczęście ? nie obsługuje on (jeszcze) układów MIPS i nie ma kilku funkcji, jakie chcemy wykorzystać podczas naszych działań. Z wykorzystaniem tego programu poczekamy, aż będzie bardziej zaawansowany.

    A tymczasem zapoznajmy się z naszym już deasemblanym kodem. Aby łatwiej się go czytało wyświetlamy go w formie grafu ? w intuicyjny sposób pokazuje nam kolejność wykonywania się poszczególnych fragmentów kodu i zależności pomiędzy nimi.

    Reverse Engineering w praktyce - część 5


    Taka czytelna reprezentacja poszczególnych gałęzi kodu, instrukcji warunkowych, pętli etc jest niezwykle przydatne podczas analizy. Bez tego musielibyśmy manualnie skakać pomiędzy poszczególnymi fragmentami kodu w assemblerze. Nic przyjemnego.

    Jeśli wczytamy kod funkcji w ten sposób, bardzo łatwo widzimy wszelkie zależności, szczególnie że widać tutaj referencje do innych funkcji i zakodowanych stringów. Może to być bardzo przydatne do znalezienia interesujących nas informacji, jednakże mimo wszystko, aby zrozumieć co się tutaj dzieje, trzeba do pewnego stopnia po prostu znać assembler.

    Zbieranie informacji o procesorze i jego assemblerze (trochę teorii)

    Spójrzmy na to w jakim formacie zapisane są binarki naszego kodu:

    Code:
    $ file bin/busybox
    bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS-II version 1 (SYSV), dynamically linked (uses shared libs), corrupted section header size


    Ponieważ nagłówki ELF zaprojektowano tak, aby były agnostyczne względem platformy, łatwo mogą przekazać nam informacje dotyczące naszych binariów. Jak widzimy powyżej binarki przekazują na informację w nagłówku odnośnie architektury dla jakiej je stworzono (32 bitowa MIPS), sposobu zapisu (LSB) czy wykorzystaniu współdzielonych bibliotek.

    Powyższe informacje zweryfikować można na podstawie danych dotyczących Ralinka. Dokumenty informują, że proces wykorzystuje rdzeń MIPS24KEc.

    Reverse Engineering w praktyce - część 5


    Znając już wersję zastosowanego w CPU rdzenia możemy znaleźć kartę katalogową tego rdzenia na stronie producenta (Imagonation Technologies). A wiedząc już jaki rdzeń siedzi w naszym CPI bez problemu możemy deasemblować binarki. Pozwoli nam to sprawdzić, czy mamy rację odnośnie sprzętu zastosowanego w systemie no i uzyskać kody źródłowe aplikacji w routerze. Idąc dalej, aby zrozumieć otrzymany kod źródłowy, konieczna jest wiedza dotycząca architektury systemu, jego instrukcji czy nazw rejestrów. Wiedzę tą uzyskamy z:
    * Zestaw Instrukcji MIPS
    * Zestaw pseudo-instrukcji MIPS ? bardzo prosta kombinacja podstawowych instrukcji, wykorzystywane przez developerów.
    * Alternatywne nazwy rejestrów układu MIPS. W systemach MIPS nie ma żadnej różnicy pomiędzy rejestrami, CPU ich nie rozróżnia. Alternatywne nazwy dla tych rejestrów są istotne jedynie z punktu widzenia developerów (czy nas, analizujących kod): rejestry od $a0 do $a3 to argumenty funkcji, a te od $t0 o $t9 to rejestry tymczasowe.

    Oprócz zestawu instrukcji i nazw rejestrów dla szeregu architektur istotna jest też wiedza o różnych, czasami dziwnych, detali architektury. W przypadku MIPS są to tak zwane sloty opóźnienia: instrukcje, które pojawiają się po instrukcji rozdzielenia (begz, jalr) są w rzeczywistości realizowane przed rozdzieleniem się kodu (skokiem do innego miejsca w kodzie) na wymienionych instrukcjach. Tego rodzaju nieliniowości są nie do pomyślenia w przypadku innych architektur. W sieci możemy na szczęście znaleźć sporo materiałów dotyczących tych procesorów.

    Przykład wykorzystania deasemblacji aplikacji z przestrzeni użytkownika

    Analogicznie jak w przykładzie z jądrem, postaramy się wykorzystać informacje jakie zdobyliśmy przez UART do analizy kodu aplikacji. W kodzie źródłowym jądra nie odnaleźliśmy komunikatu o naciśnięciu przycisku resetu do ustawień fabrycznych, jaki widzieliśmy przez UART. Musi on zatem pochodzić z jakiejś innej binarki, która najpewniej znajduje się w przestrzeni użytkownika. Poszukajmy która aplikacja jest odpowiedzialna za wysłanie tego komunikatu.

    Code:
    ~/Tech/Reversing/Huawei-HG533_TalkTalk/router_filesystem
    $ grep -i -r "restore default success" .
    Binary file ./bin/cli matches
    Binary file ./bin/equipcmd matches
    Binary file ./lib/libcfmapi.so matches


    Trzy pliki zawierają ten komunikat: dwa pliki wykonywalne z folderu /bin/ oraz jedna biblioteka z folderu /lib/. Spójrzmy najpierw na /bin/equipcmd z wykorzystaniem IDA:

    Reverse Engineering w praktyce - część 5


    Jeśli przeanalizujemy kod w assemblerze, to niemalże jesteśmy w stanie odtworzyć program jaki napisano w C, który po skompilowaniu dał deasemblowany przez nas kod maszynowy. Widzimy wyraźnie wpis dotyczący ?czystego pliku konfiguracyjnego?, który współwystępuje z komendą ERASE, jaką widzieliśmy podglądając ruch na interfejsie SPI pomiędzy procesorem a pamięcią Flash. O tej komendzie, zależnie od wyniku działań, jeden z dwóch tekstów drukowanych jest poprzez UART: ?restore default success? lub ?restore default fail?. Jeśli operacja zakończy się powodzeniem, program czyści jakieś bufory danych i resetuje się ? to zachowanie wynikające z analizy kodu zgadza się z zachowaniem, jakie zaobserwowaliśmy innymi metodami po przyciśnięciu przycisku na routerze.

    Opisana powyżej funkcja jest doskonałym przykładem jak działa opóźnienie w tej architekturze procesora. Instrukcja addiu ustawia oba stringi jako argumenty - $a0; działa ona w slocie opóźnienia, dzięki czemu gdy po tym mnemoniku następuje instrukcja warunkowa i skok to może się ona wykonać przed skokiem.

    Jak widać na załączonych zrzutach ekranu z programem IDA prezentuje nazwy wszystkich funkcji w postaci binarnej. Nie musi tak być jednak w innych binarkach i to dobry moment pomówić teraz dlaczego.

    Nazwy funkcji w kodzie binarnym ? wprowadzenie do tablic symboli (trochę teorii)

    Format ELF specyfikuje wykorzystanie tablic symboli: fragmentów danych, wewnątrz pliku binarnego, które zapewniają wartościowe informacje dla dalszego debugowania kodu. Część tych tablic jest czytelna dla człowieka ? są to nazwy poszczególnych funkcji zapisanych w skompilowanym pliku. Jest to niezwykle przydatne w sytuacji takiej jak nasza, bo pomaga przy analizie kodu; często zdarza się jednak też, że dane te usuwane są z pliku gdy trafia on na produkcję. W tym przypadku developerzy byli na tyle mili, że pozostawili ten fragment binarki w plikach.

    Do usuwania tych fragmentów z plików używa się narzędzi takich jak strip, które doskonale wiedzą jaka część pliku należy usunąć, a jaką pozostawić. Narzędzia te mają dwojakie zastosowanie; po pierwsze pozwalają zmniejszyć rozmiar pliku, przez co oszczędza się miejsce w pamięci, a po drugie usuwanie tego rodzaju danych ? zasadniczo zbędnych podczas pracy programu ? utrudnia inżynierię wsteczną układu, co poniekąd zabezpiecza urządzenie przed atakami. Nazwy funkcji nie tylko czynią analizę kodu wygodniejszą, ale także zapewniają poszczególnym funkcjom jakże niezbędny kontekst ich pracy ? coś ogromnie przydatnego.

    W niektórych przypadkach, zwłaszcza gdy deasembluje się współdzielone obiekty, nazwy funkcji od razu będą widoczne, ale nie jest to zasadą. To co na pewno zobaczymy to symbole dynamiczne, pochodzące z tablicy .dymsym. Wcześniej wspominaliśmy, że stosuje się współdzielenie funkcji poprzez biblioteki, aby oszczędzić miejsce w systemie. Popularne funkcje, takie jak chociażby printf() są współdzielone pomiędzy wieloma programami, które wywołują ją wykorzystując ich nazwę, zupełnie zrozumiałą dla człowieka. Oznacza to, że nazwa ta musi cały czas znajdować się w binarce i nie może być z niego usunięta. Pozostałe nazwy, funkcji które nie są współdzielone, są usuwane. Dlatego też w plikach w standardzie ELF znajdują się dwie tablice: dynsym (tablica symboli dynamicznych) do publicznych wywołań funkcji oraz symtab (tablica symboli) z nazwami wewnętrznymi, zrozumiałymi dla człowieka. Więcej detali tego standardu poznać można oczywiście w jego specyfikacji.

    Algorytm generowania domyślnego hasła Wi-Fi

    W części trzeciej omawialiśmy algorytm generowania hasła Wi-Fi ? coś co najbardziej chcielibyśmy wyciągnąć podczas inżynierii wstecznej urządzenia. Istnieje duże prawdopodobieństwo, że ten router w ogóle nie posiada takiego algorytmu, ale nie możemy jeszcze o tym przesądzać. Najpierw spójrzmy na to co wiemy.

    To są domyślne dane do logowania do sieci Wi-Fi urządzenia:

    Reverse Engineering w praktyce - część 5


    Każde urządzenie posiada swoje unikalne dane do logowania. Mogą one być zaprogramowane ?na sztywno? w kodzie programu ? unikatowe SSID oraz hasło może być wpisywane do pamięci każdego urządzenia na taśmie fabrycznej. Z drugiej strony mogą one być generowane przez algorytm zapisany w kodzie firmware. Niezależnie od tego jaką metodę wybrali developerzy, wiemy już że i hasło i SSID zapisane są w pamięci Flash w sąsiadujących ze sobą komórkach. Jeśli są one tam zapisywane w fabryce, to router podczas resetowania hasła musi jedynie odczytać je z znanego adresu w pamięci.

    Jeśli z kolei są one generowane przez jakiś algorytm to musi on spełniać kilka cech. Po pierwsze musi dawać za każdym razem takie same dane na wyjściu, przy założeniu że na jego wejściu jest dokładnie to samo. Po drugie, korzysta on najpewniej z danych wejściowych, które są publiczne (na przykład adresu MAC), co zasadniczo oznacza, że znając algorytm mamy możliwość generowania własnych haseł dla dowolnego urządzenia z tym firmwarem. Zobaczmy zatem co możemy w tej sprawie zrobić, pracując z kodem oprogramowania urządzenia.

    Odnajdowanie stringów zakodowanych w programie

    Załóżmy na początku, że router jest wyposażony w algorytm generacji danych do logowania. Oprócz nazwy użytkownika i hasła w kodzie źródłowym odnajdujemy tylko jeden powtarzający się ciąg znaków: TALKTALK-. String ten jest dodawany na początek 6 ostatnich znaków adresu MAC urządzenia. Jeśli w routerze znajduje się algorytm generacji z pewnością to właśnie tam zakodowany musi być ten ciąg. Przejrzyjmy kody źródłowe:

    Code:
    $ grep -r 'TALKTALK-' .
    Binary file ./bin/cms matches
    Binary file ./bin/nmbd matches
    Binary file ./bin/smbd matches


    Dwie z tych trzech binarek to pliki samby, programu jaki wykorzystywany jest do współdzielenia w sieci dysku USB, jaki podpiąć można do urządzenia. Najpewniej więc ten ciąg znaków wykorzystywany jest tam do identyfikacji urządzenia w sieci. Pozostaje nam tylko przyjrzeć się trzeciemu programowi.

    Analizowanie zasady działania programu

    Reverse Engineering w praktyce - część 5


    To co widzimy na zrzucie powyżej wygląda dokładnie tak, jakbyśmy spodziewali się, że wygląda algorytm generacji domyślnych danych do logowania w sieci Wi-Fi. Cały kod znajduje się w raczej sporej funkcji ATP_WLAN_Init, a w niej widzimy, że program dokonuje na m.in. MACu następujące operacje:

    1. Odczytuje MAC urządzenia na którym pracuje (mac = BSP_NET_GetBaseMacAddress()).
    2. Tworzy string SSID (snprintf(SSID, "TALKTALK-%02x%02x%02x", mac[3], mac[4], mac[5])).
    3. Zapisuje gdzieś wygenerowany ciąg znaków (ATP_DBSetPara(SavedRegister3, 0xE8801E09, SSID)).

    Niestety ? zaraz po tym program przechodzi do innych czynności, uruchamia komendy systemu itp:

    Reverse Engineering w praktyce - część 5


    Niestety ? dalsza analiza programu nic nam nie daje, podobnie jak sprawdzenie innych odwołań do funkcji ATP_DBSave. Nie ma tutaj niestety nic interesującego.

    Czas się poddać

    Okazuje się zatem, że nie udało nam się odnaleźć poszukiwanego algorytmu. Potwierdza to nasze wcześniejsze przypuszczenia, iż takowy algorytm w tym routerze po prostu nie występuje. Dodatkowo potwierdza to fakt, że dane do logowania znaleźliśmy zapisane w pamięci Flash w obszarze chronionym. Zasadniczo oznacza to, że są one tam wpisywane w fabryce, na linii produkcyjnej, a sam układ nie generuje hasła, tylko odczytuje je z pamięci. To bardzo dobre i bezpieczne rozwiązanie, które zasadniczo uniemożliwia generowanie haseł do routerów.

    Autor artykułu nie wyklucza, że w przyszłości może powtórzyć podobny proces dla innego urządzenia, gdzie taki algorytm może występować. Jednakże, niezależnie od naszego niepowodzenia w tym przypadku, pamiętajmy że przedstawiona tutaj metodologia jest uniwersalna i działa w zasadzie dla dowolnych urządzeń, nie tylko routerów.

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


    Fajne!
TME logo Szukaj w ofercie
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
TME Logo