Talos Vulnerability Report

TALOS-2017-0509

McAfee GetSusp VersionInfo Parsing Denial of Service Vulnerability

February 20, 2019
CVE Number

CVE-2018-6687

Summary

An exploitable Denial of Service vulnerability exists in the file scanning functionality of McAfee GetSusp 3.0.0.461. A specially crafted executable can cause an infinite loop resulting in a Denial of Service. An attacker can scan this executable to trigger this vulnerability.

Tested Versions

McAfee GetSusp 3.0.0.461

Product URLs

https://www.mcafee.com/us/downloads/free-tools/getsusp.aspx

CVSSv3 Score

6.5 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

CWE

CWE-835: Loop with Unreachable Exit Condition (‘Infinite Loop’)

Details

McAfee getsusp.exe is a portable malware scanning and removal tool that can isolate malware samples found on a given computer. Classification is done via heuristics and also by talking with the McAfee GTI (Global Threat Intelligence) service. After files have been scanned and classified, unless opted out, getsusp.exe will then submit all the samples to McAfee labs.

When loading in a PE (portable executable) file, getsusp.exe will parse out different sections of the file, in search of suspect attributes, as is typical for all anti-virus. One of the sections specifically looked at is the file version information of the executable, i.e. the information that is displayed when you right click an .exe, and then navigate to ‘Properties’->’Details’ tab. Such information might contain the file version or copyright information and so forth as a collection of strings. This information is stored as such inside of an exe:

\x00\x00\x00\x00 + <Little Endian Size (4-bytes)> + Unicode String Data
00 00 00 00  DA 02 00 00  01 00 53 00  74 00 72 00 	….............S.t.
69 00 6E 00  67 00 46 00  69 00 6C 00  65 00 49 00  	r.i.n.g.F.i.l.e.I.
6E 00 66 00  6F 00 00 00  					n.f.o...

After this, more size information and language information is included, which denotes charset and language:

B6 02 00 00  01 00 30 00  34 00 30 00  39 00 30 00  	......0.4.0.9.0
34 00 42 00  4C 00 16 00  01 00 43 00			.4.B.0...L

And finally, the actual variable names and data pairs:

69 00 6C 00  65 00 6E 00  61 00 6D 00  65 00 00 00  	i.l.e.n.a.m.e...
43 00 41 00  4C 00 43 00  2E 00 45 00  58 00 45 00 	C.A.L.C...E.X.E

Back to getsusp.exe, in order to grab this version information for parsing, the program calls the Windows API calls GetFileVersionInfoSize:

DWORD WINAPI GetFileVersionInfoSize(
 		_In_      LPCTSTR lptstrFilename,
 		_Out_opt_ LPDWORD lpdwHandle
 );

Which takes the return value and feeds it into GetFileVersionInfoW, which outputs the complete version data in a buffer pointed to by lpData:

BOOL WINAPI GetFileVersionInfo(
  	_In_       LPCTSTR lptstrFilename,
  	_Reserved_ DWORD   dwHandle,
 	_In_       DWORD   dwLen,
 	_Out_      LPVOID  lpData
);

After this, the program queries the Version information struct for the VarFileInfo struct, by using VerQueryValue function::

BOOL WINAPI VerQueryValue( In LPCVOID pBlock, // Ptr to raw ver info data from GetFileVersionInfoW In LPCTSTR lpSubBlock, // Value to be retrieved: “VarFileInfo” Out LPVOID *lplpBuffer,
Out PUINT puLen
);

Unfortunately, this call fails, and the program branches accordingly to the function in which the bug occurs. This function seems to skip over the VS_VERSION_INFO header, and search for any StringFileInfo type variables within the FileVerisionInfo buffer manually. It does this with the following loop:

loc_43E4E0:             
			    	     ; esi == part after StringVersionInfo
