26 KiB
Android 应用基础知识
从零开始学习 AWS 黑客技术,成为专家 htARTE(HackTricks AWS 红队专家)!
支持 HackTricks 的其他方式:
- 如果您想看到您的公司在 HackTricks 中做广告或下载 PDF 版本的 HackTricks,请查看订阅计划!
- 获取官方 PEASS & HackTricks 商品
- 探索PEASS 家族,我们的独家NFT收藏品
- 加入 💬 Discord 群组 或 电报群组 或在 Twitter 🐦 @carlospolopm** 上关注我**。
- 通过向 HackTricks 和 HackTricks Cloud 的 github 仓库提交 PR 来分享您的黑客技巧。
发现最重要的漏洞,以便更快地修复它们。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.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 访问的请求,如 Superuser 和 SuperSU(在 Google Play 商店中可用)。
{% hint style="danger" %} 请注意,root 过程非常危险,可能会严重损坏设备 {% endhint %}
ROM
可以通过安装自定义固件来替换操作系统。这样做可以扩展旧设备的实用性,绕过软件限制或访问最新的 Android 代码。
OmniROM 和 LineageOS 是最受欢迎的两种固件。
请注意,不总是需要 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 不像桌面应用程序那样在 Java 虚拟机(JVM)中运行 Java 代码,而是将 Java 编译为 Dalvik 可执行(DEX)字节码。
- 历史上,字节码的翻译由 Dalvik 虚拟机处理,而更近期的 Android 版本使用 Android 运行时(ART)。
- 反向工程过程涉及将 DEX 字节码反编译为人类可读格式。
Smali 是 Dalvik 字节码的人类可读形式。虽然 "Smali" 和 "baksmali" 在技术上指的是汇编器和反汇编器工具,但在 Android 上下文中,“Smali”通常指的是指令本身。SMALI 类似于汇编语言,作为源代码和字节码之间的中介。
Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));
Action之前声明的意图是ACTION_SEND,Extra是一个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 需要匹配 action、data 和 category 才能接收消息。
"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);
Pending Intents
这些允许其他应用代表您的应用执行操作,使用您应用的身份和权限。构建一个 Pending Intent 时,应该指定一个意图和要执行的操作。如果声明的意图不是显式的(没有声明哪个意图可以调用它),一个恶意应用程序可以代表受害者应用执行声明的操作。此外,如果没有指定操作,恶意应用程序将能够代表受害者执行任何操作。
Broadcast Intents
与之前的意图不同,广播意图可以被多个应用接收。但是,从 API 版本 14 开始,可以使用 Intent.set Package 来指定应该接收消息的应用程序。
另外,在发送广播时也可以指定权限。接收应用程序将需要拥有该权限。
有两种广播:普通(异步)和有序(同步)。顺序基于接收器内配置的优先级。每个应用程序可以处理、中继或丢弃广播。
可以使用 Context
类中的函数 **sendBroadcast(intent, receiverPermission)
** 来发送广播。
您还可以使用 LocalBroadCastManager
中的函数 sendBroadcast
,确保消息永远不会离开应用程序。使用这种方式,甚至不需要导出接收器组件。
Sticky Broadcasts
这种广播可以在发送后很长时间后访问。
这在 API 级别 21 中已被弃用,建议不要使用它们。
它们允许任何应用程序嗅探数据,但也可以修改数据。
如果发现包含单词“sticky”的函数,如**sendStickyBroadcast
或sendStickyBroadcastAsUser
**,检查影响并尝试删除它们。
Deep links / URL schemes
Deep links 允许通过 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 Interface Definition Language
Android Interface Definition Language(AIDL)允许您定义客户端和服务之间同意的编程接口,以便它们可以通过进程间通信(IPC)相互通信。在Android上,一个进程通常无法访问另一个进程的内存。因此,它们需要将对象分解为操作系统可以理解的基元,并在对象之间进行对象的编组以进行通信。编写执行此编组的代码很繁琐,因此Android会使用AIDL为您处理。
使用AIDL的服务称为绑定服务。在服务类中,您将找到**onBind
方法。这是交互开始的地方**,因此这是要审查的代码的初始部分,以寻找潜在的漏洞。
绑定服务是客户端-服务器接口中的服务器。它允许组件(如活动)绑定到服务,发送请求,接收响应,并执行进程间通信(IPC)。绑定服务通常仅在为另一个应用程序组件提供服务时才存在,并且不会无限期在后台运行。
Messenger
Messenger是另一种IPC机制。由于Messenger也是“绑定服务”,因此从客户端应用程序传递的数据也通过onBind
方法进行处理。因此,代码审查应从此方法开始,并查找对敏感功能的调用或对数据的不安全处理。
Binder
直接调用Binder类很奇怪,因为使用AIDL(抽象Binder类)要容易得多。但是,了解Binder是一个内核级驱动程序,可将数据从一个进程的内存移动到另一个进程的内存是很有用的。
组件
这些包括:活动、服务、广播接收器和提供程序。
启动器活动和其他活动
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
**方法。
服务
服务 在后台运行,没有用户界面。它们用于执行长时间运行的进程,即使用户开始使用不同的应用程序。
它们可以以多种方式启动,因此是应用程序的入口点。启动服务作为应用程序的入口点的默认方式是通过意图。
当调用**startService
方法启动服务时,服务中的onStart
方法将被执行。它将一直运行,直到调用stopService
方法。如果服务只在客户端连接时需要,则客户端应使用bindService
**方法“绑定”到它。
对于绑定服务(请参阅上一节),数据将传递给**onBind
**方法。
例如,服务可能在用户使用不同应用程序时在后台播放音乐,或者在不阻止用户与活动交互的情况下通过网络获取数据。
服务可以被导出,允许设备上的其他进程启动服务。默认情况下,服务不会被导出,但可以在清单中进行配置:
<service android:name=".ExampleExportedService" android:exported="true"/>
广播接收器
广播可以被视为一种消息系统,广播接收器是监听器。如果一个应用程序已经为特定广播注册了一个接收器,当系统发送广播时,该接收器中的代码将被执行。请注意,在这种情况下,多个应用程序可以接收相同的消息。
应用程序可以通过两种方式注册接收器:在应用程序清单中注册或在应用程序代码中使用**registerReceiver
** API调用动态注册。在清单中,您可以通过在接收器元素中使用权限来限制您接受的广播。在动态定义时,您可以将权限传递给registerReceiver
方法。
在这两种情况下,要注册接收器,需要设置接收器的意图过滤器。这些意图过滤器是应该触发接收器的广播。
当发送接收器已注册的特定广播时,将执行BroadcastReceiver类中的onReceive
。
例如,应用程序可以为低电量消息注册一个接收器,并根据该信息更改其行为。
广播可以是异步的(每个接收器都会接收到)或同步的(广播按照设置的优先级有序接收)。
{% hint style="danger" %} 请注意,任何应用程序都可以将自己设置为接收广播的最高优先级。 {% endhint %}
要检查广播接收器中实现的代码,您需要搜索接收器类的**onReceive
**方法。请注意,有序广播可能会丢弃接收到的Intent,甚至使用其中一个setter方法修改它。因此,接收器应该验证数据。
内容提供者
内容提供者是应用程序共享结构化数据的方式,例如关系数据库。因此,使用权限并设置适当的保护级别来保护它们非常重要。
内容提供者可以使用**readPermission
和writePermission
属性来指定应用程序必须具有的权限。这些权限优先于权限属性。
此外,它们还可以通过将grantUriPermission
设置为true,然后在清单文件中的提供者元素内部的grant-uri-permission
元素中配置适当的参数来允许临时例外**。
**grant-uri-permission
**有三个属性:path、pathPrefix和pathPattern:
- path:允许指定要排除的整个路径
- pathPrefix:允许指定路径的开头
- pathPattern:允许使用通配符和符号替换以获得更精细的控制。
重要的是验证和清理接收到的输入,以避免潜在的漏洞,如SQL注入。
内容提供者特点:
- 内容提供者组件根据请求从一个应用程序向其他应用程序提供数据。
- 您可以将数据存储在文件系统、SQLite数据库、网络上或应用程序可以访问的任何其他持久存储位置。
- 通过内容提供者,其他应用程序可以查询或甚至修改数据(如果内容提供者允许)。
- 内容提供者在应用程序希望与另一个应用程序共享数据的情况下非常有用。
- 它与数据库非常相似,并具有四种方法。
- 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 应用程序中的Web浏览器。
WebViews 的内容可以从远程站点获取,也可以是包含在应用程序中的文件。
WebViews 容易受到影响任何 Web 浏览器的相同漏洞。但是,有一些配置可以有助于限制 攻击 面。
Android 中有两种类型的 WebViews:
- WebViewClient,最适合简单的 HTML 渲染。这不会运行 JS 警报功能。因此,使用该功能进行 XSS 测试将无效。
- WebChrome client,是 Chrome 浏览器。
请注意,WebView 浏览器无法访问本机浏览器的 Cookie。
要加载 URL 或文件,可以使用 loadUrl
、loadData
或 loadDataWithBaseURL
函数。只能访问经过消毒的 URL 是很重要的。
可以通过 WebSettings
对象配置 WebView 安全性。
例如,可以使用 setJavaScriptEnabled
方法将 JS 代码执行禁用为 false
值。这将消除 XSS 和其他与 JS 相关的漏洞的可能性。
JavaScript "Bridge" 功能将 Java 对象注入到 WebView 中,使其可供 JS 访问。从 Android 4.2 开始,必须使用 @JavascriptInterface
对方法进行注释,以便让 JavaScript 访问。
如果将 true
传递给 setAllowContentAccess
,WebViews 将能够通过 content://
方案访问内容提供者。这显然存在安全风险。请注意,如果授予此访问权限,非常重要的是确保 content://
URL 是安全的。
默认情况下,WebViews 可以通过 file:// URL 访问本地文件,但有几种方法可以防止这种行为:
- 将
false
传递给setAllowFileAccess
,可以防止访问文件系统,但可以通过file:///android_asset
和file:///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" %}