hacktricks/mobile-pentesting/android-app-pentesting/android-applications-basics.md

31 KiB
Raw Blame History

Android应用程序基础知识

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥

找到最重要的漏洞以便您可以更快地修复它们。Intruder跟踪您的攻击面运行主动威胁扫描发现整个技术堆栈中的问题从API到Web应用程序和云系统。立即免费试用

{% embed url="https://www.intruder.io/?utm_campaign=hacktricks&utm_source=referral" %}


Android安全模型

有两个层次:

  • 操作系统,将安装的应用程序相互隔离。
  • 应用程序本身,允许开发人员公开某些功能并配置应用程序的功能。

UID分离

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

UID共享

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

沙箱

Android应用程序沙箱允许将每个应用程序作为单独的进程在单独的用户ID下运行。每个进程都有自己的虚拟机,因此应用程序的代码在其他应用程序中运行时处于隔离状态。
从Android 5.0L开始强制执行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权限

  • AOSPAndroid开源项目ROM一起提供的应用程序
  • 设备制造商添加的应用程序
  • 手机提供商添加的应用程序(如果从他们那里购买)

Root权限

为了在物理Android设备上获得root访问权限通常需要利用1或2个漏洞,这些漏洞通常是针对设备版本特定漏洞。
一旦漏洞成功通常会将Linux的su二进制文件复制到用户的PATH环境变量指定的位置/system/xbin

配置su二进制文件后使用另一个Android应用程序与su二进制文件进行接口交互并处理对root访问的请求SuperuserSuperSU可在Google Play商店中获得

{% hint style="danger" %} 请注意root权限过程非常危险可能会严重损坏设备 {% endhint %}

ROMs

可以通过安装自定义固件来替换操作系统。这样做可以扩展旧设备的功能绕过软件限制或者获取最新的Android代码。OmniROMLineageOS是两个最受欢迎的固件。

需要注意的是并不总是需要对设备进行root操作才能安装自定义固件。一些制造商允许以一种经过充分记录和安全的方式解锁他们的引导程序。

影响

一旦设备被root任何应用都可以请求以root权限运行。如果一个恶意应用程序获取了root权限它将能够访问几乎所有内容并且可以对手机造成损害。

Android应用程序基础

这个介绍来自于https://maddiestone.github.io/AndroidAppRE/app_fundamentals.html

基础知识回顾

  • Android应用程序以_APK文件格式_存在。APK基本上是一个ZIP文件。(您可以将文件扩展名更改为.zip并使用解压缩工具打开并查看其内容。
  • APK内容不详尽
  • AndroidManifest.xml
  • resources.arsc/strings.xml
  • resources.arsc包含预编译资源的文件例如二进制XML。
  • res/xml/files_paths.xml
  • META-INF/
  • 证书存放在这里!
  • classes.dex
  • 应用程序的Dalvik字节码以DEX文件格式编译。这是应用程序将默认运行的Java或Kotlin代码
  • lib/
  • 应用程序的本地库默认存放在这里在lib/目录下有特定于CPU的目录。
  • armeabi仅针对所有基于ARM的处理器的编译代码
  • armeabi-v7a仅针对所有基于ARMv7及以上的处理器的编译代码
  • x86针对X86的编译代码
  • mips仅针对MIPS处理器的编译代码
  • assets/
  • 应用程序可能需要的其他文件。
  • 可能会在此处包含其他本地库或DEX文件。这种情况通常发生在恶意软件作者想要尝试“隐藏”附加代码本地或Dalvik而不将其包含在默认位置中时。
  • res/
  • 包含未编译到resources.arsc中的资源的目录

Dalvik和Smali

大多数Android应用程序是用Java编写的。Kotlin也受支持并且与Java可互操作。为了方便起见在本次研讨会的其余部分中当我提到“Java”时您可以认为我是指“Java或Kotlin”。与桌面应用程序不同Android中的Java代码不是在Java虚拟机JVM中运行而是编译为_Dalvik可执行DEX字节码格式。在较早版本的Android中字节码由Dalvik虚拟机翻译。在较新版本的Android中使用Android运行时ART
如果开发人员使用Java编写代码并将其编译为DEX字节码那么在进行反向工程时我们将按相反的方向进行工作。
\

开发人员流程图。Java到DEX字节码

逆向工程师流程图。DEX字节码到SMALI到反编译的Java

Smali是Dalvik字节码的可读版本。从技术上讲Smali和baksmali是工具的名称汇编器和反汇编器但在Android中我们经常使用术语“Smali”来指代指令。如果您曾经对编译的C/C++代码进行过反向工程或计算机体系结构研究,那么SMALI就像汇编语言介于高级源代码和字节码之间

找出最重要的漏洞以便更快地修复它们。Intruder跟踪您的攻击面运行主动威胁扫描发现整个技术堆栈中的问题从API到Web应用程序和云系统。立即免费试用

{% embed url="https://www.intruder.io/?utm_campaign=hacktricks&utm_source=referral" %}


意图Intents

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

因此,意图基本上是在组件之间传递的消息。意图可以指定特定的组件或应用程序,也可以发送给没有特定接收者的组件
简单来说,意图可以用于以下目的:

  • 启动Activity通常打开应用程序的用户界面
  • 作为广播,通知系统和应用程序发生的变化
  • 启动、停止和与后台服务通信
  • 通过ContentProviders访问数据
  • 作为回调来处理事件

不正确的实现可能导致数据泄露、调用受限函数和操纵程序流程。

意图过滤器Intent-Filter

意图过滤器指定了活动、服务或广播接收器可以响应的意图类型。它指定了活动或服务可以执行的操作以及接收器可以处理的广播类型。它允许相应的组件接收声明类型的意图。意图过滤器通常是通过AndroidManifest.xml文件定义的。对于广播接收器,也可以在编码中定义。意图过滤器由其类别、操作和数据过滤器定义。它还可以包含其他元数据。

在Android中当**exported设置为true时,活动/服务/内容提供程序/广播接收器是公开的**,但是如果清单文件为其指定了意图过滤器,则组件也是公开的。然而,
开发人员可以通过在清单文件中为每个组件将**exported属性设置为false显式地将组件设置为私有(无论是否存在任何意图过滤器)。
开发人员还可以通过设置
permission属性来要求特定权限来访问**组件,从而限制对组件的访问。

隐式意图

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

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

之前声明的意图的ActionACTION_SENDExtra是一个mailto UriExtra是意图期望的额外信息

这个意图应该在清单文件中声明,如下面的示例所示:

<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属性,它可以在intent-filter声明中设置,优先级较高的将被选择。这个优先级可以在-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);

