10:37
20/1/2015

Niniejszy post opisuje narzędzie dla programistów iOS, którzy chcą utrudnić złamanie zabezpieczeń i nieuprawniony dostęp do zasobów aplikacji mobilnych na ten system poprzez stosowanie obfuskacji. Narzędziem tym jest iOS-Class-Guard stworzony w firmie Polidea i udostępniony na licencji GPL.

Autorem artykułu są pracownicy firmy Polidea, która organizuje konferencję MCE2015 poruszającą m.in. tematykę bezpieczeństwa aplikacji mobilnych.

mce2015_map

Konferencja MCE2015 odbędzie się 4-6 lutego 2015 w Warszawie, a swoje wykłady będą mieli m.in. Nikolay Elenkov z Tokio, autor książki “Android Security Internals”, Eric Lafortune, twórca Proguarda (de-facto standardu w obfuskacji aplikacji Androidowych), czy też Max Bazaliy, ekspert bezpieczeństwa aplikacji iOS z Bluebox Security. Dodatkowo ciekawą prezentację dotyczącą płatności – mobilnych także za pomocą urządzeń typu “Wearable” zapowiedział Cristiano Betta z PayPala/BrainTree. 
Niebezpiecznik, jako partner medialny przedsięwzięcia zaprasza do wzięcia udziału w MCE zarówno tych, którzy programują, jak i tych, którzy projektują aplikacje mobilne, a ważne dla nich jest bezpieczeństwo ich użytkowników. Bilety są jeszcze dostępne.

“Kradzież” i analiza kodu aplikacji mobilnej

Aplikacje mobilne zaczynamy coraz częściej wykorzystywać do poważniejszych rzeczy niż tylko gry lub aplikacje typu “podręczna latarka”. Aplikacja mobilna Facebooka czy aplikacje bankowe dające dostęp do naszych prywatnych danych są koronnym tego typu przykładem. Ich bezpieczeństwo jest szczególnie istotne. W odróżnieniu od technologii webowych, gdzie kod aplikacji znajduje się na serwerze, a to, co widzi użytkownik jest tylko warstwą prezentacyjną, aplikacje mobilne muszą być skompilowane na platformę docelową. Pośrednio zmusza to nas do udostępnienia szczegółów implementacji, logiki aplikacji, czy danych w niej zaszytych potencjalnemu atakującemu. Jedną z podstawowych technik zaciemniających szczegóły implementacyjne jest obfuskacja. Niestety, jest ona praktycznie niestosowana w aplikacjach na platformę iOS.

Aplikacje na iOS programuje się wykorzystując Objective-C. Jest to język typowo opisowy, który w założeniu ma się “łatwo czytać”. Bardzo często zdarza się, że programista nazywa metody w następujący sposób: stringByReplacingOccurrencesOfString:withString:. W przypadku języków C/C++ nazwy te mają drugorzędne znaczenie, ponieważ są potrzebne tylko w trakcie procesu linkowania aplikacji, a poźniej mogą zostać “zapomniane”. Inaczej jest w przypadku Objective-C. Język ten jest językiem dynamicznym, tzn. wszystkie klasy oraz wszystkie metody są zarejestrowane w środowisku uruchomieniowym aplikacji Objective-C, podobnie jak ma to miejsce w przypadku języków .NET. Pozwala to programiście tworzyć w trakcie działania aplikacji obiekty na podstawie leksykalnych nazw ich klas, czy wywołać metody na podstawie ich selektorów (wysokopoziomowo można je traktować jako wskaźniki do metod obiektów). Jest to wygodne, pozwala łatwo pisać m.in. różnego rodzaju parsery, które automatycznie odtwarzają strukturę obiektów na podstawie tekstu, jednakże wymusza na kompilatorze umieszczenie w kodzie skompilowanej aplikacji informacji o wszystkich typach, klasach, metodach oraz właściwościach.

