安卓系统特权提升攻击深度剖析
1. 安卓系统架构与安全机制概述
安卓系统的架构分为底层服务层和中间件层。底层服务层为系统的其他部分提供诸如网络、存储、内存和处理等基础服务。中间件层则由原生安卓库(用 C/C++ 编写)、优化版的 Java 虚拟机 Dalvik 虚拟机(DVM)以及用 Java 编写的核心库组成。DVM 负责执行高层应用的二进制文件。安卓应用使用 Java 编写,由多个独立的组件构成,这些组件可以通过安卓中间件提供的 Binder 机制进行组件间通信(ICC)。
由于安卓应用使用 Java 编写,隐式边界检查使其基本能抵御标准的缓冲区溢出攻击。然而,Java 应用可通过 Java 本地接口(JNI)访问 C/C++ 代码库。开发者可能出于性能等原因,使用 JNI 将自己的 C/C++ 库集成到程序代码中。而且,许多 C 库默认被映射到程序内存空间的固定内存地址。因此,引入 C/C++ 库后,Java 编程语言提供的安全保障就不再适用。
安卓系统具备多种安全机制,具体如下:
-
自主访问控制(DAC)
:继承自 Linux,通过进程所有权来控制对文件的访问。每个运行的进程被分配一个用户 ID,每个文件都有针对用户、组和其他用户的访问规则,这些主体集可能拥有读取、写入和执行文件的权限。
-
沙盒机制
:将应用彼此隔离,并与系统资源隔离。系统文件由“系统”或“根”用户拥有,其他应用有自己的唯一标识符。这样,应用只能访问自己拥有的文件或其他应用明确标记为可读/可写/可执行的文件。
-
权限机制
:由安卓中间件层提供,参考监视器对 ICC 调用执行强制访问控制(MAC)。安全敏感接口受标准安卓权限保护,如拨打电话、访问互联网、发送短信等,应用必须具备这些权限才能执行相应操作。此外,应用还可以声明自定义权限标签来限制对自身接口的访问。所需权限在清单文件中明确指定,并在安装时根据对声明这些权限的应用签名的检查和用户确认进行批准。
-
组件封装
:应用组件可以指定为公共或私有。私有组件只能由同一应用内的组件访问;声明为公共的组件也可被其他应用访问,但完全访问可能需要调用应用具备指定的权限。
-
应用签名
:安卓使用加密签名来验证应用的来源并建立它们之间的信任关系。开发者必须对应用代码进行签名,这允许启用基于签名的权限,或允许来自同一来源(即由同一开发者签名)的应用共享相同的用户 ID。签名密钥的证书可以自签名,无需由认证机构颁发,证书包含在应用安装包中,以便在安装时验证开发者的签名。
2. 安卓权限机制的安全缺陷与特权提升攻击
安卓权限机制存在安全缺陷,可能导致受损应用发起特权提升攻击。具体问题在于,权限较少的应用(非特权调用者)可以不受限制地访问权限更高的应用(特权被调用者)的组件,即安卓的安全架构不能确保调用者至少拥有与被调用者相同的权限。
以下通过一个示例来说明特权提升攻击的可能性:
| 应用 | 授予权限 | 组件 | 组件权限保护情况 |
| ---- | ---- | ---- | ---- |
| A | 无 | CA1、CA2 | - |
| B | p1 | CB1、CB2 | 无权限标签保护,可访问受 p1 保护的外部应用组件 |
| C | -、p2、p1 | CC1、CC2 | CC1 受 p1 保护,CC2 受 p2 保护 |
从上述表格可以看出,组件 CA1 由于应用 A 未被授予 p1 权限,无法直接访问组件 CC1。但数据可以通过组件 CB1 间接从 CA1 到达 CC1。因为 CB1 不受权限标签保护,可被 CA1 访问,且应用 B 及其组件被授予 p1 权限,所以 CB1 可以访问 CC1。
为防止此类攻击,应用 B 必须对权限进行额外检查,确保调用 CB1 组件的应用被授予 p1 权限。通常可以通过在组件代码中包含参考监视器钩子来实现,但这个任务被委托给应用开发者,而非由系统集中执行。由于开发者通常并非安全专家,他们的应用可能无法实现必要的权限检查。
3. 特权提升攻击示例
下面介绍一个基于安卓核心应用 Phone 的特权提升攻击示例。Phone 应用曾存在一个未受保护的组件,为其他应用提供了拨打电话的接口。将此攻击示例与前面的架构对应,Phone 应用可视为应用 B,受系统权限 PHONE CALLS 保护的系统接口可表示为应用 C,任何非特权应用(如 Activity Manager)可充当应用 A。
当 Activity Manager 从 shell 控制台调用以下命令时:
am start -a android.intent.action.CALL tel:1234
手机会拨打指定号码。在这个例子中,非特权应用 Activity Manager 能够进行未经授权的电话呼叫。
4. 攻击场景与假设
在我们设定的攻击场景中,用户从互联网下载一个非恶意但存在漏洞的应用,例如一个存在内存错误(如缓冲区溢出漏洞)的游戏。安装过程中,用户授予游戏访问互联网的权限,例如用于与朋友分享高分。攻击者的目标是在用户保存游戏状态时,通过 SMS 向指定的 premium - rate 号码发送短信。为实现这一目标,攻击者利用应用的漏洞进行特权提升攻击,以获取发送短信的权限。
我们做出以下假设:
- 受害者的设备未越狱,但安装了 Android 脚本环境(ASE)应用 v2.0(ase r20),其中包含 Tcl 脚本解释器。ASE 虽不是核心安卓应用,但由 Google 开发者开发,可从 ASE 主页免费下载,很多平台可能会安装它。
- 用户安装的应用存在堆溢出漏洞,利用堆溢出漏洞是当今攻击者常用的攻击手段。
- 用户为该易受攻击的应用分配了访问互联网的权限,因为超过 60% 的安卓应用都需要此权限。
5. Android 脚本环境(ASE)分析
ASE 应用为安卓平台引入了高级脚本语言,以实现快速开发。它提供了多种脚本语言的解释器,如 BeanShell、Tcl、JRuby、Lua、Perl、Python 和 Rhino。ASE 拥有发送消息、拨打电话、读取联系人、访问蓝牙和相机等多种权限。
ASE 是一个客户端 - 服务器应用。服务器部分负责命令的解释和执行,客户端只是一个前端,通过套接字连接与服务器通信以传递 shell 命令。客户端实现为可执行文件,任何应用都可以执行它。当客户端被调用时,其进程被分配与调用应用相同的用户 ID,从而自动继承其权限。由于客户端需要与 ASE 服务器建立套接字连接,调用应用必须被分配互联网权限,否则连接将失败。
ASE 服务器部分作为应用组件实现,其漏洞在于对该组件的访问没有任何权限保护。无特权的应用可以无限制地访问服务器并传递任意 shell 命令执行。ASE 服务器未能执行额外的安全检查,以确保调用应用具备执行请求操作的适当权限。因此,任何恶意或受损的应用都可以滥用 ASE 应用执行各种未经授权的操作,如拨打电话、发送短信、追踪手机位置等。
在实验中,我们尝试使用 Perl、Lua、Python 和 Tcl 脚本语言进行攻击。结果表明,这些语言对应的客户端可执行文件对所有用户都有执行权限。但与 Tcl 不同,Perl、Lua 和 Python 脚本语言还使用了只有 ASE 应用本身才能访问的库。这意味着在不额外操作这些库的访问权限的情况下,除 ASE 外的其他应用无法调用它们。因此,我们的特权提升攻击选择使用 Tcl 脚本解释器。
mermaid 流程图展示 ASE 工作流程:
graph LR
A[调用应用] -->|传递命令| B[ASE 客户端]
B -->|套接字连接| C[ASE 服务器]
C -->|执行命令| D[操作执行]
6. 攻击技术 - 无返回的面向返回编程(ROP)
为实施特权提升攻击,我们利用了存在堆溢出漏洞的应用,并采用了无返回的面向返回编程(ROP)技术。这种技术允许攻击者通过链接系统库中的各种小指令序列来诱导任意程序行为。与传统的 ROP 相比,无返回的 ROP 绕过了返回地址检查器,因为它依赖于间接跳转而非返回。而且,它不能被诸如 W⊕X(可写异或可执行)的内存保护方案阻止,该方案通过将内存页面标记为可写或可执行来防止代码注入攻击。
无返回的 ROP 攻击的基本步骤如下:
1. 攻击者利用特定程序的缓冲区溢出漏洞,溢出本地缓冲区并覆盖相邻控制流信息。
2. 攻击者注入多个跳转地址,程序执行被重定向到链接库中跳转地址 1 处的指令序列。
3. 执行链接库中的指令序列,直到遇到跳转指令,通过跳板将执行重定向到下一个指令序列。
4. 跳板负责从控制结构部分加载下一个指令序列的地址,并将执行重定向到该序列。
5. 重复上述过程,直到攻击者终止程序。
对于 ARM 架构,攻击使用 BLX(分支 - 链接 - 交换)指令作为跳转指令。BLX 在普通程序中用于间接子例程调用,它强制分支到特定寄存器中存储的跳转地址,同时将返回地址加载到链接寄存器 lr 中。必要时,它还能实现 ARM 指令集和 THUMB 指令集的交换。
mermaid 流程图展示无返回 ROP 攻击流程:
graph LR
A[攻击者] -->|溢出缓冲区| B[覆盖控制流信息]
B -->|注入跳转地址| C[重定向执行到指令序列 1]
C -->|执行指令序列| D[遇到跳转指令]
D -->|通过跳板| E[重定向到指令序列 2]
E -->|重复执行| F[攻击者终止程序]
安卓系统特权提升攻击深度剖析
7. 攻击实现
我们在安卓平台 2.0 的设备模拟器以及搭载安卓平台 1.6 的真实设备(Android Dev Phone 2)上都进行了攻击测试,下面详细介绍基于模拟器版本的攻击实现。
7.1 易受攻击的应用
我们的易受攻击应用是一个标准的 Java 应用,通过 JNI 引入了包含 C/C++ 代码的本地库。以下是包含的 C/C++ 代码:
struct foo {
char buffer[460];
jmp_buf jb;
};
jint Java_com_example_hellojni_HelloJni_doMapFile(JNIEnv* env, jobject thiz) {
// 打开一个二进制文件(未展示)
...
struct foo *f = malloc(sizeof *f);
i = setjmp(f->jb);
if (i != 0)
return 0;
fgets(f->buffer, sb.stsize, sFile);
longjmp(f->jb, 2);
}
此应用存在 setjmp 漏洞。通常,setjmp 和 longjmp 是允许非本地控制转移的系统调用。setjmp 会创建一个特殊的数据结构(jmp_buf),调用 setjmp 时,寄存器 r4 到 r14 的值会存储在 jmp_buf 中。当调用 longjmp 时,r4 到 r14 寄存器会恢复为 jmp_buf 结构中存储的值。如果攻击者能在 longjmp 调用之前覆盖 jmp_buf 结构,就能将控制转移到自己选择的代码,而不会破坏任何返回地址。
fgets 函数会将一个名为 binary 的文件中的数据插入到缓冲区(位于 foo 结构中),且不进行边界检查。由于 foo 结构还包含 jmp_buf 结构,大于 460 字节的二进制文件会覆盖相邻的 jmp_buf 结构内容。
不过,我们的实验发现安卓为 setjmp 缓冲区启用了堆保护,通过在 libc.so 中硬编码一个固定的金丝雀值,并在金丝雀和 jmp_buf 之间留出 52 字节的空间。这个金丝雀值与设备和进程无关,所以在攻击时需要考虑金丝雀值以及这 52 字节的空间。
7.2 攻击工作流程
攻击工作流程如下:
1. 当通过 JNI 调用本地库代码时,利用堆溢出的 setjmp 漏洞,发起无返回的 ROP 攻击。
2. 使用多个小工具调用 Tcl 客户端,并发送一个发送 50 条短信的命令。
3. 以易受攻击应用身份运行的 Tcl 客户端与 ASE Tcl 服务器组件建立套接字连接。
4. Tcl 客户端与 ASE Tcl 服务器通信并传递 Tcl 命令以执行。由于 Tcl 服务器不检查 Tcl 客户端的权限,攻击者能够发送短信,尽管易受攻击的 Java 应用从未被授权这样做。
以下是 mermaid 流程图展示攻击工作流程:
graph LR
A[易受攻击应用] -->|利用漏洞发起 ROP 攻击| B[调用 Tcl 客户端]
B -->|套接字连接| C[ASE Tcl 服务器]
C -->|执行命令| D[发送短信]
7.3 解释器命令
为了执行攻击,小工具链需要将执行重定向到标准的 libc 系统函数,以调用 Tcl 客户端可执行文件 tclsh,从而执行 Tcl 命令。但我们发现需要设置一个 ASE 特定的环境变量 AP_PORT 才能让 Tcl 解释器正常工作。因此,系统函数的参数实际上包含两个 shell 命令:一是设置 AP_PORT 环境变量,二是调用 Tcl 解释器并发送 50 条短信到目标电话号码 5556(另一个安卓实例)。整个系统函数的参数如下:
export AP_PORT='50090';
echo -e 'package require android\n
set android [android new]\n
set num "\'5556\'" \n
set message "Test" \n
for {set x 0} {$x < 50} {incr x} {$android sendTextMessage $num $message }' | /data/data/com.google.ase/tclsh/tclsh
7.4 使用的指令序列
我们将上述解释器命令作为参数提供给 libc 系统函数,通过无返回的 ROP 调用系统函数。所有使用的指令序列和相应的攻击步骤如下表所示:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 攻击者将解释器命令和必要的跳转地址注入应用的内存空间 |
| 2 | 初始化寄存器 r6(使其指向第一个跳转地址),并将执行重定向到序列 1 |
| 3 | 序列 1 的指令 1 将跳板地址从栈加载到 r3,指令 2 强制跳转到跳板序列 |
| 4 | 跳板序列更新 r6,将第二个跳转地址加载到 r5,然后分支到序列 2 |
| 5 | 序列 2 终止后,跳板序列将执行重定向到序列 3 |
为调用系统函数,我们需要完成以下操作:
1. 将跳转地址和解释器命令注入应用的内存空间。
2. 初始化寄存器 r6(跳板序列)。
3. 将我们的跳板序列地址加载到 r3(序列 1)。
4. 将解释器命令的地址加载到 r0(序列 2)。
5. 最终调用 libc 系统函数(序列 3)。
综上所述,安卓系统的权限机制存在安全漏洞,可能导致特权提升攻击。攻击者可以利用应用的漏洞,结合如 ASE 应用的缺陷和无返回的 ROP 技术,实现未经授权的操作。开发者和用户需要加强安全意识,采取相应的措施来防范此类攻击,如严格检查应用权限、及时修复应用漏洞等。



被折叠的 条评论
为什么被折叠?



