Android Applications Basics

Reading time: 29 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

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访问请求,如SuperuserSuperSU(在Google Play商店中可用)。

caution

请注意,root过程非常危险,可能会严重损坏设备。

ROMs

可以通过安装自定义固件来替换操作系统。这样可以扩展旧设备的使用价值,绕过软件限制或获得最新的Android代码。
OmniROMLineageOS是两个最流行的固件。

请注意,并不总是需要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 Filters定义活动、服务或广播接收器如何与不同类型的Intents交互。本质上,它们描述了这些组件的能力,例如它们可以执行的操作或可以处理的广播类型。声明这些过滤器的主要位置是在AndroidManifest.xml文件中,尽管对于广播接收器,编码它们也是一个选项。

Intent Filters由类别、操作和数据过滤器组成,并可以包含附加元数据。此设置允许组件处理与声明的标准匹配的特定Intents。

Android组件(活动/服务/内容提供者/广播接收器)的一个关键方面是它们的可见性或公共状态。如果组件的**exported值为true,或者在清单中声明了Intent Filter,则该组件被视为公共的,可以与其他应用程序交互。然而,开发人员可以明确将这些组件保持私有,以确保它们不会与其他应用程序意外交互。这是通过在其清单定义中将exported属性设置为false**来实现的。

此外,开发人员还有选择进一步保护对这些组件的访问的选项,要求特定权限。**permission**属性可以设置为强制要求只有具有指定权限的应用程序才能访问该组件,从而增加了安全性和对谁可以与其交互的控制。

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

隐式意图

意图是通过意图构造函数程序化创建的:

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

Action为先前声明的意图的ACTION_SENDExtra是一个mailto Uri(Extra是意图所期望的额外信息)。

此意图应在清单中声明,如以下示例所示:

xml
<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 需要匹配 actiondatacategory 才能接收消息。

“Intent 解析”过程决定了哪个应用程序应该接收每个消息。该过程考虑 priority attribute,可以在 intent-filter 声明中设置,优先级更高的将被选择。这个优先级可以设置在 -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”一词的函数,如**sendStickyBroadcastsendStickyBroadcastAsUser**,检查影响并尝试删除它们

在 Android 应用程序中,深度链接用于通过 URL 直接启动一个操作(意图)。这是通过在活动中声明特定的URL 方案来完成的。当 Android 设备尝试访问带有此方案的 URL时,应用程序中的指定活动将被启动。

该方案必须在**AndroidManifest.xml**文件中声明:

xml
[...]
<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>
[...]

前一个示例中的方案是 examplescheme://(还要注意 category BROWSABLE

然后,在数据字段中,您可以指定 hostpath

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

要从网页访问,可以设置一个链接,如:

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

为了找到将在应用中执行的代码,请转到由深度链接调用的活动,并搜索函数**onNewIntent**。

了解如何在不使用HTML页面的情况下调用深度链接

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找到。

组件

这些包括:活动、服务、广播接收器和提供者。

启动活动和其他活动

在Android应用中,活动就像屏幕,显示应用用户界面的不同部分。一个应用可以有许多活动,每个活动向用户呈现一个独特的屏幕。

启动活动是应用的主要入口,当您点击应用图标时启动。它在应用的清单文件中定义,具有特定的MAIN和LAUNCHER意图:

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

并非所有应用都需要启动器活动,特别是那些没有用户界面的应用,如后台服务。

通过在清单中将活动标记为“exported”,可以使其对其他应用或进程可用。此设置允许其他应用启动此活动:

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

然而,从另一个应用访问一个活动并不总是安全风险。问题出现在敏感数据被不当共享时,这可能导致信息泄露。

一个活动的生命周期 从 onCreate 方法开始,设置用户界面并准备活动与用户的交互。

应用子类

在 Android 开发中,应用可以选择创建一个 子类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后台操作,能够在没有用户界面的情况下执行任务。这些任务即使在用户切换到不同应用程序时也可以继续运行,使得服务对于 长时间运行的操作 至关重要。

服务是多功能的;它们可以通过多种方式启动,其中 Intents 是作为应用程序入口点启动它们的主要方法。一旦使用 startService 方法启动服务,其 onStart 方法就会启动并持续运行,直到显式调用 stopService 方法。或者,如果服务的角色依赖于活动的客户端连接,则使用 bindService 方法将客户端绑定到服务,激活 onBind 方法进行数据传递。

服务的一个有趣应用包括后台音乐播放或网络数据获取,而不妨碍用户与应用的交互。此外,服务可以通过 导出 使其他进程在同一设备上可访问。这不是默认行为,需要在 Android Manifest 文件中进行显式配置:

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

广播接收器

广播接收器充当消息系统中的监听器,允许多个应用程序响应来自系统的相同消息。应用程序可以通过应用的Manifest或在应用代码中通过**registerReceiver** API以两种主要方式注册接收器。在Manifest中,广播通过权限进行过滤,而动态注册的接收器在注册时也可以指定权限。

意图过滤器在这两种注册方法中至关重要,决定哪些广播触发接收器。一旦发送匹配的广播,接收器的**onReceive**方法将被调用,使应用能够相应地反应,例如在接收到低电量警报时调整行为。

广播可以是异步的,所有接收器无序接收,或同步的,接收器根据设定的优先级接收广播。然而,重要的是要注意潜在的安全风险,因为任何应用都可以优先处理自己以拦截广播。

要理解接收器的功能,请查找其类中的**onReceive方法。该方法的代码可以操作接收到的Intent,强调接收器进行数据验证的必要性,特别是在有序广播**中,这可能会修改或丢弃Intent。

内容提供者

内容提供者对于应用之间共享结构化数据至关重要,强调实施权限以确保数据安全的重要性。它们允许应用访问来自各种来源的数据,包括数据库、文件系统或网络。特定权限,如**readPermissionwritePermission,对于控制访问至关重要。此外,可以通过应用的Manifest中的grantUriPermission**设置授予临时访问,利用pathpathPrefixpathPattern等属性进行详细的访问控制。

输入验证至关重要,以防止漏洞,例如SQL注入。内容提供者支持基本操作:insert()update()delete()query(),促进应用之间的数据操作和共享。

FileProvider,一种专门的内容提供者,专注于安全地共享文件。它在应用的Manifest中定义,具有特定属性以控制对文件夹的访问,由android:exportedandroid:resource指向文件夹配置。共享目录时需谨慎,以避免意外暴露敏感数据。

FileProvider的示例Manifest声明:

xml
<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>

filepaths.xml 中指定共享文件夹的示例:

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

有关更多信息,请查看:

WebViews

WebViews 就像是 Android 应用中的 迷你网页浏览器,从网络或本地文件中提取内容。它们面临与常规浏览器类似的风险,但可以通过特定的 设置降低这些风险

Android 提供了两种主要的 WebView 类型:

  • WebViewClient 适合基本的 HTML,但不支持 JavaScript 警告功能,这影响了 XSS 攻击的测试方式。
  • WebChromeClient 更像是完整的 Chrome 浏览器体验。

一个关键点是 WebView 浏览器 不与设备的主浏览器共享 cookies

对于加载内容,可以使用 loadUrlloadDataloadDataWithBaseURL 等方法。确保这些 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);
}

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks