当tmactmon.sys接收到ioctl_code=0x9100444f的IRP时,会调用Dispatch函数进行进行处理,如下所示。
.text:00011116 ; int __stdcall BugDispatch(int, PIRP Irp)
.text:00011116 BugDispatch proc near ; DATA XREF: sub_11C4C+16D o
.text:00011116
.text:00011116 inbuffer = dword ptr -1Ch
.text:00011116 UserBuffer = dword ptr -18h
.text:00011116 IoStatus = dword ptr -10h
.text:00011116 outbufferLength = dword ptr -0Ch
.text:00011116 inbufferLength = dword ptr -8
.text:00011116 var_4 = dword ptr -4
.text:00011116 Irp = dword ptr 0Ch
.text:00011116
.text:00011116 mov edi, edi
.text:00011118 push ebp
.text:00011119 mov ebp, esp
.text:0001111B sub esp, 1Ch
在该处理函数的最后,会将inLength、outLength、inbuffer、UserBuffer等信息保存,并调用sub_12BE2函数。可以看到传入的参数即为栈中存放inbuffer的地址。
.text:0001115E loc_1115E: ; CODE XREF: BugDispatch+2A j
.text:0001115E mov edx, [eax+10h]
.text:00011161 mov [ebp+inbuffer], edx
.text:00011164 mov edx, [esi+3Ch]
.text:00011167 mov [ebp+UserBuffer], edx
.text:0001116A mov edx, [eax+8]
.text:0001116D mov [ebp+outbufferLength], edx
.text:00011170 mov edx, [eax+4]
.text:00011173 mov [ebp+inbufferLength], edx
.text:00011176 mov [ebp+IoStatus], ecx
.text:00011179 mov eax, [eax+0Ch]
.text:0001117C mov [ebp+var_4], eax
.text:0001117F lea eax, [ebp+inbuffer]
.text:00011182 push eax
.text:00011183 call sub_12BE2
sub_12BE2函数完成的主要功能是调用sub_12AE8函数对ring3传来的缓冲区进行检查。对sub_12AE8函数进行反编译后的代码如下。可以看到,该函数使用ProbeForRead 和ProbeForWrite对输入和输出缓冲区进行了严格的检查,同时还检查了发送ioctl是否是趋势本身的进程。
signed int __thiscall sub_12AE8(int this, int a2)
{
if ( &inbuffer && (inLength || !inbuffer) && (outLength || !outBuffer ))
{
if ( ExGetPreviousMode() == 1 )
{
ProbeForRead(*(const void **)v3, *(_DWORD *)(v3 + 16), 1u);
ProbeForWrite(*(PVOID *)(v3 + 4), *(_DWORD *)(v3 + 20), 1u); }
//省略无关代码
LABEL_13:
if ( !v7 )
{
v5 = *(_DWORD *)(v3 + 8);
if ( v5 && *(_BYTE *)(v5 + 24) )
v2 = *(_DWORD *)(v5 + 8) != (_DWORD)PsGetCurrentProcessId() ? 0xC00000BB : 0;
else
v2 = -1073741637;
}
result = v2;
}
else
{
result = -1073741811;
}
return result;
}
当检查通过后,就会调用sub_1291C,sub_1291C将会继续调用sub_19814函数,sub_19814的执行过程如下。注意观察红色部分的汇编代码,由于ebp+arg_4的内容为inbuffer中的dword[1],可以任意指定,不妨指定为0x12210005,所以将会调用sub_19554执行。
.text:00019814 ; int __stdcall sub_19814(int, int, PVOID Object)
.text:00019814 sub_19814 proc near ; CODE XREF: v6+29 p
.text:00019814 var_20 = dword ptr -20h
.text:00019814 ms_exc = CPPEH_RECORD ptr -18h
.text:00019814 arg_0 = dword ptr 8
.text:00019814 arg_4 = dword ptr 0Ch
.text:00019814 Object = dword ptr 10h
.text:00019814
.text:00019814 push 10h
.text:00019816 push offset unk_1E1A8
.text:0001981B call __SEH_prolog4
.text:00019820 mov esi, ecx
.text:00019822 lea eax, [esi+14h]
.text:00019825 mov [ebp+var_20], eax
.text:00019828 xor ecx, ecx
.text:0001982A inc ecx
.text:0001982B lock xadd [eax], ecx
.text:0001982F cmp [ebp+arg_4], 1221A007h
.text:00019836 jnz short loc_19885
.text:00019885 loc_19885: ; CODE XREF: sub_19814+22 j
.text:00019885 mov ebx, [ebp+Object]
.text:00019888 push ebx ; Object
.text:00019889 push [ebp+arg_4] ; int
.text:0001988C mov ecx, esi
.text:0001988E call sub_19554
sub_19554的执行过程如下所示,标红几处为重要的跳转。由于ecx=0x12210005,最终会执行到.text:00019591。ebp+Object的内容为inbuffer中的dword[3],可以被任意指定,不妨指定为0xffff0000,最终在push dword ptr [esi]执行出错,内存0xffff0000不可读。
.text:00019554 ; int __stdcall sub_19554(int, PVOID Object)
.text:00019554 sub_19554 proc near ; CODE XREF: sub_19814+7A p
.text:00019554 arg_0 = dword ptr 8
.text:00019554 Object = dword ptr 0Ch
.text:00019554
.text:00019554 mov edi, edi
.text:00019556 push ebp
.text:00019557 mov ebp, esp
.text:00019559 mov ecx, [ebp+arg_0] ;ecx=0x12210005
.text:0001955C push ebx
.text:0001955D push esi
.text:0001955E push edi
.text:0001955F mov eax, 12210006h
.text:00019564 cmp ecx, eax
.text:00019566 mov edi, 0E0000001h
.text:0001956B mov ebx, edi
.text:0001956D ja loc_19670
.text:00019573 jz loc_19645.
text:00019579 sub ecx, 12210001h
.text:0001957F jz loc_1961B
.text:00019585 dec ecx
.text:00019586 jz short loc_195E0
.text:00019588 sub ecx, 3
.text:0001958B jnz loc_196AC
.text:00019591 mov esi, [ebp+Object]
.text:00019594 test esi, esi
.text:00019596 jz loc_196B1
.text:0001959C and [ebp+Object], ecx
.text:0001959F lea eax, [ebp+Object]
.text:000195A2 push eax
.text:000195A3 push dword ptr [esi]
.text:000195A5 call sub_194E6
从整个漏洞的分析过程可以看到,触发该漏洞需要注入到趋势自身的ring3进程,并向tmactmon.sys发送恶意构造的IRP请求,其中ioctl_code=0x9100444f, inLength=0x10, outLength=0x10, inbuffer为0,0,0,0,5,0,0x21,0x12,0,0,0,0,0,0,0xff,0xff。以上就是整个分析过程。
Happy Hacking~
~~~~~~~~~~~~~~~~~~~~~~免责声明~~~~~~~~~~~~~~~~~~~~~
本文所涉及内容仅用于软件安全技术研究、学习,严禁用于不良动机。
任何个人、团体、组织不得将其用于非法目的,否则后果自负。
本人不承担由这些代码造成的用户计算机崩溃,数据丢失等责任及连带责任。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!