024 lea     eax, [esi+6]    	     ; ......S.t.r.i.n.
024 push    offset StringFileInfo ; unicode(StringFileInfo) 
push    eax
call    manual_str_cmp? ; returns 0xffffff8d     
add     esp, 8
test    eax, eax
jz      short loc_43E509 ; branch not hit

movzx   eax, word ptr [esi]   // [1]           
add     esi, 3
add     esi, eax        	                   
and     esi, 0FFFFFFFCh      // [2]
cmp     esi, edi
jb      short loc_43E4E0 ; Loop back up to top

The loop does a manual search for any StringFileInfo variables by jumping over each variable, if it does not match the correct type. Since each variable has the length of the variable name and variable data (\x00\x00\x00\x00 + <Little Endian Size (4-bytes)> + Unicode String Data), the loop just increments it’s pointer by the value found in the file [1] added with three, and then aligns it at [2]. Unfortunately, if the value pointed to by esi is ever 0x0, then ((0x0 + 0x3) & 0xFFFFFFFC) = 0x0, and the pointer, esi, never actually increments. And since esi is used to break out of the loop, by comparing it to a pointer at the end of the FileVersionInfo Buffer, the loop happens indefinitely, causing the getsusp.exe to become stuck, and needing to be forcibly killed.

Crash Information

Disassembly: getsusp!RetrieveSingleExtensionList+0x2fed5a: 000000000073f20a 55 push rbp 000000000073f20b 8bec mov ebp,esp 000000000073f20d 833dac32830000 cmp dword ptr [0000000000f724c0],0 000000000073f214 7575 jne getsusp!RetrieveSingleExtensionList+0x2feddb (000000000073f28b) 000000000073f216 8b5508 mov edx,dword ptr [rbp+8] 000000000073f219 85d2 test edx,edx 000000000073f21b 7517 jne getsusp!RetrieveSingleExtensionList+0x2fed84 (000000000073f234) 000000000073f21d e85d540000 call getsusp!RetrieveSingleExtensionList+0x3041cf (000000000074467f)

Stack Dump: 0000000003c2fcc4 0043e4ee getsusp+0x3e4ee 0000000003c2fcc8 03267c36 0000000003c2fccc 0077d500 getsusp!RetrieveSingleExtensionList+0x33d050 0000000003c2fcd0 03c2fd64 0000000003c2fcd4 031cda74 0000000003c2fcd8 0000084c 0000000003c2fcdc 00000000 0000000003c2fce0 03c2fd30 0000000003c2fce4 0043dfb1 getsusp+0x3dfb1 0000000003c2fce8 03267850 0000000003c2fcec 6c2d95ae 0000000003c2fcf0 03c2fd30 00000000`03c2fcf4 0043dfc6 getsusp+0x3dfc6

Timeline

2018-03-06 - Vendor Disclosure
2018-04-10 – 1st Follow up with vendor
2018-04-25 – 2nd Follow up with vendor
2018-04-30 – 3rd Follow up with vendor
2018-04-30 – Vendor acknowledged security report and assigned Case ID SBC1803061
2018-07-26 – 4th Follow up with vendor regarding timeline for fix
2018-08-02 – Vendor advised issue addressed in build made public on 2018-07-04 (GetSusp build 3.0.0.575)
2018-08-06 – Inquired about CVE assignment from vendor; Vendor advised will not assign since it is considered a software defect and they do not consider it a vulnerability
2018-08-15 – Talos requested CVE from Mitre; Mitre reached out and provided deadline of 2018-09-05 for McAfee to respond
2018-09-28 – Vendor acknowledged best interest to publish CVE for customers
2018-10-29 – Vendor assigned CVE< br> 2018-11-12 – Follow up with vendor for public disclosure timeline
2019-01-03 – Follow up with vendor
2019-02-08 – Vendor advised will review for any internal updates to the issue
2019-02-18 - Vendor revised CVE allocation
2019-02-20 – Public Disclosure

Credit

Discovered by Lilith of Cisco Talos