2011最新电影 doc 用什么软件读取 20...

对Cve的利用分析
早就听说duqu是第二代的stuxnet,心中就保留了一份好奇。之前对stuxnet的分析不仅可以看到作者在挖掘上的功力,同时也对作者那些精巧的利用思路由衷敬佩。而且,据说这个内核漏洞是以doc形式来触发的,心中更是想仔细跟一下。搜了一下,找到两篇比较好的文章启明星辰的分析,以及kk牛的《Analysing CVE- Font Parasing Vulnerability》。这两篇资料,对我的分析起到了很大的帮助。
一、漏洞原理:
在&启文&和KK的文章中,已经把漏洞原理说得比较清楚了,这里就是对他们所描述原理的理解、抄袭。
Win32k在处理字体的时候,需要计算出两个重要的变量:ulBitmapOffset、ulWorkMemSize。如果计算出来的ulBitmapOffset&ulWorkMemSize则会导致问题。因为,sfac_GetSbitBitmap函数会对ulBitmapOffset+0xA48位置的数据进行了修改,导致修改了后面结构fnt_GlobalGraphicStateType中的数据。而ulBitmapOffset+0xA48恰好指向fnt_GlobalGraphicStateType中的cvtcount成员。将修改cvtcount的值改得比较大以后,将从而使得解析引擎RCVT和WCVT函数在后面读和写ControlValueTable的时候越界读写。
Win32k!sfac_GetSbitBitmap
bf988bab 8d2424 lea esp,[esp]
bf988bae 6685c0 test ax,ax
bf988bb1 8b5528 mov edx,dword ptr [ebp+28h]
bf988bb4 8d3413 lea esi,[ebx+edx]
bf988bb7 760e jbe win32k!sfac_GetSbitBitmap+0x109 (bf988bc7)
bf988bb9 0fb7f8 movzx edi,ax
bf988bbc 8bff mov edi,edi
bf988bbe 8a11 mov dl,byte ptr [ecx]
bf988bc0 0816 or byte ptr [esi],dl
由于字体解析的时候ulBitmapOffset越界,sfac_GetSbitBitmap函数修改了Bitmap后面相邻结构中的变量(fnt_GlobalGraphicStateType中的cvtcount),导致cvtcount变大,从而使得解析引擎RCVT和WCVT函数在后面读和写ControlValueTable的时候越界读写。而在ControlValueTable后面紧跟的是fnt_GlobalGraphicStateType结构(本例原始cvtcount=1,因此fnt_GlobalGraphicStateType在controlValueTable偏移4的位置)。
利用时,通过精心构造字体文件0x3BAF2处存放的字体的指令及操作数,可以控制字体的执行流程。通过RCVT函数依靠cvtcount过大的错误将指向shellcode的指针取出来,再通过WCVT函数将其写入fnt_GlobalGraphicStateType偏移0xAC的位置,再通过itrp_LSW函数产生对fnt_GlobalGraphicStateType偏移0xAC处的调用,从而达到执行任意代码的目的。
二、对溢出过程的调试
对itrp_InnerExecute的调试:
按照&启文&的说法itrp_InnerExecute是字体引擎的核心,会不断会读取instruments的值,然后调用具体的函数。在字体文件中包含的Instruments表(该表保存着解析该字体的时候需要调用的解析函数的index值以及参数值。)位于文件偏移的0x3BAF2处。
&启文&中还对itrp_InnerExecute和itrp_PUSHB1进行了注解说明。读者可以参阅&启文&的说明自己相关代码。代码逻辑并不复杂,在相关提示下很容易就能看得懂。
另外要注意的是,字体虚拟机所使用的内存栈和我们熟悉的栈有所区别:
1、在字体虚拟机中,栈指针指向当前栈顶数据的下一个内存单元,也就是栈指针总是比当前栈顶元素地址大4。
2、在字体虚拟机中,栈是向高地址生长的。
在有上面的基础后,执行ba e1 itrp_InnerExecute,可以看到&启文&中所说的函数调用序列。
下面是常用的函数及其功能列表
Itrp_PUSHB1
向栈内压入一个字节(该参数存储在该index 值后面),同时栈指针加4
Itrp_PUSHW1
向栈内压入一个字,栈指针加4
以栈中的参数作为storeindex读取storage存储的一个临时变量,并存储在栈中storeindex的位置
将数据从storage读入栈中,用于存一些临时变量
1(该参数作为storeindex,该函数会以GlobalGS.storeaddress为基址,并通过index作索引读取存储的临时变量)
以当前栈倒数第二个压入得数值作storeindex,将当前栈最后一个压入的参数赋值给storage+storeindex*4的地方,栈指针-8
将栈中数据取出,存到storage中。用于存一些临时变量
以栈中最后一个压入的参数为cvtIndex,执行检测操作,如果cvtIndex&=cvtcount且小于255或者cvtIndex小于cvtcount的时候,从controlValueTable取数值,放入栈中之前存放cvtIndex的位置。栈指针不变。
将数据从controlValueTable读入栈中
该函数还将调用itrp_GetCVTEntryFast,从pfnt_GlobalGraphicStateType+0x8读取指针pControlValueTable,该指针指向ControlValueTable。ControlValueTable结构紧邻在fnt_GlobalGraphicStateType前面
1(cvtIndex)
Itrp_FLIPON
修改autoflip的值为1,修改FLIP标记
Itrp_FLIPOFF
修改autoflip的值为0
当前栈倒数第二个压入的值加上当前栈最后压入的数值,将其和放入当前栈倒数第二个压入的值的位置,栈指针-4
当前栈倒数第二个压入的值减去当前栈最后压入的数值,将其差放入当前栈倒数第二个压入的值的位置,栈指针-4
当前栈倒数第二个压入的值和当前栈最后压入的值作交换,栈指针不变
当前栈-4位置的数据复制到当前栈的位置,栈指针加4
有条件跳转,这里的跳转是指在instruments列表中跳转。该函数判断栈中最后压入的参数是否为0,如果为0,则不跳转;如果不为0,则以栈中倒数第二个参数为跳转的offset,并跳转到当前instruments指针+offset-1的instruments地址上。栈-8
在instruments列表上跳转
相当于无条件跳转,以当前栈中最后压入的参数为offset,跳转到当前instruments指针+offset-1的instruments地址上。栈-4
比较两个数据的大小,如果栈中最后压入的参数大于栈中倒数第二个压入的参数,则返回1,否则返回0。并将结果放到倒数第二个参数的位置。栈-4
检测当前栈中最后压入的数据,如果为0的话,则返回1,并存储在栈中最后压入的数据的地址处。
对应于itrp_RCVT,也就是写controlValueTable数据。该函数使用栈中倒数第二个参数作为cvtindex值,将倒数第一个参数作为数据存储在controlValueTable+cvtindex*4的地址中
将数据从栈中写入到trolValueTable
关键是要使用call dword ptr [eax+0ACh],使其执行到shellcode
调试过程简化:
为简化调试过程,可对原代码进行修改。
在即将使用dexter.tff字体,调用TextOut输出文字时,弹出messagebox。
执行code.exe程序,在弹出提示框之后,执行断点ba e1 BF9886AF,点击提示窗口的确定按钮让程序继续执行。然后执行指令g;db eax+1 l1观察此时eax +1的值。
当取值为0xFF时,可以对Win32k!sfac_GetSbitBitmap下断点,观察对ulBitmapOffset的计算。在此之后,程序将马上转入对itrp_InnerExecute的调用。因此,通过对win32k! itrp_InnerExecute下执行断点,可以观察win32k的字体引擎的调用过程。
bf85c471 mov dword ptr [win32k!LocalGS+0x74 (bf9a9284)],eax ds:0023:bf9abb3
bf85c476 jae win32k!itrp_InnerExecute+0x42 (bf85c4a4)
bf85c478 mov ecx,dword ptr [win32k!LocalGS+0x80 (bf9a9290)]
bf85c47e movzx edx,byte ptr [eax]
bf85c481 dec ecx
bf85c482 mov dword ptr [win32k!LocalGS+0x80 (bf9a9290)],ecx
bf85c488 je win32k!itrp_InnerExecute+0x38 (bf85c49a)
bf85c48a lea ecx,[eax+1]
bf85c48d call dword ptr win32k!function (bf9a4480)[edx*4]
执行命令:kd& db eax
02fcbaf2 b0 00 b0 00 42 4e b0 00-43 45 4d b0 00 43 45 61 ....BN..CEM..CEa
02fcbb02 b0 17 23 78 b0 00 43 b0-01 60 20 b0 00 23 42 b0 ..#x..C..` ..#B.
02fcbb12 50 61 b8 ff df 23 78 b0-80 1c b0 00 43 20 b0 01 Pa...#x.....C ..
通过对比原始dexter.ttf可以看到instrument数据从文件的0x3BAF2开始。
虚拟机执行记录:
执行完后的栈分布情况
Itrp_PUSHB1
Itrp_PUSHB1
storage=e22adf00,将栈中数据0取出,存到storage中
修改autoflip标志位为0
Itrp_PUSHB1
以刚压入0作为storeindex取出WS存储的数据
读取controlValueTable偏移为0处的数据
修改autoflip标志位为1
以刚压入0作为storeindex取出storage中存储的数据
读取controlValueTable偏移为0处的数据
对结果没有影响
压入参数17
栈顶的两个元素交换
不跳转,通过对比函数返回前后eax可以知道是否跳转
将storage中index=0位置的数据
通过db ecx l1可以看到参数值
栈顶的两个数据相加,放到栈顶之下的空间1+0-&0
压入参数50
压入参数ffdf,实际结果是0xffffffdf
跳转成功,instrument从0x3bb18跳转到0x3baf7,从指令表中看是回到指令4处
第一次产生循环
Itrp_PUSHB1
以刚压入0作为storeindex取出storage存储的数据,0x1,存入栈中
读取controlValueTable中index=1处的数据,由于cvtcount被恶意篡改,导致index&cvtcount使得紧跟controlValueTable其后的fnt_GlobalGraphicStateType数据被读出。该数据也就是fnt_GlobalGraphicStateTyep偏移0的数据,即stackbase。
正常情况下如果index&cvtcoun将会转到错误处理分支中。
Itrp_PUSHB1
以刚压入0作为storeindex取出storage中存储的数据,压入栈
再次使用错误的cvtcount数据从controlValueTable中读取数据,得到fnt_GlobalGraphicStateType的stackbase的值。
压入参数17
以刚压入0作为storeindex取出storage存储的数据,0x0,存入栈中
栈顶的两个数据相加,放到栈顶之下的空间1+1-&2
当前栈-4位置的数据复制到当前栈的位置,栈指针加4
相当于是将2保存下来,作为storage的第一个临时变量
压入参数50
跳转成功,instrument从0x3bb18跳转到0x3baf7,从指令表中看是回到指令4处
这里是第二次产生循环
从这里可以看出,由于程序将一直在表项4与表项28所构成的循环序列中来回循环操作。循环结束的条件将是add指令构成的累加结果等于0x50
这里由于篇幅关系,只跟踪了两个itrp循环。有兴趣的读者可以自行将其补充完全。
当win32k!itrp_WCVT中断后,可以观察到如下指令及状态:
bf860df6 8b70fc mov esi,dword ptr [eax-4] ----1、2
bf860df9 83e804 sub eax,4
bf860dfc 83e804 sub eax,4
bf860dff 57 push edi
bf860e00 8b38 mov edi,dword ptr [eax] ds:afc=0000002c ----3
kd& dd poi(bf9a9228) l-4 ---1
e25bcaf4 2c e25bd368
kd& db esi l5 ---2
e22e1368 e8 fb ff ff ff
kd& dd eax l1
e25bcafc 0000002c ----3
在执行写操作时,有如下状态:
bf860e29 8934b9 mov dword ptr [ecx+edi*4],esi
kd& db esi l5
e22e1368 e8 fb ff ff ff
ecx=e25bcf80
edi=0000002c
kd& ? ecx+edi*4
Evaluate expression: - = e22e1030
此时将要存放shellcode的地址指针写入到pfnt_GlobalGraphicStateType+0x2b*4也就是要写入到pfnt_GlobalGraphicStateType+0xAC的位置,而该地址在后面将会被另一个函数所调用。
在函数itrp_LSW处中断后,观察一下关键步骤执行时的状态:
BF98B9BC mov eax, pfnt_GlobalGraphicStateType
BF98BA11 call dword ptr [eax+0ACh]
观察此时eax+0xac所指向的值有:
kd& ? eax+0xac
Evaluate expression: - = e22e1030
kd& db poi(eax+0xac) l5
e22e1030 e8 fb ff ff ff
可见此时,将马上转入对我们所放置在e22e1030 处的shellcode的调用。
按下F11,可以看到程序已经按照我们的要求跳转到e22e1030处执行。
e22e102C 0000 add byte ptr [eax],al
e22e1030 e8fbffffff call e22e1030
e22e add byte ptr [eax],al
观察此时程序调用栈的状态如下:
# ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 b219c1e8 bf98ba17 0xe232b368
01 b219c1f8 bf85c494 win32k!itrp_LSW+0x5b
02 b219c200 bf85f4c7 win32k!itrp_InnerExecute+0x32
03 b219c284 bf85bff7 win32k!itrp_Execute+0x24f
04 b219c2ac bf85f92f win32k!itrp_ExecuteGlyphPgm+0x4c
05 b219c2e0 bf862709 win32k!fsg_SimpleInnerGridFit+0x103
06 b219c378 bf85e8bc win32k!fsg_ExecuteGlyph+0x1d3
07 b219c3d4 bf85e779 win32k!fsg_CreateGlyphData+0xd5
08 b219c414 bf85ed09 win32k!fsg_GridFit+0x4d
09 b219c48c bf85c15d win32k!fs__Contour+0x291
0a b219c498 bf85c18f win32k!fs_ContourGridFit+0x12
0b b219c4a8 bf85e43f win32k!fs_NewContourGridFit+0x10
0c b219c4c0 bf85ef8c win32k!bGetGlyphOutline+0xb3
0d b219c4e8 bf85f09a win32k!bGetGlyphMetrics+0x20
0e b219c62c bf85e5df win32k!lGetGlyphBitmap+0x2b
0f b219c654 bf85e4cb win32k!ttfdQueryFontData+0x13e
10 b219c6a0 bf85b797 win32k!ttfdSemQueryFontData+0x45
11 b219c6d0 bf85bae5 win32k!PDEVOBJ::QueryFontData+0x3c
12 b219c748 bf8081d8 win32k!xInsertMetricsPlusRFONTOBJ+0x11e
13 b219c77c bf812b35 win32k!RFONTOBJ::bGetGlyphMetricsPlus+0x180
估计内核之所以崩溃是由于内核系统栈上溢造成的。
三、对dexter.ttf文件的分析
通过查阅ttf文件格式的资料(上微软msdn上寻找),我们可以看到,字体虚拟机执行的虚拟指令位于ttf文件的glyf表中(offset-0x3bae4,length-0xbc)。通过查阅ttf文件格式的说明,得知:
图元数据(glyf表)是主要用于存放图元轮廓定义以及网格调整指令。Glyf表是TrueType字体的核心信息,因此通常它是最大的表。因为其位置索引是一张单独的表,图元数据表就完全只是图元的序列而已,每个图元以图元头结构如下:
typedef struct
WORD numberOfC 0x01 /*轮廓数目,复合图元时为负数*/
FWord xM 0x00
FWord yM 0x00
FWord xM 0x01
FWord yM 0x01
右边用红色表示的为文件中相应的数据值。
如果numberOfContours的值大于0,则为简单图元。如果小于0,则此文字为复合文字。复合文字由若干个简单文字组成。简单图元的结构如下:
对于简单图元,图元的描述紧跟在GlyphHeader结构之后。图元的描述由几部分信息组成:所有轮廓线结束点的索引、图元指令和一系列的控制点。每个控制点包括一个标志以x和y坐标。概念上而言,控制所需的信息和GDI函数PolyDraw函数所需的信息相同:一组标志和一组点的坐标。但TrueType字体中的控制点的编码要复杂得多。
下面是图元描述信息的概述:
USHORT endPtsOfContours[n]; 0x01 /*轮廓线描叙,记录廓线上点的数量,n=轮廓线条数*/
USHORT 0xA9 /*图元指令长度*/
BYTE instruction[i]; 字体虚拟机指令,从0x3BAF2~0xBB9A/*i=指令长度*/
BYTE flags[];
BYTE xCoordinates[];
BYTE yCoordinates[];
通过上面的说明,已经从文件结构的角度对相关部分做了详细阐述。如果您愿意修改这些字节的话,可以按照这样的规范进行改动。(用来做什么呢?你知道的)。
从Shellcode字节存放的文件偏移看,这部分字节位于fpgm表中(offset-0x10c,length-0x3b8)。Fpgm表也是用于存放图元指令的字体程序表。在TTF字体目录表结构定义中,专门设有checkSum字段,尽管没有仔细对照微软的文档查找计算checkSum的方法,但是在实践中猜测表项的校验方法为简单的奇偶检验法。
typedef sturct
CHAR tag[4]; /*资源标记*/
ULONG checkS /*校验位*/
ULONG /*表在TrueType结构体中的偏移量*/
ULONG /*每个表的大小*/
}TableE /*此结构体为TrueType字体中的表的定义形式*/
如果发生校验错,那么加载文件后是不能触发漏洞的。
四、漏洞利用方式的分析:
在原POC文件中,shellcode存放于dexter.tff的0x15C处,原字节指令是E8 FB FF FF FF FF,也就是直接call自身所在位置。由于没有仔细调试过内核,猜测是由于循环调用自身发生了内核堆栈上溢。
和以往的内核漏洞利用不同,通常我们无法将shellcode布置到内核空间。因此,我们一般是将shellcode放在0x0处,然后使用系统调用的形式来完成对shellcode的调用。这次不同我们可以将shellcode直接放置到内核空间中。
在实际利用时,我们可以直接将用于shellcode直接填充在该位置,溢出后将直接跳转执行。程序返回n32k!itrp_InnerExecute后将继续执行其余的字体虚拟机指令,由于eax保存了下一个虚拟字节码的地址,esi指向虚拟字节的结束地址,所以,所以当这两个个指针出错时,将导致字体虚拟机循环出错,从而再次引起内核崩溃。解决方法是严格保持内核堆栈的平衡,同时在转入shellcode后使用pushad保留寄存器的内容,在shellcode执行结束时使用popad恢复原始寄存器内容。
要利用漏洞还有一个步骤是将dexter.ttf安装到的fonts目录中,安装可以通过脚本来完成,也可以通过AddFontResourceW函数来完成。
在编译pco文件时,还有一个细节需要注意,就是需要以UNICODE的方式来生成,否则不能触发该漏洞
作者 cmdhz
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。2011上半年软件设计师试题及答案_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
2011上半年软件设计师试题及答案
21上​半​年​软​件​设​计​师​试​题​及​答​案​,​有​试​题​解​析​。
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢2011全国中考课外文言文阅读(一) - 答案(1-15)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
2011全国中考课外文言文阅读(一) - 答案(1-15)
中​考​题​课​外​文​言​文​阅​读
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢数据通信与计算机网络试卷A_试题及答案_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
数据通信与计算机网络试卷A_试题及答案
深​圳​大​学​实​验​报​告
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢微机系统与维护-试题与答案2011.7_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
微机系统与维护-试题与答案2011.7
阅读已结束,如果下载本文需要使用
想免费下载本文?
你可能喜欢

我要回帖

 

随机推荐