返回列表 发帖

[转载]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插件(http://physio-a.univ-tours.fr/tcplugins/FILEINFO.htm),至少这些工具都可以触发这个小问题。

需要解释一下的是,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虽然还不能说完全是浮云,起码也是部分浮云了。

返回列表