Android Applications Basics

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)

支持HackTricks的其他方式:

Try Hard Security Group


Android安全模型

有两个层次:

  • 操作系统(OS),将安装的应用程序相互隔离。

  • 应用程序本身,允许开发人员公开某些功能并配置应用程序功能。

UID分离

每个应用程序被分配一个特定的用户ID。这是在应用程序安装期间完成的,因此应用程序只能与其用户ID拥有的文件或共享文件进行交互。因此,只有应用程序本身、操作系统的某些组件和根用户可以访问应用程序的数据。

UID共享

两个应用程序可以配置为使用相同的UID。这可能有助于共享信息,但如果其中一个受到损害,则两个应用程序的数据将受到损害。这就是为什么不鼓励这种行为。 要共享相同的UID,应用程序必须在其清单中定义相同的android:sharedUserId值。

沙箱

Android应用程序沙箱允许将每个应用程序作为单独的进程在单独的用户ID下运行。每个进程都有自己的虚拟机,因此应用程序的代码在与其他应用程序隔离的环境中运行。 从Android 5.0(L)开始,SELinux被强制执行。基本上,SELinux拒绝了所有进程交互,然后创建策略仅允许它们之间的预期交互

权限

当您安装一个应用程序并请求权限时,该应用程序正在请求在AndroidManifest.xml文件中配置的**uses-permission元素中配置的权限。uses-permission元素指示请求的权限的名称在name** 属性中。它还具有maxSdkVersion属性,该属性在高于指定版本的版本上停止请求权限。 请注意,Android应用程序不需要一开始就请求所有权限,它们也可以动态请求权限,但所有权限必须在清单中声明

当应用程序公开功能时,它可以限制仅允许具有指定权限的应用程序访问。 权限元素具有三个属性:

  • 权限的名称

  • permission-group属性,允许对相关权限进行分组。

  • protection-level,指示权限如何授予。有四种类型:

  • Normal:当应用程序没有已知威胁时使用。用户无需批准

  • Dangerous:指示权限授予请求应用程序一些提升的访问权限用户被要求批准

  • Signature:只有由与导出组件相同的证书签名的应用程序可以被授予权限。这是最强大的保护类型。

  • SignatureOrSystem:只有由与导出组件相同的证书签名的应用程序以系统级访问权限运行的应用程序可以被授予权限。

预安装应用程序

这些应用程序通常位于**/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过程非常危险,可能会严重损坏设备

ROM

可以通过安装自定义固件替换操作系统。这样做可以扩展旧设备的用途,绕过软件限制或访问最新的Android代码。 OmniROMLineageOS是两种最受欢迎的固件。

请注意,并非总是需要对设备进行root处理才能安装自定义固件。一些制造商允许以文件记录和安全的方式解锁其引导加载程序。

影响

一旦设备被root,任何应用程序都可以请求以root权限访问。如果恶意应用程序获得了这一权限,它将可以访问几乎所有内容,并且可以损坏手机。

Android应用程序基础知识

  • 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来创建应用程序。Android将此代码编译为Dalvik Executable (DEX) bytecode,而不是像桌面应用程序那样使用JVM。以前,Dalvik虚拟机处理这些字节码,但现在,在较新的Android版本中,Android Runtime (ART)接管了这一任务。

对于逆向工程,Smali变得至关重要。它是DEX bytecode的可读版本,类似于汇编语言,通过将源代码转换为字节码指令来实现。在这个上下文中,Smali和baksmali是指汇编和反汇编工具。

意图

意图是Android应用程序在其组件之间或与其他应用程序之间进行通信的主要手段。这些消息对象还可以在应用程序或组件之间传递数据,类似于HTTP通信中使用GET/POST请求的方式。

因此,Intent基本上是在组件之间传递的消息。Intent 可以定向到特定组件或应用程序,也可以发送给没有特定接收方的情况。 简单来说,Intent可以用于:

  • 启动Activity,通常打开应用程序的用户界面

  • 作为广播通知系统和应用程序的更改

  • 启动、停止和与后台服务通信

  • 通过ContentProviders访问数据

  • 作为回调来处理事件

如果存在漏洞,Intent可以用于执行各种攻击

意图过滤器

Intent Filters定义了活动、服务或广播接收器如何与不同类型的Intent交互。本质上,它们描述了这些组件的功能,例如它们可以执行哪些操作或可以处理哪些广播。声明这些过滤器的主要位置是在AndroidManifest.xml文件中,尽管对于广播接收器,也可以通过编码来实现。

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

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

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

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

隐式意图

意图是使用 Intent 构造函数在程序中创建的:

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

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

这个意图应该在清单文件中声明,就像以下示例中所示:

<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 resolution" 过程确定哪个应用程序应该接收每条消息。此过程考虑 priority attribute,该属性可以在 intent-filter declaration 中设置,优先级较高的将被选择。此优先级可以设置在 -1000 到 1000 之间,应用程序可以使用 SYSTEM_HIGH_PRIORITY 值。如果出现 冲突,将出现一个“选择器”窗口,以便 用户可以决定

