CVE-2012-0003漏洞分析

本文为看雪论坛优秀文章

看雪论坛作者ID: 21Gun5

一、漏洞描述

二、测试环境

三、漏洞复现

四、准备工作

4.1 提取样本

4.2 MIDI格式

4.3 堆调试技巧

五、漏洞成因

六、漏洞利用

七、参考文献

一、漏洞描述

漏洞战争: 微软的多媒体库 winmm.dll 在处理 MIDI 文件时,由于对数据处理不当导致的堆溢出,攻击者可在网页中嵌入恶意构造的 MIDI 文件来远程执行任意代码。

https://cve.mitre.org: Unspecified vulnerability in winmm.dll in Windows Multimedia Library in Windows Media Player (WMP) in Microsoft Windows XP SP2 and SP3, Server 2003 SP2, Vista SP2, and Server 2008 SP2 allows remote attackers to execute arbitrary code via a crafted MIDI file, aka "MIDI Remote Code Execution Vulnerability."

二、测试环境

目标系统: Windows XP SP3(虚拟机)

OllyDebug: 动态调试

IDA Pro: 静态分析

IExplore 6、Media Player: 漏洞软件

Metasploit: 漏洞复现、利用

辅助工具: wget-保存mid文件到本地、gflags.exe-开启/关闭hpa

三、漏洞复现

1. msf生成恶意文件

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

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

3. msf中也得到相关记录

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

四、准备工作

> > > >

4.1 提取样本

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

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

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

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

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

5. 通过--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

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

> > > >

4.2 MIDI格式

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

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

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

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

> > > >

4.3 堆调试技巧

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

htc:堆尾检查,在堆块末尾附加额外的标记信息(通常为 8 字节),用于检査堆块是否发生溢出。

hfc: 堆释放检查,在释放堆块时对堆进行各种检査,防止多次释放同一个堆块。

hpc: 堆参数检査,对传递给堆管理的参数进行更多的检查。

ust: 用户态栈回湖,即将每次调用堆函数的函数调用信息记录到一个数据库中。

htg: 堆标志,为堆块增加附加标记,以记录堆块的使用情况或其他信息。

hvc: 调用时验证,即每次调用堆函数时都对整个堆进行验证和检査

hpa: 启用页堆,在堆块后增加专门用于检测溢出的栅栏页,若发生堆溢出触及栅栏页便会立刻触发异常

堆尾检查:

在每个堆块的尾部添加 8 字节内容,若该数据被破坏就说明可能存在堆溢出。

要检测尾部数据是否被破坏,还需要开启堆参数检查hpc 或堆释放检查hfe

用调试器直接打开目标进程,而非附加方式(附加方式无法在堆尾添加额外信息,也就无法进行堆尾检查)

开启堆尾检查和堆参数检査: !gflag +htc +hpc

页堆机制:

调试漏洞时,需要定位到导致漏洞的代码

堆尾检査方式主要是堆被破坏后的场景,不利于定位导致漏洞的代码。

开启页堆机制后,堆管理器会在堆块中增加不可访问的栅栏页,当溢出覆盖到栅栏页时就立即触发异常

五、漏洞成因

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

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

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

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

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

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

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

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

最终设置的断点如下:

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

8. 观察各变量,寻找规律

参数a1=v1不变、v2不变

v9递增,增到0x44重新开始

v11=v13

v21等于v11/v13最低位

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

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

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

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

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

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

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

v1来自参数a1: v1 = wParam;

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

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

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

mesOpen内部又调用了winmmAlloc函数:

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

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

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

HeapAlloc函数申请0x400字节的堆空间,将堆基址赋值给esi

程序在处理”打开音符“事件时,计算得到偏移0x419

偏移0x419 > 堆空间大小0x400,最终导致堆块的越界访问,产生漏洞

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

六、漏洞利用

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

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

2. 第二段js代码分析:

创建一个 select 元素,并设置 56 个属性:只有 w0 为 String 类型,其余为 Object 类型;

创建一个1000大小的数组 clones

调用feng_shui函数: cloneNode 循环复制selob到数组 clones 中、间隔地释放 clones 数组中的元素

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

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

分配: CAttrArray::Clone函数:

释放: CImplAry::Delete函数

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

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

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

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

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

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

11. 第三段js代码分析

调用trigger函数

遍历数组元素,若其属性不是String(即Object),则执行clones[k].w0('come on!');

此语句会调用 CAttrValue::GetIntoVariant 函数

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

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

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

    七、参考文献

《漏洞战争》

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

看雪ID: 21Gun5

https://bbs.pediy.com/user-868592.htm  

*本文由看雪论坛 21Gun5  原创,转载请注明来自看雪社区。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章