# Android Applications Basics {{#include ../../banners/hacktricks-training.md}} ## Android Security Model **有两个层次:** - **操作系统**,它使已安装的应用程序彼此隔离。 - **应用程序本身**,允许开发人员**暴露某些功能**并配置应用程序能力。 ### UID Separation **每个应用程序被分配一个特定的用户ID**。这在应用程序安装时完成,因此**该应用程序只能与其用户ID拥有的文件或共享文件进行交互**。因此,只有应用程序本身、操作系统的某些组件和根用户可以访问应用程序的数据。 ### UID Sharing **两个应用程序可以配置为使用相同的UID**。这可以用于共享信息,但如果其中一个被攻破,则两个应用程序的数据都会受到影响。这就是为什么这种行为是**不鼓励**的。\ **要共享相同的UID,应用程序必须在其清单中定义相同的 `android:sharedUserId` 值。** ### Sandboxing **Android应用程序沙箱**允许**每个应用程序**作为**在单独用户ID下的单独进程**运行。每个进程都有自己的虚拟机,因此应用程序的代码在与其他应用程序隔离的情况下运行。\ 从Android 5.0(L)开始,**SELinux**被强制执行。基本上,SELinux拒绝所有进程交互,然后创建策略以**仅允许它们之间的预期交互**。 ### Permissions 当你安装一个**应用程序并请求权限**时,该应用程序是在请求**AndroidManifest.xml**文件中配置的**`uses-permission`**元素中的权限。**uses-permission**元素在**name**属性中指示请求的权限名称。它还有**maxSdkVersion**属性,该属性在版本高于指定版本时停止请求权限。\ 请注意,Android应用程序不需要在开始时请求所有权限,它们也可以**动态请求权限**,但所有权限必须在**清单**中**声明**。 当一个应用程序暴露功能时,它可以限制**仅允许具有指定权限的应用程序访问**。\ 权限元素有三个属性: - 权限的**名称** - **permission-group**属性,允许对相关权限进行分组。 - **protection-level**,指示权限的授予方式。共有四种类型: - **Normal**:用于当应用程序**没有已知威胁**时。用户**不需要批准**它。 - **Dangerous**:指示权限授予请求应用程序某些**提升的访问权限**。**用户被要求批准它们**。 - **Signature**:只有**由与导出组件相同证书签名的应用程序**才能获得权限。这是最强的保护类型。 - **SignatureOrSystem**:只有**由与导出组件相同证书签名的应用程序**或**以系统级访问权限运行的应用程序**才能获得权限。 ## Pre-Installed Applications 这些应用程序通常位于**`/system/app`**或**`/system/priv-app`**目录中,其中一些是**优化过的**(你可能甚至找不到 `classes.dex` 文件)。这些应用程序值得检查,因为有时它们**运行的权限过多**(作为root)。 - 随**AOSP**(Android开源项目)**ROM**一起提供的应用程序 - 由设备**制造商**添加的 - 由手机**提供商**添加的(如果是从他们那里购买的) ## Rooting 为了获得对物理Android设备的root访问权限,通常需要**利用**1或2个**漏洞**,这些漏洞通常是**特定**于**设备**和**版本**的。\ 一旦漏洞成功利用,通常会将Linux `su` 二进制文件复制到用户的PATH环境变量中指定的位置,如`/system/xbin`。 一旦配置了su二进制文件,另一个Android应用程序将用于与`su`二进制文件接口并**处理root访问请求**,如**Superuser**和**SuperSU**(在Google Play商店中可用)。 > [!CAUTION] > 请注意,root过程非常危险,可能会严重损坏设备。 ### ROMs 可以通过**安装自定义固件来替换操作系统**。这样可以扩展旧设备的使用价值,绕过软件限制或获得最新的Android代码。\ **OmniROM**和**LineageOS**是两个最流行的固件。 请注意,**并不总是需要root设备**才能安装自定义固件。**一些制造商允许**以良好文档和安全的方式解锁其引导加载程序。 ### Implications 一旦设备被root,任何应用程序都可以请求root访问。如果恶意应用程序获得了它,它将几乎可以访问所有内容,并能够损坏手机。 ## Android Application Fundamentals - Android应用程序的格式被称为 _APK文件格式_。它本质上是一个**ZIP文件**(通过将文件扩展名重命名为.zip,可以提取和查看内容)。 - APK内容(不详尽) - **AndroidManifest.xml** - resources.arsc/strings.xml - resources.arsc:包含预编译资源,如二进制XML。 - res/xml/files_paths.xml - META-INF/ - 这里是证书所在的位置! - **classes.dex** - 包含Dalvik字节码,表示应用程序默认执行的编译Java(或Kotlin)代码。 - lib/ - 存放本地库,按CPU架构在子目录中分隔。 - `armeabi`:ARM架构处理器的代码 - `armeabi-v7a`:ARMv7及更高架构处理器的代码 - `x86`:X86处理器的代码 - `mips`:仅用于MIPS处理器的代码 - assets/ - 存储应用程序所需的杂项文件,可能包括额外的本地库或DEX文件,有时被恶意软件作者用来隐藏额外代码。 - res/ - 包含未编译到resources.arsc中的资源。 ### **Dalvik & Smali** 在Android开发中,**Java或Kotlin**用于创建应用程序。与桌面应用程序使用JVM不同,Android将此代码编译为**Dalvik可执行文件(DEX字节码)**。早期,Dalvik虚拟机处理此字节码,但现在,在较新版本的Android中,Android Runtime(ART)接管。 对于逆向工程,**Smali**变得至关重要。它是DEX字节码的人类可读版本,像汇编语言一样通过将源代码转换为字节码指令。Smali和baksmali在此上下文中指的是汇编和反汇编工具。 ## Intents Intents是Android应用程序在其组件之间或与其他应用程序之间通信的主要方式。这些消息对象还可以在应用程序或组件之间携带数据,类似于HTTP通信中的GET/POST请求。 因此,Intent基本上是**在组件之间传递的消息**。Intents**可以定向**到特定组件或应用程序,**也可以在没有特定接收者的情况下发送**。\ 简单来说,Intent可以用于: - 启动一个Activity,通常打开应用程序的用户界面 - 作为广播通知系统和应用程序的变化 - 启动、停止和与后台服务通信 - 通过ContentProviders访问数据 - 作为回调处理事件 如果存在漏洞,**Intents可以用于执行各种攻击**。 ### Intent-Filter **Intent过滤器**定义**活动、服务或广播接收器如何与不同类型的Intents交互**。本质上,它们描述了这些组件的能力,例如它们可以执行的操作或可以处理的广播类型。声明这些过滤器的主要位置是在**AndroidManifest.xml文件**中,尽管对于广播接收器,编码它们也是一个选项。 Intent过滤器由类别、操作和数据过滤器组成,并可以包含附加元数据。此设置允许组件处理与声明的标准匹配的特定Intents。 Android组件(活动/服务/内容提供者/广播接收器)的一个关键方面是它们的可见性或**公共状态**。如果组件的**`exported`**值为**`true`**,或者在清单中声明了Intent过滤器,则该组件被视为公共的,可以与其他应用程序交互。然而,开发人员可以明确将这些组件保持私有,以确保它们不会与其他应用程序意外交互。这是通过在其清单定义中将**`exported`**属性设置为**`false`**来实现的。 此外,开发人员还有选择进一步保护对这些组件的访问的选项,要求特定权限。**`permission`**属性可以设置为强制要求只有具有指定权限的应用程序才能访问该组件,从而增加了安全性和对谁可以与其交互的控制。 ```java ``` ### 隐式意图 意图是通过意图构造函数程序化创建的: ```java Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:")); ``` 该意图的 **Action** 是 **ACTION_SEND**,**Extra** 是一个 mailto **Uri**(Extra 是意图所期望的额外信息)。 该意图应在清单中声明,如以下示例所示: ```xml ``` 一个 intent-filter 需要匹配 **action**、**data** 和 **category** 才能接收消息。 “Intent 解析”过程决定了哪个应用程序应该接收每个消息。这个过程考虑了 **priority attribute**,可以在 **intent-filter declaration** 中设置,**优先级更高的将被选择**。这个优先级可以设置在 -1000 到 1000 之间,应用程序可以使用 `SYSTEM_HIGH_PRIORITY` 值。如果出现 **冲突**,将出现一个“选择器”窗口,以便 **用户可以决定**。 ### 显式 Intent 显式 Intent 指定了它所针对的类名: ```java Intent downloadIntent = new (this, DownloadService.class): ``` 在其他应用程序中,为了访问先前声明的意图,您可以使用: ```java Intent intent = new Intent(); intent.setClassName("com.other.app", "com.other.app.ServiceName"); context.startService(intent); ``` ### Pending Intents 这些允许其他应用程序**代表您的应用程序采取行动**,使用您的应用程序的身份和权限。构造 Pending Intent 时,应该**指定一个意图和要执行的操作**。如果**声明的意图不是显式的**(没有声明哪个意图可以调用它),则**恶意应用程序可能会代表受害者应用程序执行声明的操作**。此外,**如果未指定操作**,恶意应用程序将能够**代表受害者执行任何操作**。 ### Broadcast Intents 与之前的意图不同,后者仅由一个应用程序接收,广播意图**可以被多个应用程序接收**。然而,从 API 版本 14 开始,可以**使用 Intent.setPackage 指定应该接收**消息的应用程序。 另外,在发送广播时也可以**指定权限**。接收应用程序需要拥有该权限。 广播有**两种类型**:**正常**(异步)和**有序**(同步)。**顺序**基于**接收器元素中的配置优先级**。**每个应用程序可以处理、转发或丢弃广播。** 可以使用 `Context` 类中的函数 `sendBroadcast(intent, receiverPermission)` 来**发送**一个**广播**。\ 您还可以使用**`LocalBroadCastManager`**中的函数**`sendBroadcast`**,确保**消息永远不会离开应用程序**。使用此方法,您甚至不需要导出接收器组件。 ### Sticky Broadcasts 这种广播**可以在发送后很长时间内访问**。\ 这些在 API 级别 21 中被弃用,建议**不要使用它们**。\ **它们允许任何应用程序嗅探数据,还可以修改数据。** 如果您发现包含“sticky”一词的函数,如**`sendStickyBroadcast`**或**`sendStickyBroadcastAsUser`**,**检查影响并尝试删除它们**。 ## Deep links / URL schemes 在 Android 应用程序中,**深度链接**用于通过 URL 直接启动一个操作(意图)。这是通过在活动中声明特定的**URL 方案**来完成的。当 Android 设备尝试**访问带有此方案的 URL**时,应用程序中的指定活动将被启动。 该方案必须在**`AndroidManifest.xml`**文件中声明: ```xml [...] [...] ``` 前一个示例中的方案是 `examplescheme://`(还要注意 **`category BROWSABLE`**) 然后,在数据字段中,您可以指定 **host** 和 **path**: ```xml ``` 要从网页访问,可以设置一个链接,如: ```xml click here click here ``` 为了找到**将在应用中执行的代码**,请转到由深度链接调用的活动,并搜索函数**`onNewIntent`**。 了解如何[在不使用HTML页面的情况下调用深度链接](#exploiting-schemes-deep-links)。 ## AIDL - Android接口定义语言 **Android接口定义语言(AIDL)**旨在通过**进程间通信**(IPC)促进Android应用程序中客户端和服务之间的通信。由于不允许直接访问另一个进程的内存,AIDL通过将对象编组为操作系统理解的格式来简化该过程,从而简化了不同进程之间的通信。 ### 关键概念 - **绑定服务**:这些服务利用AIDL进行IPC,使活动或组件能够绑定到服务,发出请求并接收响应。服务类中的`onBind`方法对于启动交互至关重要,标志着它是安全审查中寻找漏洞的重要领域。 - **Messenger**:作为绑定服务,Messenger促进IPC,重点处理通过`onBind`方法的数据。必须仔细检查此方法,以发现任何不安全的数据处理或敏感功能的执行。 - **Binder**:尽管由于AIDL的抽象,Binder类的直接使用较少,但了解Binder作为内核级驱动程序在不同进程的内存空间之间促进数据传输是有益的。有关进一步理解的资源可在[https://www.youtube.com/watch?v=O-UHvFjxwZ8](https://www.youtube.com/watch?v=O-UHvFjxwZ8)找到。 ## 组件 这些包括:**活动、服务、广播接收器和提供者。** ### 启动活动和其他活动 在Android应用中,**活动**就像屏幕,显示应用用户界面的不同部分。一个应用可以有多个活动,每个活动向用户呈现一个独特的屏幕。 **启动活动**是应用的主要入口,当您点击应用图标时启动。它在应用的清单文件中定义,具有特定的MAIN和LAUNCHER意图: ```html ``` 并非所有应用都需要启动器活动,特别是那些没有用户界面的应用,如后台服务。 通过在清单中将活动标记为“exported”,可以使其对其他应用或进程可用。此设置允许其他应用启动此活动: ```markdown ``` 然而,从另一个应用访问一个活动并不总是安全风险。问题出现在敏感数据被不当共享时,这可能导致信息泄露。 一个活动的生命周期 **从 onCreate 方法开始**,设置用户界面并准备活动与用户的交互。 ### 应用子类 在 Android 开发中,应用可以选择创建一个 **子类** 的 [Application](https://developer.android.com/reference/android/app/Application) 类,尽管这不是强制性的。当定义了这样的子类时,它将成为应用中第一个被实例化的类。如果在这个子类中实现了 **`attachBaseContext`** 方法,它将在 **`onCreate`** 方法之前执行。这个设置允许在应用的其余部分启动之前进行早期初始化。 ```java 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 } } ``` ### Services [Services](https://developer.android.com/guide/components/services) 是 **后台操作**,能够在没有用户界面的情况下执行任务。这些任务即使在用户切换到不同应用程序时也可以继续运行,使得服务对于 **长时间运行的操作** 至关重要。 服务是多功能的;它们可以通过多种方式启动,其中 **Intents** 是作为应用程序入口点启动它们的主要方法。一旦使用 `startService` 方法启动服务,其 `onStart` 方法就会启动并持续运行,直到显式调用 `stopService` 方法。或者,如果服务的角色依赖于活动的客户端连接,则使用 `bindService` 方法将客户端绑定到服务,激活 `onBind` 方法以进行数据传递。 服务的一个有趣应用包括后台音乐播放或网络数据获取,而不妨碍用户与应用的交互。此外,服务可以通过 **导出** 使其他进程在同一设备上可访问。这不是默认行为,需要在 Android Manifest 文件中进行显式配置: ```xml ``` ### 广播接收器 **广播接收器**充当消息系统中的监听器,允许多个应用程序响应来自系统的相同消息。应用程序可以通过应用的**Manifest**或在应用代码中通过**`registerReceiver`** API以**两种主要方式**注册接收器。在Manifest中,广播通过权限进行过滤,而动态注册的接收器在注册时也可以指定权限。 **意图过滤器**在这两种注册方法中至关重要,决定哪些广播触发接收器。一旦发送匹配的广播,接收器的**`onReceive`**方法将被调用,使应用能够相应地反应,例如在接收到低电量警报时调整行为。 广播可以是**异步**的,所有接收器无序接收,或**同步**的,接收器根据设定的优先级接收广播。然而,重要的是要注意潜在的安全风险,因为任何应用都可以优先处理自己以拦截广播。 要理解接收器的功能,请查找其类中的**`onReceive`**方法。该方法的代码可以操作接收到的Intent,强调接收器进行数据验证的必要性,特别是在**有序广播**中,这可能会修改或丢弃Intent。 ### 内容提供者 **内容提供者**对于应用之间**共享结构化数据**至关重要,强调实施**权限**以确保数据安全的重要性。它们允许应用访问来自各种来源的数据,包括数据库、文件系统或网络。特定权限,如**`readPermission`**和**`writePermission`**,对于控制访问至关重要。此外,可以通过应用的Manifest中的**`grantUriPermission`**设置授予临时访问,利用`path`、`pathPrefix`和`pathPattern`等属性进行详细的访问控制。 输入验证至关重要,以防止漏洞,例如SQL注入。内容提供者支持基本操作:`insert()`、`update()`、`delete()`和`query()`,促进应用之间的数据操作和共享。 **FileProvider**,一种专门的内容提供者,专注于安全地共享文件。它在应用的Manifest中定义,具有特定属性以控制对文件夹的访问,由`android:exported`和`android:resource`指向文件夹配置。共享目录时需谨慎,以避免意外暴露敏感数据。 FileProvider的示例Manifest声明: ```xml ``` 在 `filepaths.xml` 中指定共享文件夹的示例: ```xml ``` 有关更多信息,请查看: - [Android Developers: Content Providers](https://developer.android.com/guide/topics/providers/content-providers) - [Android Developers: FileProvider](https://developer.android.com/training/secure-file-sharing/setup-sharing) ## WebViews WebViews 就像是 Android 应用中的 **迷你网页浏览器**,从网络或本地文件中提取内容。它们面临与常规浏览器类似的风险,但可以通过特定的 **设置** 来 **降低这些风险**。 Android 提供了两种主要的 WebView 类型: - **WebViewClient** 适合基本的 HTML,但不支持 JavaScript 警告功能,这会影响 XSS 攻击的测试方式。 - **WebChromeClient** 更像是完整的 Chrome 浏览器体验。 一个关键点是 WebView 浏览器 **不与设备的主浏览器共享 cookies**。 对于加载内容,可以使用 `loadUrl`、`loadData` 和 `loadDataWithBaseURL` 等方法。确保这些 URL 或文件是 **安全使用** 的至关重要。安全设置可以通过 `WebSettings` 类进行管理。例如,通过 `setJavaScriptEnabled(false)` 禁用 JavaScript 可以防止 XSS 攻击。 JavaScript "Bridge" 允许 Java 对象与 JavaScript 交互,从 Android 4.2 开始,要求方法使用 `@JavascriptInterface` 标记以确保安全。 允许内容访问 (`setAllowContentAccess(true)`) 使 WebViews 能够访问内容提供者,这可能会带来风险,除非内容 URL 被验证为安全。 要控制文件访问: - 禁用文件访问 (`setAllowFileAccess(false)`) 限制对文件系统的访问,某些资产除外,确保它们仅用于非敏感内容。 ## 其他应用组件和移动设备管理 ### **应用程序的数字签名** - **数字签名** 是 Android 应用的必要条件,确保它们在安装前是 **真实作者**。此过程使用证书进行应用识别,并必须在安装时由设备的包管理器进行验证。应用可以是 **自签名或由外部 CA 认证**,以防止未经授权的访问,并确保应用在传送到设备时保持未被篡改。 ### **增强安全性的应用验证** - 从 **Android 4.2** 开始,名为 **Verify Apps** 的功能允许用户在安装前检查应用的安全性。此 **验证过程** 可以警告用户潜在有害的应用,甚至阻止特别恶意的应用安装,从而增强用户安全性。 ### **移动设备管理 (MDM)** - **MDM 解决方案** 通过 **设备管理 API** 提供对移动设备的 **监督和安全**。它们需要安装 Android 应用以有效管理和保护移动设备。主要功能包括 **强制实施密码策略**、**要求存储加密** 和 **允许远程数据擦除**,确保对移动设备的全面控制和安全。 ```java // 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); } ``` {{#include ../../banners/hacktricks-training.md}}