U wielu programistów iOS panuje błędne przekonanie, że aplikacje trudno jest skopiować z urządzenia mobilnego. Wszystkie aplikacje dystrybuowane w AppStore są szyfrowane mechanizmem FairPlay, który jest wykorzystywany do szyfrowania kodu binarnego aplikacji. Jednak jest i na to sposób. Wystarczy urządzenie z jailbreakiem, zainstalowaną aplikacją i dostępem przez terminal. Można wtedy podłączyć się do uruchomionej aplikacji i wykonać jej zrzut pamięci, odtwarzając w ten sposób jej zaszyfrowaną część. Od dłuższego czasu dostępne jest narzędzie, które automatyzuje ten proces: Clutch. Wystarczy z poziomu terminala wykonać polecenie: Clutch nazwa_aplikacji. W rezultacie w folderze użytkownika zostanie zapisana odszyfrowana aplikacja w w formacie .ipa.

Pewnie część z Was zastanawia się, jak wyglądają takie informacje. Na rynku znajduje się co najmniej kilka narzędzi, które pozwalają wykorzystać informacje zapisane w kodzie binarnym aplikacji. Dwa szczególnie przydatne to:

  • Cycript – pozwala atakującemu podłączyć się do uruchomionej aplikacji, przeglądać i modyfikować jej kontekst. Cycript umożliwia modyfikowanie zawartości aktualnie wyświetlonych widoków, wykonywanie metody w kontekście aplikacji, odczytywanie danych zapisanych w pamięci.
  • Class-Dump – pozwala atakującemu na podgląd informacji o strukturze aplikacji, tj.: klasy, protokołów czy metod. Informacje te są bardzo przydatne w kontekście poprzedniego narzędzia: atakujący zna strukturę aplikacji i może łatwo zidentyfikować potencjalne, wrażliwe miejsca w kodzie, które może przetestować “na żywo” cycriptem.

Jak wygląda to w praktyce?

Zróbmy prosty test. Potrzebny do tego będzie system Mac OS X oraz parę poleceń, które należy wykonać w terminalu. Zacznijmy od sklonowania źródeł testowej aplikacji:

git clone https://github.com/Polidea/ios-class-guard-example

Kolejnym krokiem będzie jej kompilacja na iOS, w naszym przypadku jest to symulator:

xcodebuild -sdk iphonesimulator -scheme ExampleBankingApp build SYMROOT=build

Wynik kompilacji znajduje się w katalogu build/ naszego projektu.

Zainstalujmy teraz class-dump. Wymagane jest do tego posiadanie w systemie managera paczek Homebrew:

brew install class-dump

Zróbmy zrzut informacji o strukturze aplikacji do katalogu headers/:

class-dump -H -o headers build/Debug-iphonesimulator/ExampleBankingApp.app/ExampleBankingApp

Jeśli wszystko wykonaliśmy dobrze, w katalogu headers zobaczymy listę klas oraz protokołów wykorzystywanych przez aplikację:

$ ls -1 headers/
CDStructures.h
EBAppDelegate.h
EBLoginViewController.h
EBWelcomeViewController.h
NSObject-Protocol.h
UIApplicationDelegate-Protocol.h

Otwierając dowolny z ww. plików zobaczymy wszystkie pola, właściwości oraz metody implementowane przez daną klasę.

__attribute__((visibility("hidden")))
@interface EBLoginViewController : UIViewController
{
UITextField *_clientIDField;
UITextField *_clientPasswordField;
UILabel *_loginErrorField;
}

- (void).cxx_destruct;
- (void)buttonLogin:(id)arg1;
- (void)writeClientPassword:(id)arg1;
- (void)writeClientID:(id)arg1;
- (id)readClientPassword;
- (id)readClientID;
- (id)userDefaults;
- (void)viewDidLoad;

@end

Podobny problem występuje w przypadku Androida (w którym aplikacje są napisane w Javie). Tam w kodzie binarym znajduje się znacznie więcej informacji. Jednakże standardem jest wykorzystanie ProGuarda stworzonego przez Erica Lafortune. Erica można będzie posłuchać na MCE 2015 na początku lutego w Warszawie.

