Android Applications Basics
Reading time: 33 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
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访问请求,如Superuser和SuperSU(在Google Play商店中可用)。
caution
请注意,root过程非常危险,可能会严重损坏设备。
ROMs
可以通过安装自定义固件来替换操作系统。这样可以扩展旧设备的实用性,绕过软件限制或访问最新的Android代码。
OmniROM和LineageOS是两个最流行的固件。
请注意,并不总是需要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 Filters定义活动、服务或广播接收器如何与不同类型的Intents交互。本质上,它们描述了这些组件的能力,例如它们可以执行的操作或可以处理的广播类型。声明这些过滤器的主要位置是在AndroidManifest.xml文件中,尽管对于广播接收器,编码它们也是一种选择。
Intent Filters由类别、操作和数据过滤器组成,并可以包含附加元数据。此设置允许组件处理与声明的标准匹配的特定Intents。
Android组件(活动/服务/内容提供者/广播接收器)的一个关键方面是它们的可见性或公共状态。如果组件的**exported
值为true
,则该组件被视为公共的,可以与其他应用程序交互;如果在清单中声明了Intent Filter,则也是如此。然而,开发人员可以明确保持这些组件私有,确保它们不会与其他应用程序意外交互。这是通过在其清单定义中将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:"));
该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 解析”过程决定了哪个应用程序应该接收每个消息。这个过程考虑了 priority attribute,可以在 intent-filter declaration 中设置,优先级更高的将被选择。这个优先级可以设置在 -1000 到 1000 之间,应用程序可以使用 SYSTEM_HIGH_PRIORITY
值。如果出现 conflict,将出现一个“选择器”窗口,以便 用户可以决定。
Explicit Intents
一个显式 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
**,检查影响并尝试删除它们。
Deep links / URL schemes
在 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>
[...]
该示例中的方案是 examplescheme://
(还请注意 category BROWSABLE
)
然后,在数据字段中,您可以指定 host 和 path:
<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"/>
广播接收器
广播接收器充当消息系统中的监听器,允许多个应用程序响应来自系统的相同消息。应用程序可以通过应用的Manifest或在应用代码中通过**registerReceiver
** API以两种主要方式注册接收器。在Manifest中,广播通过权限进行过滤,而动态注册的接收器在注册时也可以指定权限。
意图过滤器在这两种注册方法中至关重要,决定哪些广播触发接收器。一旦发送匹配的广播,接收器的**onReceive
**方法将被调用,使应用能够相应地反应,例如在接收到低电量警报时调整行为。
广播可以是异步的,所有接收器无序接收,或同步的,接收器根据设定的优先级接收广播。然而,重要的是要注意潜在的安全风险,因为任何应用都可以优先处理自己以拦截广播。
要理解接收器的功能,请查找其类中的**onReceive
方法。该方法的代码可以操作接收到的Intent,强调接收器进行数据验证的必要性,特别是在有序广播**中,这可能会修改或丢弃Intent。
内容提供者
内容提供者对于应用之间共享结构化数据至关重要,强调实施权限以确保数据安全的重要性。它们允许应用访问来自各种来源的数据,包括数据库、文件系统或网络。特定权限,如**readPermission
和writePermission
,对于控制访问至关重要。此外,可以通过应用的Manifest中的grantUriPermission
**设置临时访问,利用path
、pathPrefix
和pathPattern
等属性进行详细的访问控制。
输入验证至关重要,以防止漏洞,例如SQL注入。内容提供者支持基本操作:insert()
、update()
、delete()
和query()
,促进应用之间的数据操作和共享。
FileProvider,一种专门的内容提供者,专注于安全地共享文件。它在应用的Manifest中定义,具有特定属性以控制对文件夹的访问,由android:exported
和android: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>
有关更多信息,请查看:
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)
) 限制对文件系统的访问,某些资产除外,确保它们仅用于非敏感内容。
其他应用组件和移动设备管理
应用程序的数字签名
- 数字签名 是 Android 应用的必需,确保它们在安装前是 真实作者。此过程使用证书进行应用识别,并必须在安装时由设备的包管理器进行验证。应用可以是 自签名或由外部 CA 认证,以防止未经授权的访问,并确保应用在传送到设备时保持未被篡改。
应用验证以增强安全性
- 从 Android 4.2 开始,名为 Verify Apps 的功能允许用户在安装前检查应用的安全性。此 验证过程 可以警告用户潜在有害的应用,甚至阻止特别恶意的应用安装,从而增强用户安全性。
移动设备管理 (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);
}
枚举和利用 AIDL / Binder 服务
Android Binder IPC 暴露了许多 系统和供应商提供的服务。当这些服务在没有适当权限检查的情况下被导出时,它们就成为了 攻击面(AIDL 层本身 不 执行访问控制)。
1. 发现正在运行的服务
# from an adb shell (USB or wireless)
service list # simple one-liner
am list services # identical output, ActivityManager wrapper
- Android应用程序的基本知识
- Android应用程序的架构
- Android应用程序的组件
- Android应用程序的生命周期
- Android应用程序的安全性
- Android应用程序的权限管理
- Android应用程序的存储机制
- Android应用程序的网络通信
- Android应用程序的调试与测试
- Android应用程序的反编译与分析
145 mtkconnmetrics: [com.mediatek.net.connectivity.IMtkIpConnectivityMetrics]
146 wifi : [android.net.wifi.IWifiManager]
- 索引(第一列)在运行时分配 – 不要 在重启后依赖它。
- Binder 名称(例如
mtkconnmetrics
)将被传递给service call
。 - 括号内的值是生成存根的完全限定 AIDL 接口。
2. 获取接口描述符(PING)
每个 Binder 存根自动实现 事务代码 0x5f4e5446
(1598968902
十进制,ASCII "_NTF")。
# "ping" the service
service call mtkconnmetrics 1 # 1 == decimal 1598968902 mod 2^32
一个有效的回复返回编码为UTF-16字符串的接口名称,封装在Parcel
中。
3. 调用事务
语法:service call <name> <code> [type value ...]
常见参数说明:
i32 <int>
– 有符号32位值i64 <long>
– 有符号64位值s16 <string>
– UTF-16字符串(Android 13+使用utf16
)
示例 – 在MediaTek手机上以uid 1启动网络监控:
service call mtkconnmetrics 8 i32 1
4. 暴力破解未知方法
当头文件不可用时,你可以迭代代码,直到错误从:
Result: Parcel(00000000 00000000) # "Not a data message"
到一个正常的 Parcel
响应或 SecurityException
。
for i in $(seq 1 50); do
printf "[+] %2d -> " $i
service call mtkconnmetrics $i 2>/dev/null | head -1
done
如果服务是 使用 proguard 编译的,则必须猜测映射 – 请参见下一步。
5. 通过 onTransact() 映射代码 ↔ 方法
反编译实现接口的 jar/odex(对于 AOSP 存根,请检查 /system/framework
;OEM 通常使用 /system_ext
或 /vendor
)。
搜索 Stub.onTransact()
– 它包含一个巨大的 switch(transactionCode)
:
case TRANSACTION_updateCtaAppStatus: // 5
data.enforceInterface(DESCRIPTOR);
int appId = data.readInt();
boolean ok = data.readInt() != 0;
updateCtaAppStatus(appId, ok);
reply.writeNoException();
return true;
现在原型和参数类型非常清晰。
6. 发现缺失的权限检查
实现(通常是一个内部的 Impl
类)负责授权:
private void updateCtaAppStatus(int uid, boolean status) {
if (!isPermissionAllowed()) {
throw new SecurityException("uid " + uid + " rejected");
}
/* privileged code */
}
缺乏这种逻辑或特权 UID 的白名单(例如 uid == 1000 /*system*/
)是一个漏洞指标。
案例研究 – MediaTek startMonitorProcessWithUid()
(事务8)在没有任何权限门的情况下完全执行 Netlink 消息,允许一个无特权的应用与内核的 Netfilter 模块交互并向系统日志发送垃圾信息。
7. 自动化评估
加速 Binder 侦察的工具/脚本:
- binderfs – 以每个服务节点的形式暴露
/dev/binderfs
binder-scanner.py
– 遍历 binder 表并打印 ACL- Frida 快捷方式:
Java.perform(()=>console.log(android.os.ServiceManager.listServices().toArray()))
参考文献
- Android Services 101 – Pentest Partners
- Android Developer Docs – AIDL
- Android Developer Docs – IBinder
- Understanding Binder, Talk @ Google
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。