Trend Micro SSAPI Long Path Buffer Overflow Vulnerability I - BACKGROUND Trend Micro AntiSpyware is a security product by Trend Micro. http://www.trendmicro.com/en/home/us/home.htm II - DESCRIPTION A stack overflow vulnerability has been identified in Trend Micro AntiSpyware. It can be exploited by malicious users to gain escalated privileges, or by malicious people to compromise a vulnerable system. The vulnerability is caused due to boundary errors when the AntiSpyware file monitor engine perform actions if a new file is created with an overlay long path. These can be exploited to cause an stack-based buffer overflow. Successful exploitation may allow execution of arbitrary code, gain escalated privileges or crash AntiSpyware process (write access to any directory on remote server is required to remotely exploitation) The program is compiled with /GS and /SAFESEH nevertheless it's possible to overwrite all the stack triggering an exception (the program reach the end of the stack) III - DISCLOSURE TIMELINE IV - AFFECTED PRODUCTS Trend Micro AntiSpyware: Product Version: 3.50 Build: 3.5.0.1041 vstlib32.dll - 1.0.0.1037 (Previous versions could be affected) V - ADVANCED DESCRIPTION According to MSDN you cannot use any file functions (CreateFile, GetFileAttributes, etc) with a path which is longer than MAX_PATH, unless you prefix it with \\?\. Paths longer than 520 bytes (0x208) will cause the program to overwrite the entire stack. The affected component is vstlib32.dll. The vulnerability resides with the function sub_67105290. This function perform actions with a FILE_NOTIFY_INFORMATION structure returned from a previous call to ReadDirectoryChangesW: .text:671054C7 50 push eax ; lpCompletionRoutine .text:671054C8 8D 54 24 18 lea edx, [esp+0A68h+Overlapped] .text:671054CC 52 push edx ; lpOverlapped .text:671054CD 89 44 24 1C mov [esp+0A6Ch+Overlapped.Internal], eax .text:671054D1 89 44 24 20 mov [esp+0A6Ch+Overlapped.InternalHigh], eax .text:671054D5 89 44 24 24 mov [esp+0A6Ch+Overlapped.Offset], eax .text:671054D9 89 44 24 28 mov [esp+0A6Ch+Overlapped.OffsetHigh], eax .text:671054DD 89 44 24 2C mov [esp+0A6Ch+Overlapped.hEvent], eax .text:671054E1 8D 44 24 18 lea eax, [esp+0A6Ch+BytesReturned] .text:671054E5 50 push eax ; lpBytesReturned .text:671054E6 68 53 01 00 00 push 153h ; dwNotifyFilter .text:671054EB 6A 01 push 1 ; bWatchSubtree .text:671054ED 68 00 20 00 00 push 2000h ; nBufferLength .text:671054F2 8D 9E 28 0A 00 00 lea ebx, [esi+0A28h] .text:671054F8 53 push ebx ; lpBuffer .text:671054F9 57 push edi ; hDirectory .text:671054FA FF D5 call ebp ; ReadDirectoryChangesW This is the structure: typedef struct _FILE_NOTIFY_INFORMATION { DWORD NextEntryOffset; DWORD Action; DWORD FileNameLength; WCHAR FileName[1]; } The vulnerability resides in the following code: A local variable of 0x208 Bytes size is initialized with zeros: .text:671052C5 loc_671052C5: ; CODE XREF: sub_67105290+127j .text:671052C5 68 08 02 00 00 push 208h ; size_t ; Size of var to initialize .text:671052CA 8D 4C 24 0C lea ecx, [esp+420h+var_414] .text:671052CE 6A 00 push 0 ; int ; Value to initialize var with .text:671052D0 51 push ecx ; void * ; Local var to initialize .text:671052D1 E8 1A 39 00 00 call _memset ; Initialize Local variable (B) [0x208 bytes (520)] This local var is filled with the path obtained from the FILE_NOTIFY_INFORMATION structure (WCHAR FileName[1]). The size of data to be copied from the FILE_NOTIFY_INFORMATION structure to the local variables is obtained from the same structure (DWORD FileNameLength): .text:671052D6 8B 56 08 mov edx, [esi+8] ; ;Get Size from a FILE_NOTIFY_INFORMATION structure (DWORD FileNameLength) .text:671052D9 D1 EA shr edx, 1 .text:671052DB 52 push edx ; size_t ;Size from a FILE_NOTIFY_INFORMATION structure (DWORD FileNameLength) .text:671052DC 8D 46 0C lea eax, [esi+0Ch] .text:671052DF 50 push eax ; wchar_t *;Path from FILE_NOTIFY_INFORMATION structure (WCHAR FileName[1]) .text:671052E0 8D 4C 24 1C lea ecx, [esp+430h+var_414] .text:671052E4 51 push ecx ; wchar_t *;Destination (local variable) .text:671052E5 E8 AB 3F 00 00 call _wcsncpy ; ;Copy path from (WCHAR FileName[1]) to local variable This size isn't checked to verify if it's greater than the local var size. Therefore if a path longer than 0x208 bytes is used, a local variable will be overflowed. But why is the entire stack overwritten? The function sub_67105290 reserve two local variables of 0x208 bytes each one: _______________________________ | | | Local Var (B) | |_______________________________| | | | Local Var (A) | |_______________________________| .text:671052A6 push 208h ; size_t .text:671052AB lea eax, [esp+420h+var_20C] .text:671052B2 push 0 ; int .text:671052B4 push eax ; void * .text:671052B5 mov edi, ecx .text:671052B7 call _memset ; Initialize Local variable (A) [208h bytes (520)] .text:671052BC add esp, 0Ch .text:671052BF lea esi, [edi+0A28h] .text:671052C5 .text:671052C5 loc_671052C5: ; CODE XREF: sub_67105290+127j .text:671052C5 push 208h ; size_t .text:671052CA lea ecx, [esp+420h+var_414] .text:671052CE push 0 ; int .text:671052D0 push ecx ; void * .text:671052D1 call _memset ; Initialize Local variable (B) [208h bytes (520)] The path name from the FILE_NOTIFY_INFORMATION structure is copied to variable (B): .text:671052D6 mov edx, [esi+8] .text:671052D9 shr edx, 1 .text:671052DB push edx ; size_t; .text:671052DC lea eax, [esi+0Ch] .text:671052DF push eax ; wchar_t * ; .text:671052E0 lea ecx, [esp+430h+var_414] .text:671052E4 push ecx ; wchar_t * .text:671052E5 call _wcsncpy ; Copy path to Variable (B) (OVERFLOW!!!!: overwrite var (A) ) Some actions with the path name are done: .text:6710532D loc_6710532D: ; CODE XREF: sub_67105290+96j .text:6710532D 85 C0 test eax, eax .text:6710532F 74 44 jz short loc_67105375 .text:67105331 8D 54 24 08 lea edx, [esp+41Ch+var_414] .text:67105335 52 push edx ; Variable (B) .text:67105336 8B CF mov ecx, edi .text:67105338 E8 33 FD FF FF call sub_67105070 ; Check the path: Break a path name into components, check FileName extension ; (cab, zip, exe, dll, rar, scr, sys, chm, ocx, com, pif, wmf), chech against predefined ; filenames (etc\hosts, etc\hosts.new, start menu\programs, win.ini, iereset.inf, ; system.ini) .text:6710533D 68 08 02 00 00 push 208h ; size_t .text:67105342 8D 84 24 14 02 00 00 lea eax, [esp+420h+var_20C] .text:67105349 6A 00 push 0 ; int .text:6710534B 50 push eax ; void * .text:6710534C E8 9F 38 00 00 call _memset ; Initialize Local variable (A) [208h bytes (520)] again And finally, copy the local variable (B) to local variable (A): .text:67105360 loc_67105360: ; CODE XREF: sub_67105290+C6j .text:67105360 ; sub_67105290+E3j .text:67105360 0F B7 4C 04 08 movzx ecx, [esp+eax+41Ch+var_414] ;Get a char from (B) .text:67105360 ;Copy char from var (B) to var (A) .text:67105365 66 89 8C 04 10 02 00 00 mov word ptr [esp+eax+41Ch+var_20C], cx .text:6710536D 83 C0 02 add eax, 2 .text:67105370 66 85 C9 test cx, cx ;test if end of string .text:67105373 75 EB jnz short loc_67105360 Here is the problem. If local var (B) is overflowed, no end string characted (\00) will be at the end of the local var (B), therefore an infinite copy loop will overwrite the entire stack. VI - CREDIT Bug found by Ismael Briones [http://www.inkatel.com]