[转载]OLEAUT32.dll模块中处理类型库的相关函数可导致代码执行
刚下载完几部不错的片子,但是考虑到做人要讲信用,所以我还是坚持写完了这篇Blog。这个小问题是2008年初发现的,报告给微软后,微软认为这并不是一个安全漏洞——或者说,这并不是一个值得修复的安全漏洞。所以,以下以“小问题”来称呼这个小问题。
接触过COM的朋友,对类型库(TypeLib)一定不陌生。类型库由接口描述语言(IDL)编写,类似下面这样:
// TLBTest.idl
[
uuid( 11111111-1111-1111-1111-111111111111 ),
version( 1.0 ),
helpstring( "TLBTest COM" )
]
library TLBTest
{
importlib( "stdole2.tlb" );
[
uuid( 22222222-2222-2222-2222-222222222222 ),
//helpstring( "struct TLBTestVtbl" ),
dual,
oleautomation,
hidden,
nonextensible
]
interface TLBTest_ : IDispatch
{
};
[
uuid( 33333333-3333-3333-3333-333333333333 ),
//helpstring( "struct TLBTest" ),
appobject
]
coclass TLBTest
{
[default] interface TLBTest_;
}
}
把上面的TLBTest.idl用midl.exe编译,生成TLBTest.tlb。这就是一个类型库。类型库可以独立存在,也可以捆绑在PE文件中。
在Windows系统中,处理类型库的函数都封装在系统目录下的OLEAUT32.dll模块里。当你使用COM(譬如ActiveX控件)时,就会和这些函数打交道。
打开前面编译生成的TLBTest.tlb,可以看到偏移0x1A8处的4字节是0。当类型库被映射到内存中后,这四个字节会用来存放一个类实例指针。四个字节的0实际上是把这个指针初始化为NULL。
事实上,当类型库被加载到内存中后,OLEAUT32.dll模块中的代码会先判断一下这个指针是不是NULL:
775F4D8C mov edi, [esi+5Ch] ;
775F4D8F test edi, edi ; 如果0x1A8处不为0
775F4D91 jnz loc_775F4E6E
775F4E6E loc_775F4E6E:
775F4E6E mov ecx, edi ;
775F4E70 call CTypeInfo2::InternalAddRef(void)
如果这四字节不为0,就会直接将它作为类实例指针进行一系列的函数调用。类似这样:
7760C1F0 mov eax, [edi]
7760C1F2 lea ecx, [ebp+var_2F0]
7760C1F8 push ecx
7760C1F9 push [ebp+lpSubKey]
7760C1FF push edi
7760C200 call dword ptr [eax+10h] ; OLEAUT32!CTypeLib2::GetTypeInfo()
7760C203 mov ebx, eax
7760C205 test ebx, ebx
7760C207 jl loc_7760C13C
7760C20D mov eax, [ebp+var_314]
7760C213 test eax, eax
7760C215 jnz loc_776134EB
7760C21B mov eax, [ebp+var_2F0]
7760C221 mov ecx, [eax] ; eax = 0x1A8处的值+4
7760C223 lea edx, [ebp+var_334]
7760C229 push edx
7760C22A push eax
7760C22B call dword ptr [ecx+0Ch] ; bingo
那么这个小问题影响哪些软件呢?可以说,凡是用了OLEAUT32.dll中那些类型库函数的软件,都受影响。但是对大多数软件来说,这并不算太大的问题。因为当其处理某类型库时,也就意味着执行了类型库对应的COM代码。
譬如说,你在系统上安装了Real Player,那么当你用任何软件播放Real格式的媒体文件时,实际上都处理了Real Player的类型库。理论上,如果这个类型库是特殊处理过的,就可能使你执行任意代码。但是别忘了,系统处理Real Player的类型库是为了执行播放器的代码。如果有人想构造一个恶意的Real Player,那么直接修改代码本身是更简单的办法。
所以说,这个小问题,对一般人影响并不大。
但是,假如你从事的是软件方面的技术工作,经常编译一些网上看到的代码,或者做一些逆向分析的工作,那么这个小问题也许就不那么小了。
很多开发工具都会处理那些并没有安装在系统上的COM中的类型库。也就是说,这些工具仅仅只是“查看”COM的类型库。而现在,当你认为自己只是在“查看”一个类型库时,就可能已经执行了代码。
VC中带的OLEView工具、VC编译器本身(cl.exe及其处理CPP的相关模块)、eXeScope或者其它任何一个能查看PE资源中类型库信息的工具、Total Commander的Fileinfo插件([url]http://physio-a.univ-tours.fr/tcplugins/FILEINFO.htm[/url]),至少这些工具都可以触发这个小问题。
需要解释一下的是,VC编译器为什么会在受影响的列表里。VC编译器支持直接从类型库中“import”获取接口定义的工作方式。也就是说,当我们在任意一个CPP文件中,插入一行:
#import "\\192.168.0.254\Daddy\TLBTest.tlb"
这样,编译器在编译时就会试图从192.168.0.254这台主机上的Daddy共享目录下读取并解析TLBTest.tlb。
由于这是系统库的问题,和任何软件本身无关。所以它影响至少VC 6之后所有版本的VC。又由于至少Windows 2000之后所有Windows的OLEAUT32.dll都有这个问题,所以它基本上影响所有用VC的人。想象一下:你从某网页找到一段能解决你问题的代码,复制下来,一编译,啪~。
这是一个控制指针的小问题,对这类问题一般首先想到的是Heap Spray。然而,在VC编译器这类软件中是没法Heap Spray的(我和同事测试过多种方案,都没有成功,当然也许还有我们没有想到的方法)。
所以后来用了一种不依赖Heap Spray的方法,也可以几乎百分之百成功利用。不过对软件的大版本号略有依赖。以VC为例,VC 6的利用是一个代码,VC 2005和VC 2008是另一个。也许还有人记得这篇Blog:/Article/200910/41773.html,其实讲的就是这个小问题的利用。
顺便多说一句:对部分类型的漏洞来说,DEP+ALSR+SEHOP虽然还不能说完全是浮云,起码也是部分浮云了。
页:
[1]