ProGuard jest w stanie zamienić nazwy klas oraz metod w całej skompilowanej aplikacji. Efektem jest wersja aplikacji o mniejszym rozmiarze, która jest trudniejsza do przeanalizowania i odtworzenia jej struktury i kodu źródłowego.

Obfuskacja kodu aplikacji iOS

Do niedawna nie było publicznego narzędzia do obfuskacji kodu dla iOS. Podczas tworzenia bardzo dobrze zabezpieczonej aplikacji bankowej przez naszą firmę potrzebowaliśmy takiego narzędzia i po poszukiwaniach okazało się że najlepiej będzie, jeśli zrobimy to sami. Rozwiązanie stworzone dla aplikacji bankowej świetnie spełniło swoją rolę, więc zdecydowaliśmy się od zera stworzyć samodzielne uniwersalne narzędzie tego typu. Tak powstał iOS-Class-Guard, który udostępniliśmy na bardzo liberalnej licencji open-source.

Class Guard jest odpowiednikiem ProGuarda i ma za zadanie zaciemnić kod aplikacji iOS. iOS-Class-Guard analizując kod binarny generuje mapę symboli, która przemianowuje nazwy klas, protokołów, metod, pól oraz właściwości na nazwy mało czytelne, np.: “k0J”. Wygenerowana mapa symboli dołączana jest jako nagłówek do projektu i wykorzystywana podczas drugiej kompilacji już “zaciemnionej” aplikacji. Jest to jedyne darmowe tego typu narzędzie dostępne dla iOS.

Wykorzystanie samego iOS-Class-Guarda podnosi znacząco poufność naszego kodu. Spójrzmy na wcześniejszy przykład, ale już po obfuskacji:

$ ls -1 headers/
CDStructures.h
NSObject-Protocol.h
UIApplicationDelegate-Protocol.h
k0J.h
l0g.h
t0a.h

Zrzut zobfuskowanej klasy EBLoginViewController:

__attribute__((visibility("hidden")))
@interface k0J : UIViewController
{
UITextField *c9p;
UITextField *a9G;
UILabel *e0S;
}

- (void).cxx_destruct;
- (void)n15:(id)arg1;
- (void)h1v:(id)arg1;
- (void)l8h:(id)arg1;
- (id)p9z;
- (id)k64;
- (id)d40;
- (void)viewDidLoad;

@end

Aby osiągnąć powyższy rezultat, wystarczy wykonać skrypt:

bash obfuscate_project

który kompiluje projekt, obfuskuje go a następnie rekompiluje.

Na chwilę obecną iOS-Class-Guard obsługuje tylko przemianowywanie klas i metod, ale bardzo prosto można dodać np. szyfrowanie ciągów znaków. W większości przypadków już samo wykorzystanie iOS-Class-Guarda będzie całkiem dobrym zabezpieczeniem kodu aplikacji mobilnej. Natomiast jeśli ktoś potrzebuje podnieść poziom bezpieczeństwa, może wykorzystać Obfuscator-LLVM który dodatkowo zaciemnia strukturę funkcji. Oba te narzędzia świetnie ze sobą współpracują.

Cel: opóźnienie działań atakującego

Obfuskacja nie jest rozwiązaniem, które gwarantuje 100% bezpieczeństwo aplikacji. Pozwala natomiast w bardzo łatwy sposób utrudnić pracę osobom, które próbują nasze aplikacje złamać (czyli podnosi koszty ataku — dop red.).

Prezentowane rozwiązanie nie wpływa w żaden sposób na efektywność pracy twórców aplikacji. Efektem ubocznym jest również zmniejszenie rozmiarów pliku binarnego aplikacji, ponieważ generowane symbole mają krótsze nazwy.

Aplikacje Androidowe mają wbudowane rozwiązanie, które pozwala na ich zabezpieczenie. Zaprezentowane rozwiązanie jest próbą poprawy bezpieczeństwa aplikacji iOS, a także danych, jakie użytkownicy przechowują za ich pomocą.