Pending Intents

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

Broadcast Intents

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

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

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

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

Sticky Broadcasts

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

如果您发现包含"sticky"一词的函数如sendStickyBroadcast或sendStickyBroadcastAsUser请检查其影响并尝试删除它们。

深层链接允许通过URL触发意图。应用程序可以在活动中声明一个URL模式因此每当Android设备尝试使用该模式访问地址时应用程序的活动将被调用

在这种情况下,模式是myapp://(还注意到category BROWSABLE

如果在intent-filter中找到类似以下内容的内容:

那么,它期望的是类似http://www.example.com/gizmos的东西

如果找到类似以下内容的内容:

这意味着它期望以example://gizmos开头的URL
在这种情况下您可以尝试滥用功能创建一个包含以下有效负载的网页。它将尝试导航到任意页面并尝试执行JS

<a href="example://gizmos/https://google.com">click here</a>
<a href="example://gizmos/javascript://%250dalert(1)">click here</a>

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

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

AIDL - Android接口定义语言

Android接口定义语言AIDL允许您定义客户端和服务之间达成一致的编程接口以便使用进程间通信进行通信。在Android上一个进程通常无法访问另一个进程的内存。因此为了进行通信它们需要将对象分解为操作系统可以理解的基本类型并在此边界上对对象进行编组。编写执行此编组的代码很繁琐因此Android使用AIDL来处理。

使用AIDL的服务称为绑定服务。在服务的类中您将找到onBind方法。这是交互开始的地方,因此这是要检查的代码的初始部分,以寻找潜在的漏洞。

绑定服务是客户端-服务器接口中的服务器。它允许组件如活动绑定到服务发送请求接收响应并执行进程间通信IPC。绑定服务通常仅在为另一个应用程序组件提供服务时才存在并且不会无限期在后台运行。

Messenger

Messenger是另一种IPC机制。由于Messenger也是“绑定服务”因此从客户端应用程序传递的数据也会通过onBind方法进行处理。因此,代码审查应从此方法开始,并查找对敏感功能的调用或对数据的不安全处理。

Binder

直接调用Binder类很奇怪因为使用AIDL抽象了Binder类更容易。但是了解Binder是一个在内核级别移动数据从一个进程的内存到另一个进程的驱动程序是很好的。

组件

这些包括活动Activities、服务Services、广播接收器Broadcast Receivers和内容提供者Providers

启动器活动和其他活动

Android活动是Android应用程序用户界面的一个屏幕。在这种方式上Android活动与桌面应用程序中的窗口非常相似。一个Android应用程序可以包含一个或多个活动意味着一个或多个屏幕。

