没有合适的资源?快使用搜索试试~ 我知道了~
Android安全攻防指南_用户态软件的漏洞利用_编程案例解析实例详解课程教程.pdf
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 17 浏览量
2023-05-05
14:50:50
上传
评论
收藏 918KB PDF 举报
温馨提示
试读
22页
本章主要介绍 Android 系统用户态软件中内存破坏漏洞的利用方法。我们会在 ARM 架构上讨论常见的漏洞类型,例如栈溢出。本章首先解释开发漏洞利用代码(exploit)中的相关实现细节;然后以一些曾经公开的 exploit 为例,帮助理解前面介绍的概念;最后以 WebKit 浏览器引擎中一个可远程利用的漏洞为例,介绍高级堆利用技术。
资源推荐
资源详情
资源评论
206 第8 章 用户态软件的漏洞利用
用户态软件的漏洞利用
本章主要介绍 Android 系统用户态软件中内存破坏漏洞的利用方法。我们会在 ARM 架构上
讨论常见的漏洞类型,例如栈溢出。本章首先解释开发漏洞利用代码(exploit)中的相关实现细
节;然后以一些曾经公开的 exploit 为例,帮助理解前面介绍的概念;最后以 WebKit 浏览器引擎
中一个可远程利用的漏洞为例,介绍高级堆利用技术。
8.1 内存破坏漏洞基础
要理解内存破坏漏洞的利用技术,最关键的是抽象。很重要的一点是,应当避免用 C 语言这
种高级语言的方式来思考问题。作为攻击者,只需要把目标机器的内存当作有限数量的内存单元,
由目标程序的语义进行操作。内存单元包括某些指令类型或者函数暗含的内存区域,例如栈和堆。
接下来的几节会讨论内存破坏漏洞的典型案例,以及它们在 Android 平台上是如何被利用的。
这些漏洞利用方法都有一个共同特点:攻击者利用目标代码破坏某些内存区域,使得目标程序转
入攻击者预期的状态。有的攻击方式比较直接,例如将原生代码的执行流转入攻击者所控制的内
存;有的则比较隐秘,攻击者可以让程序语义发生一些攻击者所设定的非预期变化(通常称之为
邪恶机器编程)。
用户空间中堆和栈上的利用技术有非常多的细节,也有很多高级方法。需要采用的技术取决
于漏洞本身,本章无法一一介绍。网上有不计其数的资源介绍与具体架构相关的细节,本章仅专
注于介绍 ARM 设备的 Android 系统中最常见的漏洞利用相关概念。
8.1.1 栈缓冲区溢出
就像其他体系架构中的应用程序二进制接口(ABI)一样,ARM 嵌入式 ABI(EABI)大量
使用了(特定于线程的)栈。下列是 ARM 使用的 ABI 规则:
函数的参数如果超过 4 个,超过的部分会使用栈来传递;
局部变量如果不能存储在寄存器中,则在当前栈帧中分配。特别是大于 32 比特的变量和
指针引用的变量;
非叶节点函数(Non-leaf Function)的返回地址存储在栈上,更多关于函数返回地址的细
节将在第 9 章中介绍。
8.1 内存破坏漏洞基础 207
1
2
3
4
5
13
6
7
8
9
10
11
12
如果一个函数中用到了栈,那么它通常会以 prologue 代码开始,以 epilogue 代码结束。prologue
代码用来初始栈帧,epilogue 代码则用来还原栈帧。prologue 代码把函数执行过程中会遭到破坏
的寄存器值保存在栈上;函数返回时,epilogue 代码就会恢复相应的寄存器值。prologue 代码也
会通过调整栈指针来为栈上的局部变量分配空间。栈空间从虚拟内存高地址往低地址方向增长,
所以栈指针指向的地址会在 prologue 代码中变低,在 epilogue 代码中变高。嵌套的函数调用会产
生如图 8-1 所示的栈帧结构。
图 8-1 多栈帧示例
注意,尽管在 Thumb 模式下有一些处理栈指针寄存器的特殊指令(push 和 pop),栈的本
质概念只是不同函数之间的 ABI 约定。栈指针寄存器也可以被用作其他目的。因此,在攻击者
看来,栈上分配的局部变量与其他内存并无本质区别。
如果漏洞与栈上的局部变量有关,就十分有关注的价值,因为其附近有与控制相关的数据:
已保存的函数返回地址。如图 8-1 所示,所有的局部变量都挨得很近,没有交错的控制数据。事
实上,这样的栈帧布局信息被隐式地编码在了编译器生成的原生代码中。
利用局部变量越界的漏洞,攻击者可以很容易地向其他局部变量或者控制数据写入想要的数
据。Aleph1 是第一位公开发表相关文章的人,这篇有较大影响的文章名为“Smashing the Stack for
Fun and Profit”(Phrack 第 49 期,文章 14,http://phrack.org/issues/49/14.html#article)。由于临时字符
串或数组经常被分配在栈上,所以这是一种很常见的漏洞类型。下面这段代码是一个简单的例子。
栈溢出代码样例
void getname() {
struct {
char name[32];
int age
} info;
info.age = 23;
208 第8 章 用户态软件的漏洞利用
printf("Please enter your name: ");
gets(info.name);
printf("Hello %s, I guess you are %u years old?!\n", info.name,
info.age);
}
众所周知,gets 函数不会进行边界检查。如果往标准输入 stdin 中输入超过 32 个字符,
程序行为就会出现异常。使用 GCC 4.7.1 加上选项
-mthumb –mcpu=cortex-a9 –O2,生成的汇
编代码如下:
栈溢出代码样例反汇编
00000000 <getname>:
0: f240 0000 movw r0, #0
↓ 在栈上保存返回地址
4: b500 push {lr}
6: 2317 movs r3, #23
↓ 在栈上为局部变量预留空间
8: b08b sub sp, #44
a: f2c0 0000 movt r0, #0
↓ 初始化变量 age 为固定值 23,之前已经将 r3 赋为 23
e: 9301 str r3, [sp, #36]
10: f7ff fffe bl 0 <printf>
↓ 计算缓冲区地址,作为 gets 函数的第一个参数
14: a802 add r0, sp, #4
16: f7ff fffe bl 0 <gets>
1a: f240 0000 movw r0, #0
↓ 载入局部变量 age,用于打印
1e: 9a01 ldr r2, [sp, #36]
8.1 内存破坏漏洞基础 209
1
2
3
4
5
13
6
7
8
9
10
11
12
↓ 再次计算缓冲区地址,用于打印
20: a902 add r1, sp, #4
22: f2c0 0000 movt r0, #0
26: f7ff fffe bl 0 <printf>
2a: b00b add sp, #44
↓ 从栈上载入返回地址并返回
2c: bd00 pop {pc}
正如前面所说,函数代码定义了栈帧布局,或者说是定义了相对 SP 寄存器的偏移。栈布局
如图 8-2 所示。
图 8-2 栈帧布局样例
当攻击者输入超过 32 字节的数据时,33 到 36 字节会覆盖 age 这个局部变量,37 到 40 字节
会覆盖栈上保存的返回地址。所以攻击者可以把程序执行流重定向到任意地址上,或者修改一个
原本不能修改的局部变量。
由于这种漏洞类型频繁出现,GNU C 编译器实现了一种缓解措施,自从 Android 第一个版本
发布就默认开启了,具体请参考 12.7 节。即便有了栈 cookie 这个缓解措施,依然可以使用一些
与漏洞有关的技术来对程序发起攻击,例如马上介绍的 zergRush 漏洞利用案例中使用的技巧。
尽管存在缓解措施,本章中标准的栈缓冲区溢出依然是一个用来介绍内存破坏漏洞的好例子。
8.1.2 堆的漏洞利用
生存域超过一个函数范围的非局部对象必须分配在堆上。堆上的数组和字符串与栈上的情况
相同,也会面临越界的问题。除了数据本身,堆上分配的每一个对象内还有控制元数据。与栈上
210 第8 章 用户态软件的漏洞利用
的局部变量不同,堆分配变量的生命周期并非由编译器自动管理。这两个原因使堆上的漏洞变得
容易利用,攻击者可以利用的漏洞也就更多了。
1. 释放后重用问题
释放后重用问题是指,应用程序使用指针访问了一个已经通过
free 函数或 delete 操作符
释放过的对象。在复杂的软件中,这是一个很常见的 bug,并且很难通过人工源代码审计发现。
因为
delete 操作符内部也依赖于 free 函数释放内存,所以对 delete 和 free 不作区分。
大部分堆分配器在释放一块分配过的内存时不会修改其内容,原先使用时的内存数据会保持
不变。很多分配器会在释放内存块最开始的地方存储一些控制信息,但是大多数内存块内的数据
依然保持原样。释放后的内存被使用时,会产生不同的情况。
被释放的内存还没有被用作新的内存分配 释放后的内存被使用时,它们的内容与释放前
的相同。在这种情况下,bug 可能不会表现出来。但是有些时候,析构器可能会让对象的
内容失效,访问时就可能会造成程序崩溃。除此之外,这种情况还可能会导致信息泄露,
攻击者可以因此获得敏感内存数据。
被释放的内存已经被全部或部分用作新的内存分配 在这种情况下,两个语义上完全不同
的指针指向同一内存地址,如果两种语义下的代码互相冲突,就会导致程序崩溃。例如,
一个函数可能会在分配的内存中写入数据,而这些数据在另一个函数中则被用作内存地
址。如图 8-3 所示。
图 8-3 堆上的释放后重用示意
如果没有被其他的内存分配使用,被释放的内存块就没有多大用处了(除非可以让程序再一
次释放这块内存)。如果能通过精心构造应用的输入来产生大小相同的内存块分配,释放点就可
以正好被新的分配所用。不过这种方法与特定的堆分配器实现相关。
2. 自定义分配器
大多数开发者都认为堆分配器是操作系统的一部分,但事实并非如此。操作系统只提供了页
分配(大小通常是 4KB)的机制,堆分配器负责将页划分成想要分配的大小。大多数人使用的是
C 运行时库(libc)中的堆分配器,应用程序完全可以使用基于操作系统页分配机制的其他分配
器。事实上,大部分桌面浏览器出于性能的考虑而采取了该做法。
剩余21页未读,继续阅读
资源评论
好知识传播者
- 粉丝: 490
- 资源: 4204
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功