显式 Intent

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

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

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

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

挂起意图

这允许其他应用程序代表您的应用程序执行操作,使用您应用程序的身份和权限。构造挂起意图时,应该指定一个意图和要执行的操作。如果声明的意图不是显式的(没有声明哪个意图可以调用它),恶意应用程序可以代表受害者应用程序执行声明的操作。此外,如果未指定操作,恶意应用程序将能够代表受害者执行任何操作

广播意图

与之前的意图不同,广播意图可以被多个应用程序接收。但是,从 API 版本 14 开始,可以使用 Intent.set Package 来指定应该接收消息的应用程序

另外,在发送广播时也可以指定权限。接收应用程序将需要拥有该权限。

两种广播:普通(异步)和有序(同步)。顺序基于接收器内配置的优先级每个应用程序可以处理、中继或丢弃广播

可以使用 Context 类中的 sendBroadcast(intent, receiverPermission) 函数发送广播。 您还可以使用**LocalBroadCastManager中的sendBroadcast函数,确保消息永远不会离开应用程序**。使用这种方式,甚至不需要导出接收器组件。

粘性广播

这种广播可以在发送后很长时间后访问。 这在 API 级别 21 中已被弃用,建议不要使用它们它们允许任何应用程序嗅探数据,但也可以修改数据

如果发现包含单词“粘性”的函数,如**sendStickyBroadcastsendStickyBroadcastAsUser**,检查影响并尝试删除它们

深层链接 / URL 方案

在 Android 应用程序中,深层链接用于通过 URL 直接启动操作(意图)。这是通过在活动中声明特定的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>
[...]

前面示例中的scheme是exampleapp://(还要注意**category BROWSABLE**)

然后,在数据字段中,您可以指定主机路径

<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应用程序中促进客户端和服务之间的通信。由于在Android上不允许直接访问另一个进程的内存,AIDL通过将对象编组成操作系统理解的格式来简化该过程,从而简化了跨不同进程的通信。

关键概念

  • 绑定服务:这些服务利用AIDL进行IPC,使活动或组件能够绑定到服务,发出请求并接收响应。服务类中的onBind方法对于启动交互至关重要,因此在寻找漏洞时,这是一个重要的安全审查领域。

  • Messenger:作为绑定服务运行,Messenger便于通过onBind方法处理数据的IPC。必须仔细检查此方法,以查找任何不安全的数据处理或执行敏感功能。

  • 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
}
}

服务

服务是能够在没有用户界面的情况下执行任务的后台操作者。这些任务可以在用户切换到不同应用程序时继续运行,因此服务对于长时间运行的操作至关重要。

服务具有多种用途;它们可以通过各种方式启动,其中**意图(Intents)**是启动服务的主要方法,作为应用程序的入口点。一旦使用startService方法启动服务,其onStart方法就会开始运行,并持续运行,直到显式调用stopService方法。或者,如果服务的作用取决于活动客户端连接,则使用bindService方法将客户端绑定到服务,通过onBind方法进行数据传递。

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

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

广播接收器

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

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

广播可以是异步的,无序地到达所有接收器,也可以是同步的,其中接收器根据设置的优先级获取广播。然而,需要注意潜在的安全风险,因为任何应用程序都可以将自己优先级提高以拦截广播。

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

内容提供程序

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

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

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

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>

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

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

WebViews

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

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

  • WebViewClient非常适合基本的HTML,但不支持JavaScript警报功能,影响了XSS攻击的测试方式。

  • WebChromeClient更像完整的Chrome浏览器体验。

一个关键点是,WebView浏览器不会与设备的主要浏览器共享cookie

用于加载内容的方法包括loadUrl, loadData, 和 loadDataWithBaseURL。确保这些URL或文件安全可用至关重要。安全设置可以通过WebSettings类进行管理。例如,使用setJavaScriptEnabled(false)禁用JavaScript可以防止XSS攻击。

JavaScript“桥梁”允许Java对象与JavaScript交互,需要在Android 4.2及更高版本中使用@JavascriptInterface标记方法以确保安全性。

允许内容访问(setAllowContentAccess(true))允许WebViews访问内容提供程序,这可能存在风险,除非内容URL经过验证为安全。

控制文件访问:

  • 禁用文件访问(setAllowFileAccess(false))限制对文件系统的访问,某些资产除外,确保它们仅用于非敏感内容。

其他应用组件和移动设备管理

应用程序的数字签名

  • 对于Android应用程序,数字签名是必不可少的,确保在安装之前它们是真实授权的。这个过程使用应用程序标识的证书,并且必须在设备的包管理器上安装时进行验证。应用程序可以是自签名的或由外部CA认证,防止未经授权的访问,并确保应用程序在传递到设备过程中保持不被篡改。

增强安全性的应用程序验证

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

移动设备管理(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);
}

尝试困难安全团队

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)

支持HackTricks的其他方式:

最后更新于