Ciekawostką jest, że narzędzie to stworzyliśmy jako swego rodzaju “20% projekt”: Polidea daje czas swoim pracownikom na to, aby rozwijali swoje pomysły w czasie, w którym mogliby pracować nad “normalnymi” projektami. Takich projektów mamy więcej i czasami – tak jak w tym przypadku – udaje się nam stworzyć bardzo użyteczne narzędzie, które jest używane przez bardzo wiele osób – w krótkim czasie dostaliśmy ponad 300 gwiazdek na githubie.

Autorem artykułu jest firma Polidea, która organizuje konferencję MCE2015 poruszającą m.in. tematykę bezpieczeństwa aplikacji mobilnych. Konferencja MCE2015 odbędzie się 4-6 lutego 2015 w Warszawie, a swoje wykłady będą mieli m.in. Nikolay Elenkov z Tokio, autor książki “Android Security Internals”, Eric Lafortune, twórca Proguarda (de-facto standardu w obfuskacji aplikacji Androidowych), czy też Max Bazaliy, ekspert bezpieczeństwa aplikacji iOS z Bluebox Security. Dodatkowo ciekawą prezentację dotyczącą płatności – mobilnych także za pomocą urządzeń typu “Wearable” zapowiedział Cristiano Betta z PayPala/BrainTree. 
Niebezpiecznik, jako partner medialny przedsięwzięcia zaprasza do wzięcia udziału w MCE zarówno tych, którzy programują, jak i tych, którzy projektują aplikacje mobilne, a ważne dla nich jest bezpieczeństwo ich użytkowników. Bilety są jeszcze dostępne.

PS. A gdyby ktoś chciał poćwiczyć przełamywanie zabezpieczeń aplikacji mobilnych, zapraszamy na kolejną edycję naszych warsztatów, które odbędą się 26-27 lutego w Warszawie. W ramach warsztatów poruszany jest także temat obfuskacji i deobfuskacji kodu na obie platformy mobilne.


Dowiedz się, jak zabezpieczyć swoje dane i pieniądze przed cyberprzestępcami. Wpadnij na nasz kultowy ~3 godzinny wykład pt. "Jak nie dać się zhackować?" i poznaj kilkadziesiąt praktycznych i przede wszystkim prostych do zastosowania porad, które skutecznie podniosą Twoje bezpieczeństwo i pomogą ochronić przed atakami Twoich najbliższych. Uczestnicy tego wykładu oceniają go na: 9,34/10!

Na ten wykład powinien przyjść każdy, kto korzysta z internetu na smartfonie lub komputerze, prywatnie albo służbowo. Wykład prowadzimy prostym językiem, wiec zrozumie go każdy, także osoby spoza branży IT. Dlatego na wykład możesz spokojnie przyjść ze swoimi rodzicami lub mniej technicznymih znajomych. W najbliższych tygodniach będziemy w poniższych miastach:

Zobacz pełen opis wykładu klikając tutaj lub kup bilet na wykład klikając tu.

21 komentarzy