启动器活动是大多数人认为是Android应用程序的入口点。启动器活动是当用户点击应用程序图标时启动的活动。您可以通过查看应用程序的清单来确定启动器活动。启动器活动将列出以下MAIN和LAUNCHER意图。

请记住并非每个应用程序都会有一个启动器活动特别是没有UI的应用程序。没有UI的应用程序因此也没有启动器活动的示例是在后台执行服务的预安装应用程序例如语音邮件。

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

活动可以被导出,允许设备上的其他进程启动该活动。默认情况下,它们不会被导出,但是您可以通过设置来导出它们:

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

请注意,绕过活动保护并不总是漏洞,您需要检查您已获得访问权限的数据。此外,某些活动会将数据返回给调用者。在这种情况下,您需要搜索**setResult**方法并检查传递给Intent参数的数据。如果这是敏感数据,您可能存在信息泄露漏洞,并且可以利用能够与活动通信的应用程序进行利用。

活动的代码从onCreate方法开始

应用程序子类

Android应用程序可以定义一个Application子类。应用程序可以定义一个自定义的Application子类但不一定要这样做。如果Android应用程序定义了一个Application子类此类在应用程序中的任何其他类之前实例化

如果在Application子类中定义了**attachBaseContext方法,则在onCreate**方法之前调用它。

服务

服务在后台运行,没有用户界面。它们用于执行长时间运行的进程,即使用户开始使用其他应用程序

它们可以通过各种方式启动,因此是应用程序的入口点。启动服务作为应用程序入口点的默认方式是通过意图Intents

当调用**startService方法启动服务时,服务中的onStart方法将被执行。它将无限期运行,直到调用stopService方法。如果只需要在客户端连接时使用服务,客户端应使用bindService**方法进行“绑定”。

对于绑定服务(参见前一节),数据将传递给**onBind**方法。

例如,服务可以在用户位于其他应用程序时在后台播放音乐,或者可以在不阻塞与活动的用户交互的情况下从网络获取数据。

服务可以导出,允许设备上的其他进程启动服务。默认情况下,服务不会导出,但可以在清单文件中进行配置:

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

广播接收器

广播可以被看作是一种消息系统,而广播接收器则是监听器。如果一个应用程序为特定的广播注册了接收器,当系统发送广播时,接收器中的代码将被执行。需要注意的是,在这种情况下,多个应用程序可以接收相同的消息

两种方式可以注册接收器:在应用程序的清单中注册或在应用程序的代码中使用**registerReceiver** API调用进行动态注册。在清单中可以通过在接收器元素中使用权限来限制接受的广播。当动态定义时可以将权限传递给registerReceiver方法。

在这两种情况下,都需要设置接收器的意图过滤器来注册接收器。这些意图过滤器是应该触发接收器的广播。

当发送接收器注册的特定广播时将执行BroadcastReceiver类中的**onReceive**方法。

例如,应用程序可以为低电量消息注册一个接收器,并根据该信息更改其行为。

广播可以是异步的(每个接收器都会接收到它)或同步的(广播按照设置的优先级有序接收)。

{% hint style="danger" %} 请注意,任何应用程序都可以将自己设置为接收广播的最高优先级。 {% endhint %}

检查广播接收器中实现的代码,需要搜索接收器类的**onReceive**方法。
需要注意的是,有序广播可以丢弃接收到的Intent甚至修改它使用其中一个setter方法。因此接收器应该验证数据

内容提供器

内容提供器是应用程序共享结构化数据(如关系数据库)的方式。因此,使用权限并设置适当的保护级别来保护它们非常重要。
内容提供器可以使用**readPermissionwritePermission属性来指定应用程序必须具有的权限。这些权限优先于权限属性
此外,它们还可以通过将
grantUriPermission设置为true然后在清单文件中的提供程序元素内配置适当的参数来允许临时例外**。

**grant-uri-permission**有三个属性path、pathPrefix和pathPattern

  • path:允许指定要排除的整个路径
  • pathPrefix:允许指定路径的开头
  • pathPattern:允许使用通配符和符号替换来获得更精细的控制。

重要的是验证和清理接收到的输入以避免潜在的漏洞如SQL注入。

内容提供器的特点:

  • 内容提供器组件根据请求从一个应用程序向其他应用程序提供数据。
  • 您可以将数据存储在文件系统、SQLite数据库、Web上或应用程序可以访问的任何其他持久存储位置中。
  • 通过内容提供器,其他应用程序可以查询甚至修改数据(如果内容提供器允许)。
  • 内容提供器在应用程序想要与另一个应用程序共享数据的情况下非常有用。
  • 它与数据库非常相似,有四种方法。
  • insert()
  • update()
  • delete()
  • query()

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>

