CVE-2010-2553:Microsoft Cinepak Codec CVDecompress 函数堆溢出漏洞调试分析
0x01 前言
- 微软提供一个叫 Cinepak 的视频解码器,通过调用 iccvid.dll 这个动态链接库文件可以使用这个解码器;微软自带的 Windows Media Player(视频音频软件)通过调用 iccvid.dll 解析有漏洞的 RIFF 音频文件格式时会触发 CVE-2010-2553 这个漏洞
- 该漏洞的成因是由于 iccvid.dll 中的 CVDecompress 这个函数在解析漏洞文件时没有对 cvid 格式编码条中的 Chunk 数量做限制,导致连续复制数据到堆空间,最终超出,导致堆溢出
0x02 调试环境
- 虚拟机:Windows XP sp3,版本:11.0.8211(提取码:woi9)
- 调试工具:Windbg(Windbg32,Windbg64)、OD、IDA(提取码:v0pt)、C32Asm(16进制分析文件工具,提取码:4sj8)
- 漏洞程序:Windows Media Player(提取码:07n9)
- 实验样本:能触发漏洞的 .avi 格式的 POC 文件(提取码:5ilg)
》每台机器调试时的地址可能会不一样
0x03 关于 cvid 的格式
- cvid 是一种压缩格式,嵌入在 .avi 格式之中,在使用它这个格式时需要对其进行解压缩
- 下图就是 cvid 的格式,由格式头(Frame Header)和 body 组成,body 里面就是顺序排列的编码条,每一个编码条都包含一个编码条头(Strip Header)和多个 Chunk ID + 对应数据的 Cvid Chunk 组成的结构,这些结构按顺序排列在编码条头之后
- 这个就是样本中对应的 cvid 格式
Flag:00(1个字节)
Cvid 数据长度:00 00 68(3个字节)
编码帧宽度:01 60
编码帧高度:01 20
编码条数量:00 10
编码条 ID:10 00
编码条数据大小:00 10
顶部 X 坐标:00 00
顶部 Y 坐标:00 00
底部 X 坐标:00 60
底部 Y 坐标:01 60
Chunk ID:20 00
Chunk 数据大小:00 00
Chunk ID:11 00
Chunk 数据大小:00 10
Chunk 数据:AAAAAAAAAAAA(12个字节,0x10 - 4 的结果)
Chunk ID:11 00
Chunk 数据大小:00 10
Chunk 数据:AAAAAAAAAAAA
Chunk ID:11 00
Chunk 数据大小:00 10
Chunk 数据:AAAAAAAAAAAA
0x04 根据异常定位漏洞函数
- 打开 Windows Media Player 软件,用 Windbg 附加进程
- 开启堆页保护之后运行,之后将 POC 拖入 Windows Media Player 内,成功捕获异常,出问题的动态链接库就是 iccvid.dll,该链接库在 system32 中;因为开启了堆页保护,所以可以断定 rep 循环复制命令复制了超量的数据导致了堆溢出
- 使用 !Heap 列出程序使用的堆空间列表,查看 edi 的值,发现在第一个堆中;为什么查看 edi 的值呢,rep 指令将 esi 指向的数据复制到 edi 的地址之中,因为造成了堆溢出,所以此时 edi 指向的地址一定在堆中
- 之后使用 !Heap -a 0090000 查询堆中的堆块信息,发现 edi 所指向的地址在这一个堆块当中,0x00156e40 这个堆块就是有问题的堆块,该堆块的下面还有一个 0x15ce48 的堆块,继续溢出的话会对这个堆块造成影响
- 将 system32 中的 iccvid.dll 载入到 IDA 中,找到 0x73b722cc 这个地址,F5 翻译成伪 C 代码
- 找到了问题函数 memcpy,这个就是 CVE-2010-2553 漏洞触发的根源
0x05 根据样本调试分析漏洞成因
- 与以往的漏洞不太一样,该漏洞所调用的 memcpy 函数并不是一次性的将数据复制到堆中,而是循环调用该函数复制数据到堆中,直到堆中数据溢出
- OD 载入程序可以选择先打开程序(也可以使用 Windbg 或 Immunity Debugger 调试,根据个人喜好),再附加进程,就像这样,你也可以找到源程序再拖入 OD 运行,一个道理
- 附加完进程之后,这时下 0x73b722cc 这个异常断点是断不下来的,因为此时程序还没有加载 iccvid.dll 这个动态链接库(0x73b722cc地址是动态链接库里面的)
- 这里可以采用等程序异常终止以后再下这个断点,就像下面这样,此时已经加载了 iccvid 动态链接库并且可以下断点
- 如图所示,已经在异常处断下
- 在断点断下后,需要找到函数的入口,从函数入口调试有利于理解运行流程
- 如何找到函数的入口,很简单,可以查看栈中返回值
- 查看栈,找到一个返回值,使用 enter 键跳到这个地址
- 假如有些调试过程中这个地址可能不是想要寻找的函数地址,这时可以继续沿栈向下查找返回值,多试几次就可以了(当然,在栈中的数据完好不被恶意覆盖时可以这么做)
- 可以发现就是这个函数调用了 memcpy 将数据复制到栈上的
- 下断点之后重新运行
- 之后 F7 进入这个函数,来看看函数对样本数据做了哪些操作:
- 首先第一步>>>先对比 Cvid 数据长度是否大于 0x20,之后会将 Cvid 数据长度的第一个字节和第二个字节分别放入寄存器 eax 的高位和地位,我们将此时的 eax 中的数据暂且称为 result1
- 然后将 result1 和 Cvid 数据长度的第三个字节做积或运算,我们将运算的结果称为result2
- 如果 Cvid数据长度小于 result2 或者 UlongSub 函数的返回值小于 0 的话就进入 if 语句
- ULongSub 函数的功能是将第一个参数的值减去第二个参数的值赋到第三个参数的地址中,如果第一个参数大于第二个参数就返回负数,反之则返回 0
- 第二步>>>判断编码条的数量是否大于 0
- v11 存放的是 POC 数据的地址,v13 的值是 POC 地址偏移 0x8 个字节,刚好是编码条数量的地址
- 第三步>>>比较未解压缩数据的长度是否小于 0x16
- 此时未解压数据的大小为 0x5E,由 UlongSub 函数计算未解压数据的大小
- 第四步>>>取出编码条 ID 的第二个字节和编码条数据大小的第一个字节分别放入寄存器的高低字节,我们暂且将这个值称为 res1
- 之后 res1 和编码条数据大小的第二个字节(0x10)做积或运算,运算完的值称为 res2
- 最后判断未解压数据的大小是否小于 res2
- 第五步>>>判断编码条 ID 的第一个字节是否等于 0x10 或者 0x11
- v11 指向 POC 的地址,v14 是 v11 的地址加 10(0xA) 后地址的值,用于读取 POC 样本中的数据
- v14 指向的 POC 地址为编码条 ID 的地址 + 0x1值,也就是编码条的第二个字节 0x10
好像确实有点复杂,多调试几次就可以了
- 最后一步>>>首先判断引用计数是否不为 0,由此可知 do - while 循环第一次并没有执行 memcpy 函数,其次判断 Cvid 数据长度的第三个字节是否不为 0(BTYE3 函数取目标的第三个字节),最后判断 Chunk ID 的第一个字节是否等于 0x11(十进制 17)
- 在满足上述所有条件后才会执行 memcpy 函数
- 逻辑图如下图所示,只列出了相对重要的步骤
- do - while 循环结尾如下图所示,v28 是引用计数,v32 控制着 memcpy 函数的第一个和第二个参数的值
- 下面就是 memcpy 这个函数,v32 每次递增 0x2000;参数一和参数二由 v7 和 v32 控制,参数三也就是复制数据的大小为固定的 0x2000
- 所以按理说执行条件满足后执行 3 次就会造成堆溢出(上面 Windbg 调试出堆的大小为 6000,因为堆中还要存放其他数据,所以 3 次足够了),由于初次进入循环时引用计数为 0,所以并没有执行 memcpy 函数,所以加上这一次,在 do - while 执行到第四次循环的时候才真正的溢出
注意堆虽然会溢出,但是并不代表一定触发异常
0x06 漏洞是否可以利用
- 反观 memcpy 函数,发现只有 v7 的值相对可以被控制,下面是函数的追根溯源
- v7 = a1,a1 是 CVDecompress 的第一个参数
- CVDecompress 的第一个参数由 a1 控制
- a1 由 Decompress 的第一个参数传入
- 第一个参数由 v5 控制
- v5 又是 DriverProc 函数的 dwDriverIdentifier 参数传入,貌似传入的是一个地址
- 调用关系就是 dwDriverIdentifier() -> Decompress() -> CVDecompress() -> memcpy()
- 那么 dwDriverIdentifier 是怎么传进来的呢,答案是由源程序传入,并不是 iccvid.dll 中的数据,也就是说程序在调用 iccvid.dll 链接库时才将 dwDriverIdentifier 传进来,故漏洞利用的难度是非常大的
- 再往上追根溯源的话就已经超出了 iccvid.dll 动态链接库的范围了,与本文主题不符,故不做过多阐述
- 参考资料:0day安全:软件漏洞分析技术 + 漏洞战争
本次对于 CVE-2010-2553 堆溢出漏洞的分析到此结束,如有错误,欢迎指证
CVE-2010-2553:Microsoft Cinepak Codec CVDecompress 函数堆溢出漏洞调试分析的更多相关文章
- CVE-2012-0003:Microsoft Windows Media Player winmm.dll MIDI 文件堆溢出漏洞调试分析
0x01 蜘蛛漏洞攻击包 前言:2012 年 2月,地下黑产中流行着一款国产名为蜘蛛漏洞的攻击包 -- "Zhi-Zhu Exploit Pack",该工具包含 5 个漏洞,都是在 ...
- CVE-2018-0802:Microsoft office 公式编辑器 font name 字段二次溢出漏洞调试分析
\x01 前言 CVE-2018-0802 是继 CVE-2017-11882 发现的又一个关于 font name 字段的溢出漏洞,又称之为 "第二代噩梦公式",巧合的是两个漏洞 ...
- CVE-2013-0077:Microsoft DirectShow quartz.dll m2p 文件堆溢出漏洞简单分析
0x01 前言 2012 年 10 月 5 日,exploit-db 漏洞公布站点上发布了 QQplayer.exe 3.7.892 m2p quartz.dll Heap Pointer OverW ...
- CVE-2012-0158:Microsoft Office MSCOMCTL.ocx 栈溢出漏洞调试分析
0x01 Lotus Blossom 行动 在 2015 年 6 月,国外安全厂商 Palo Alto Networks 的威胁情报团队 Unit42 发现了一起针对东南亚政府的一次间谍行为,试图获取 ...
- Samba ‘dcerpc_read_ncacn_packet_done’函数缓冲区溢出漏洞
漏洞名称: Samba ‘dcerpc_read_ncacn_packet_done’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201312-169 发布时间: 2013-12-12 更新时间 ...
- Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞
漏洞名称: Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-423 发布时间: 2013-11-29 更新时间: 201 ...
- Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞
漏洞名称: Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-392 发布时间: 2013-11-29 更新 ...
- Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞
漏洞名称: Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-154 发布时间: 2013-11-13 更新时间: 201 ...
- Apache mod_fcgid fcgid_header_bucket_read函数缓冲区溢出漏洞
漏洞名称: Apache mod_fcgid fcgid_header_bucket_read函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201310-455 发布时间: 2013-10-21 ...
随机推荐
- C#中委托、匿名函数、Lambda表达式的一些个人理解
0x01定义一个委托,相当于定义一个可以存储方法的特殊变量类型 下面我们看具体的代码,通过代码更好理解 delegate void IntMethodInvoker(int x); 这行代码就是声明一 ...
- 「NOIP 2020」微信步数(计数)
「NOIP 2020」微信步数(Luogu P7116) 题意: 有一个 \(k\) 维场地,第 \(i\) 维宽为 \(w_i\),即第 \(i\) 维的合法坐标为 \(1, 2, \cdots, ...
- 2019 GDUT Rating Contest I : Problem G. Back and Forth
题面: G. Back and Forth Input file: standard input Output file: standard output Time limit: 1 second Mem ...
- 让你的浏览器变成Siri一样的语音助手
最近业余时间浏览技术文章的时候,看到了一篇关于语音朗读的文章:Use JavaScript to Make Your Browser Speak(用Javascript让你的浏览器说话),文章中提到可 ...
- Centos7 Firewall 使用笔记
在 Centos 7 中防火墙由 firewalld 来管理,而不是以前的 iptables. 记录一下常用操作备查 firewall-cmd 操作 firewall-cmd --state 查看防火 ...
- java例题_47 读取 7 个数(1—50)的整数值,每读取一个值,程序打印出该值个数的*
1 /*47 [程序 47 打印星号] 2 题目:读取 7 个数(1-50)的整数值,每读取一个值,程序打印出该值个数的*. 3 */ 4 5 /*分析 6 * 1.多次读取---for循环 7 * ...
- 单链表c语言实现的形式
包括初始化,创建,查询,长度,删除,清空,销毁等操作 代码如下: #include<stdio.h> #include<stdlib.h> //定义单链表的数据类型 typed ...
- JS基础学习第一天
JavaScript JavaScript负责页面中的的行为. 它是一门运行在浏览器端的脚本语言. JS的编写的位置 1.可以编写到标签的指定属性中 12 <button onclick=&qu ...
- Webpack的基本配置和打包与介绍
1. 前言 1.1 Webpack是什么 可能有很多的小伙伴对于这个Webpack既熟悉又陌生,有一些刚开始接触vue的小伙伴在对项目进行打包的时候经常会使用到npm run build来进行打包,但 ...
- 周爱民带你深入剖析JavaScript核心原理
作为前端工程师必备技能,JavaScript 的重要性不言而喻.虽然易上手,但却有着诸多复杂微妙的机制,想要真正掌握绝非易事. 专栏面向JavaScript语言的实际应用者与深度爱好者,以讲述Java ...