Android Applications Basics

Support 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商店中可用)。

请注意,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 Runtime(ART)在较新版本的Android中接管。

对于逆向工程,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**属性可以设置为强制要求只有具有指定权限的应用程序才能访问该组件,从而增加了安全性和对谁可以与其交互的控制。

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

隐式意图

意图是通过意图构造函数以编程方式创建的:

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

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).

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

<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 声明中设置,the one with the higher priority will be selected。这个优先级可以设置在 -1000 到 1000 之间,应用程序可以使用 SYSTEM_HIGH_PRIORITY 值。如果出现 conflict,将出现一个“选择器”窗口,以便 user can decide

显式 Intent

显式 intent 指定了它所针对的类名:

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

在其他应用程序中,为了访问先前声明的意图,您可以使用:

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 直接启动一个操作(Intent)。这是通过在活动中声明特定的URL 方案来完成的。当 Android 设备尝试访问具有此方案的 URL时,应用程序中的指定活动将被启动。

该方案必须在**AndroidManifest.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>
[...]

该示例中的方案是 exampleapp://(还要注意 category BROWSABLE

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

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

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

<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意图:

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

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

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

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

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

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

应用程序子类

在 Android 开发中,应用程序可以选择创建一个 子类Application 类,尽管这不是强制性的。当定义了这样的子类时,它将成为应用程序中第一个被实例化的类。如果在这个子类中实现了 attachBaseContext 方法,它将在 onCreate 方法之前执行。这个设置允许在应用程序的其余部分启动之前进行早期初始化。

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 文件中进行显式配置:

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

Broadcast Receivers

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

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

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

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

Content Provider

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

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

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

FileProvider的示例Manifest声明:

<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 中指定共享文件夹的示例:

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

For further information check:

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)) 限制对文件系统的访问,某些资产除外,确保它们仅用于非敏感内容。

Other App Components and Mobile Device Management

Digital Signing of Applications

  • 数字签名 是 Android 应用的必需,确保它们在安装前是 真实作者。此过程使用证书进行应用识别,并必须在安装时由设备的包管理器进行验证。应用可以是 自签名或由外部 CA 认证,以防止未经授权的访问,并确保应用在传送到设备时保持未被篡改。

App Verification for Enhanced Security

  • Android 4.2 开始,名为 验证应用 的功能允许用户在安装前检查应用的安全性。此 验证过程 可以警告用户潜在有害的应用,甚至阻止特别恶意的应用安装,从而增强用户安全。

Mobile Device Management (MDM)

  • MDM 解决方案 通过 设备管理 API 提供对移动设备的 监督和安全。它们需要安装 Android 应用以有效管理和保护移动设备。主要功能包括 强制密码策略要求存储加密允许远程数据擦除,确保对移动设备的全面控制和安全。

// 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);
}
支持 HackTricks

Last updated