请注意**android:exported属性,如果它的值为true,外部应用程序将能够访问共享文件夹。
请注意,配置项android:resource="@xml/filepaths"表示文件_res/xml/filepaths.xml_包含了
FileProvider将要共享的文件夹**的配置。以下是在该文件中指定共享文件夹的示例:

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

共享像 path="." 这样的东西可能是危险的,即使提供者没有被导出,如果代码的某个部分尝试访问这个提供者时存在其他漏洞。
你可以使用 content://com.example.myapp.fileprovider/myimages/default_image.jpg 来访问该文件夹内的一个图像

<paths> 元素可以有多个子元素,每个子元素指定一个不同的目录进行共享。除了 <files-path> 元素外,还可以使用 <external-path> 元素来共享外部存储中的目录,以及使用 <cache-path> 元素来共享内部缓存目录中的目录。
有关特定文件提供者属性的更多信息,请点击此处。

在此处了解有关文件提供者的更多信息

WebViews

WebViews 是嵌入到 Android 应用程序中的网页浏览器
WebViews 的内容可以来自远程站点,也可以是包含在应用程序中的文件。
WebViews 受到影响任何网页浏览器的相同漏洞的攻击。但是,有一些配置可以用来限制攻击

Android 中有两种类型的 WebViews

  • WebViewClient,最适合简单的 HTML 渲染。它不会运行 JS 的 alert 函数。因此,使用该函数进行 XSS 测试将无效。
  • WebChrome client,是一个 Chrome 浏览器。

请注意,WebView 浏览器无法访问本机浏览器的 cookie

可以使用 loadUrlloadDataloadDataWithBaseURL 函数来加载 URL 或文件。只能访问经过消毒处理的 URL 是很重要的
WebView 的安全性可以通过 WebSettings 对象进行配置。
例如,可以使用 setJavaScriptEnabled 方法将 JS 代码执行禁用为 false 值。这将消除 XSS 和其他与 JS 相关的漏洞的可能性。

JavaScript 的 "Bridge" 功能将 Java 对象注入到 WebView 中,使其可以被 JS 访问。从 Android 4.2 开始,必须使用 @JavascriptInterface 进行方法注释,以便 JavaScript 可以访问它们。

如果将 true 传递给 setAllowContentAccess,则 WebView 将能够通过 content:// 方案访问内容提供者。这显然会带来安全风险。请注意,如果给予此访问权限,非常重要的是确保 content:// URL 是安全的

默认情况下WebViews 可以通过 file:// URL 访问本地文件,但有几种方法可以阻止此行为:

  • false 传递给 setAllowFileAccess,防止访问文件系统,但允许通过 file:///android_assetfile:///android_res 访问资源。这些路径应仅用于非敏感数据(如图像),因此这应该是安全的。
  • setAllowFileAccess 方法指示是否应该允许来自 file:// URL 的路径能够访问其他文件方案 URL 的内容。
  • setAllowUniversalAccessFromFileURLs 方法指示是否应该允许来自 file:// URL 的路径能够访问任何来源的内容。

其他应用组件

应用程序签名

  • Android 要求在应用程序安装之前,所有应用程序都必须使用证书进行数字签名。Android 使用此证书来识别应用程序的作者。
  • 要在设备上运行应用程序,它必须经过签名。当应用程序安装到设备上时,包管理器会验证应用程序是否已使用 apk 文件中的证书正确签名。
  • 应用程序可以自签名,也可以通过 CA 进行签名。
  • 应用程序签名确保一个应用程序不能访问任何其他应用程序,除非通过明确定义的 IPC并且它未经修改地传递到设备上。

应用程序验证

  • Android 4.2 及更高版本支持应用程序验证。用户可以选择启用“验证应用程序”,并在安装之前由应用程序验证器对应用程序进行评估。
  • 应用程序验证可以在用户尝试安装可能有害的应用程序时向用户发出警报;如果应用程序特别糟糕,它可以阻止安装。

移动设备管理

MDM 或移动设备管理是用于在移动设备上确保控制和安全要求的软件套件。这些套件使用称为设备管理 API 的功能,并需要安装一个 Android 应用程序。

通常MDM 解决方案执行诸如强制密码策略、强制加密存储和启用远程擦除设备数据等功能。

找到最重要的漏洞以便您可以更快地修复它们。Intruder 跟踪您的攻击面,运行主动威胁扫描,发现整个技术堆栈中的问题,从 API 到 Web 应用程序和云系统。立即免费试用

{% embed url="https://www.intruder.io/?utm_campaign=hacktricks&utm_source=referral" %}


☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