Dodaj komentarz
  1. A jak ta tematyka ma się do Windows Phone, jak to tam wygląda?

    • Są dostępne narzędzia do obfuskacji .NET, które też działają dla WP (np. EazFuscator)

    • Przy okazji ciekaw jestem, jakie sa skuteczne narzędzia do obfuskacji aplikacji dla Windows i OS X, czyli systemów desktopowych. Przy okazji warto poruszyć kwestię walidacji kodu, bo jak wiadomo błędy w kodzie obniżają wydajność aplikacji i co gorsza mogą być wektorem ataku. To samo dotyczy serwisów internetowych i webaplikacji. Oczywiście ideałem byłoby narzędzie pozwalające generować apki jednocześnie na platformy Android, iOS i Windows Phone, ale to raczej będzie niespełnione marzenie developerów aplikacji. Słyszałem jednak o narzędziu portującym aplikacje iOS oraz Androida (oczywiście przed ubfuskacją) do Windows Phone, które z założenia miało ułatwić developerom wprowadzenie aplikacji także na Windows Phone. Mówię o tym jako teoretyk, ale wiadomo, że niezależne tworzenie aplikacji na trzy platformy jest trudniejsze od ich przeportowania z jednej platformy na druga. Jednak nie słyszałem o możliwości przeportowania apki z iOS na Androida z uwagi na kompletnie różne podejście do kodowania w tych zupełnie różnych systemach.

  2. ciekawe co widać w przypadku apek publikowanych z adobe aira, pewnie niewiele, ale trzeba będzie sprawdzić.
    nie rozumiem skąd założenie, że logika musi być zapisana w apce, przecież apka może działać dokładnie tak jak strona www tylko jako frontend a cała logika być po stronie serwera

    • Bo przerzucenie całej logiki na serwer sprawia że taka apka staje się bezużyteczna bez połączenia z internetem, działa też wolniej i zaciąga masę danych, co nie każdemu musi się podobać przy aktualnych stawkach za net mobilny. Dużo lepszym rozwiązaniem jest apka działająca bez internetu, zapisująca działania lokalnie i dopiero po podłączeniu do internetu synchronizować zmiany – wtedy logika musi być już w aplikacji.

    • Ciekawe pytanie. W epoce coraz mocniejszych komputerów/urządzeń mobilnych chciałoby się, żeby apka działa się na telefonie ale z drugiej strony jak słychać kwestie bezpieczeństwa. Wyobrażam sobie, że dla konkretnej aplikacji da się znaleźć jakiś kompromisowy model działania (z naciskiem: korzystny dla użytkownika końcowego).

    • System wymagający dostępu do internetu żeby odpalić aplikacje już wymyślili nazywa się Firefox OS i jest okropny, nie tędy droga.

    • Jak sobie sam AS-a nie zobfuskujesze tonarzedzie opisane w tym arcie dotkneloby tylko wrapera w ktorym jest osadzony AIR a wlasciwy kod aplikacji pozostalby czysty jak pupa niemowlaka :)

    • Jaki sens ma apka z logiką na serwerze. W sumie w takim wypadku wystarczy link do webaplikacji. Aplikacja ma działać szybko i w miarę możliwości offline synchronizując zmieniające się dane. W ten sposób apka nabiera sensu i jest szybka. Jedynym wyjściem jest więc zaciemnienie kodu, które przy okazji zmniejsza rozmiar aplikacji, co ma znaczenie z uwagi na dominację urządzeń z pamięcią 16 GB a w przypadku tanich smartfonów z Androidem czy Windows Phone niestety jest sporo modeli z 8 GB pamięci a poza tym ciągle jeszcze tanie smartfony nie zawsze mają 1 GB RAM. Dlatego im lżejsza apka, tym lepiej.

    • Aplikacja klient-serwer skierowana do szerokiej publiki, w której strona kliencka jest zaufana, brzmi jak fundamentalny błąd projektowy. Nawet “obfuskacja” kodu nie pomoże, ponieważ jest wiele sposobów, w jakie można zmodyfikować działanie takiego kodu i środowiska, w którym pracuje.

      Jedynym skutecznym i bezpiecznym rozwiązaniem jest trzymanie logiki biznesowej po stronie serwera i nie ufanie klientom. Oczywiście, klient może oferować caching danych, a nawet optymistycznie symulować wyniki pewnych operacji.

      Posługując się klasycznym już przykładem aplikacji “ToDo List”: kiedy użytkownik doda zadanie do listy zadań do wykonania, do serwera wysyłane jest żądanie (przez autoryzowany kanał) wprowadzenia zmian do stanu domeny. Z drugiej strony, GUI może już w tym momencie wyświetlić tę notatkę na liście, ponieważ zakłada, że zostanie faktycznie dodana. Zważając na to, że błędy zdarzają się raczej rzadko (i aplikacja będzie w stanie je wykryć i poinformować o nich usera), zapewnia to najlepszą “responsywność” programu dość małym kosztem. Oczywiście, to nie działa wszędzie – aplikacje biznesowe, gdzie działania (i ich powodzenie/niepowodzenie) mają konkretne, poważne konsekwencje (wystawianie faktury?), wymagają dokładnego sprawdzenia i zachowania implikacji “GUI wyświetlił udaną operację => zmiany zostały trwale zachowane”.

      Serwer powinien jednak zawsze podejmować kluczowe decyzje – np. czy zezwolić danemu użytkownikowi, na podstawie uzyskanych przez niego autoryzacji, na edycję danej listy ToDo?

    • @CluelessKiwi
      Nikt nie mówi o zaufaniu stronie klienckiej, bo serwer zawsze musi traktować odpowiedź od klienta jako niezaufaną i walidować ją ponownie. Zawsze, choćby miało to doprowadzić do 100-procentowej redundancji.
      Opieranie bezpieczeństwa aplikacji bankowej na zaciemnieniu kodu brzmi jak fundamentalny błąd projektowy. ;)

  3. Dla przykładu aplikacji klienckiej trzymającej newralgiczne dane można podać aplikację Facebooka, która trzyma tokeny sesji w keychainie na ios. I na jailbreaku można fajne rzeczy wtedy robić.http://resources.infosecinstitute.com/ios-application-security-part-12-dumping-keychain-data/

    • Przypadek szczególny. Robiąc Jailbreaka robisz to na własną odpowiedzialność wyłączając część mechanizmów bezpieczeństwa. Na niezjailbreakowanym telefonie nie jest to do odczytania. W moim odczuciu iOS jest jedną z bezpieczniejszych platform mobilnych, jeśli nie najbezpieczniejszą.

  4. @rob006 @Paweł Nyczaj
    no nie wiem wydaje mi się, że aplikacja bankowa, która ma logikę po stronie klienta to porażka, jedynnie widzę tutaj opcję logiki po stronie serwera, ale być może się mylę

  5. za to w androidzie ze względu na jave to mamy wszystko na tacy podane jak nas interesuje jakieś rozwiązanie. Pewnie dlatego firmy wykorzystują możliwość pisania modułów w c++. Jestem ciekaw czy na androidzie zadziałałby jakiś protektor.

    • ProGuard daje sobie całkiem nieźle radę. Fakt jest taki, że ProGuard nie może zaciemnić wszystkich symboli. System Android oraz aplikacje androidowe dosyć mocno polegają na refleksji: np. demarshaling danych otrzymanych z serwera. Najlepszym pod tym względem jednak jest C/C++. Wycinając wszystkie symbole z aplikacji, kod staje się mocno nieczytelny.

  6. Mogą to wykorzystać twórcy wirusa do ochrony kodu

  7. @Pan_redaktor. Ciekaw jestem w jaki sposób po takiej operacji można z crashloga przy jakimkowliek crashu cokolwiek wywnioskować?

  8. Jest opcja deobfuskacji crash logów.

  9. Ludzie taka obsfukacja to nie jest żadne zabezpieczenie. Dalej jest to o niebo czytelniejsze jak kod maszynowy i naprawdę szczerze wątpię by spowolniło to w jakikolwiek znaczący sposób doświadczonego crakera.

    Lata mijają i dalej co do niektórych nie dociera oczywisty fakt, że aplikacja po stronie klienta nigdy nie będzie bezpieczna. Wszelkiego rodzaju DRM-y udowadniają to raz po raz.

  10. Prawdziwa obfuskacja to jest np. tutaj:

    http://www.pelock.com/products/obfuscator/screenshots

Twój komentarz

Zamieszczając komentarz akceptujesz regulamin dodawania komentarzy. Przez moderację nie przejdą: wycieczki osobiste, komentarze nie na temat, wulgaryzmy.

RSS dla komentarzy: