TestHarnesses/T1134.004_ParentPIDSpoofing/PPIDSpoof.ps1
function Start-ATHProcessUnderSpecificParent { <# .SYNOPSIS Creates a process as a child of a specified process ID. Technique ID: T1134.004 (Access Token Manipulation: Parent PID Spoofing) .DESCRIPTION Start-ATHProcessUnderSpecificParent performs parent process ID spoofing and allows you to spawn an executable (with optional command-line arguments) from the parent process of one's choosing. .PARAMETER FilePath Specifies the filename or path to the executable to execute. If just a filename is specified, Start-ATHProcessUnderSpecificParent will look for the executable in the current working directory. If it is not present in the current working directory, Start-ATHProcessUnderSpecificParent will attempt to retrieve the full path via the OS load order. Relative or absolute paths will be resolved accordingly. .PARAMETER CommandLine Optionally, specify command-line arguments. .PARAMETER ParentId Specifies the process ID of the process under which a process will be spawned. .PARAMETER TestGuid Optionally, specify a test GUID value to use to override the generated test GUID behavior. .INPUTS System.Diagnostics.Process Start-ATHProcessUnderSpecificParent accepts the output of Get-Process. Only one Process object should be supplied to Start-ATHProcessUnderSpecificParent. Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process Start-ATHProcessUnderSpecificParent accepts the output 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 - Indicates that parent spoofing was successful. * TestGuid - Specifies the test GUID that was used for the test. This property will not be populated when -FilePath is specified. * ProcessId - Process ID of the process that spawned. * ProcessPath - Executable path of the process that spawned. * ProcessCommandLine - Command-line of the process that spawned. * ParentProcessId - Process ID of the process that the child process spawned from. * ParentProcessPath - Executable path of the process that the child process spawned from. * ParentProcessCommandLine - Command-line of the process that the child process spawned from. * SpoofingProcessId - Process ID of the process that performed the parent PID spoofing. * SpoofingProcessPath - Executable path of the process that performed the parent PID spoofing. * SpoofingProcessCommandLine - Command-line of the process that performed the parent PID spoofing. .EXAMPLE Start-ATHProcessUnderSpecificParent -ParentId $PID -FilePath notepad.exe Spawns a notepad.exe process as a child of the current process. .EXAMPLE Get-Process -Name explorer | Select-Object -First 1 | Start-ATHProcessUnderSpecificParent -FilePath notepad.exe Spawns a notepad.exe process as a child of the first explorer.exe process. .EXAMPLE Get-CimInstance -ClassName Win32_Process -Property Name, CommandLine, ProcessId -Filter "Name = 'svchost.exe' AND CommandLine LIKE '%'" | Select-Object -First 1 | Start-ATHProcessUnderSpecificParent -FilePath powershell.exe -CommandLine '-Command Write-Host foo' Spawnd a process as a child of the first accessible svchost.exe process. .EXAMPLE Start-Process -FilePath $Env:windir\System32\notepad.exe -PassThru | Start-ATHProcessUnderSpecificParent -FilePath powershell.exe -CommandLine '-Command Write-Host foo' Creates a notepad.exe process and then spawns a powershell.exe process as a child of it. .EXAMPLE Get-Process -Name lsass | Start-ATHProcessUnderSpecificParent -FilePath powershell.exe -CommandLine '-Command Write-Host foo' Spawns a powershell.exe process as a child of lsass.exe. This example requires elevation. #> [CmdletBinding(DefaultParameterSetName = 'UseTemplate')] param ( [Parameter(Mandatory, ParameterSetName = 'SupplyArgs')] [String] $FilePath, [Parameter(ParameterSetName = 'SupplyArgs')] [String] [ValidateNotNull()] $CommandLine, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'UseTemplate')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'SupplyArgs')] [Int] [Alias('Id')] # Supports pipelining with Get-Process [Alias('ProcessId')] # Supports pipelining with Get-CimInstance Win32_Process $ParentId, [Parameter(ParameterSetName = 'UseTemplate')] [Guid] $TestGuid = (New-Guid) ) $TypeDef = @' using System; using System.Runtime.InteropServices; namespace AtomicTestHarnesses_T1134_004 { public class ProcessNativeMethods { 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; } [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess( int processAccess, bool bInheritHandle, int processId); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool CloseHandle( IntPtr hHandle); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool InitializeProcThreadAttributeList( IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool UpdateProcThreadAttribute( IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool DeleteProcThreadAttributeList( IntPtr lpAttributeList); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CreateProcess( IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); } } '@ Add-Type -TypeDefinition $TypeDef $FullFilePath = $null $CraftedCommandLineString = $null $TestGuidToUse = $null switch ($PSCmdlet.ParameterSetName) { 'UseTemplate' { $FullFilePath = Get-Command -Name powershell.exe | Select-Object -ExpandProperty Path $TestGuidToUse = $TestGuid $CraftedCommandLineString = "`"$FullFilePath`" -nop -Command Write-Host $TestGuid" } 'SupplyArgs' { # Attempt to retrieve file directory $FilePathParent = Split-Path -Path $FilePath -Parent if (-not $FilePathParent) { # Just a filename was supplied. No relative or absolute path. $MergedFilePath = Join-Path -Path $PWD -ChildPath $FilePath if (Test-Path -Path $MergedFilePath -PathType Leaf) { $FullFilePath = Resolve-Path -Path $MergedFilePath -ErrorAction Stop | Select-Object -ExpandProperty Path } else { $FullFilePath = Get-Command -Name $FilePath -ErrorAction Stop | Select-Object -ExpandProperty Path } } else { # A relative or absolute path was supplied. $FullFilePath = Resolve-Path -Path $FilePath -ErrorAction Stop | Select-Object -ExpandProperty Path } if ($CommandLine) { # Append the command-line arguments $CraftedCommandLineString = "`"$FullFilePath`" $CommandLine" } else { # Just supply the full path of the executable with no command-line arguments $CraftedCommandLineString = "`"$FullFilePath`"" } } } Write-Verbose "Full path of specified executable: $FullFilePath" $EXTENDED_STARTUPINFO_PRESENT = 0x00080000 $CREATE_NO_WINDOW = 0x08000000 $PROCESS_DUP_HANDLE = 0x00000040 $PROCESS_CREATE_PROCESS = 0x00000080 $PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000 $StartupInfo = New-Object -TypeName AtomicTestHarnesses_T1134_004.ProcessNativeMethods+STARTUPINFO $StartupInfoEx = New-Object -TypeName AtomicTestHarnesses_T1134_004.ProcessNativeMethods+STARTUPINFOEX $ProcessInfo = New-Object -TypeName AtomicTestHarnesses_T1134_004.ProcessNativeMethods+PROCESS_INFORMATION #region Initialize ProcThreadAttributeList $ProcThreadAttributeListSize = [IntPtr]::Zero Write-Verbose 'Determining size required for PROC_THREAD_ATTRIBUTE_LIST structure.' $Result = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::InitializeProcThreadAttributeList( $StartupInfoEx.lpAttributeList, # lpAttributeList 1, # dwAttributeCount 0, # dwFlags [ref] $ProcThreadAttributeListSize # lpSize );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() # Throw an exception if the error code is anything other than "The data area passed to a system call is too small" if (($Result -eq $False) -and ($LastError.NativeErrorCode -ne 122)) { throw $LastError } # Allocate unmanaged memory of sufficient size for the PROC_THREAD_ATTRIBUTE_LIST structure $ProcThreadAttributeListPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcThreadAttributeListSize) $StartupInfoEx.lpAttributeList = $ProcThreadAttributeListPtr Write-Verbose 'Initializing memory for PROC_THREAD_ATTRIBUTE_LIST structure.' # Allocate ProcThreadAttributeList $Result = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::InitializeProcThreadAttributeList( $StartupInfoEx.lpAttributeList, # lpAttributeList 1, # dwAttributeCount 0, # dwFlags [ref] $ProcThreadAttributeListSize # lpSize );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($Result -eq $False) { [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcThreadAttributeListPtr) throw $LastError } #endregion Write-Verbose "Attempting to obtain a PROCESS_CREATE_PROCESS|PROCESS_DUP_HANDLE handle to process ID: $ParentId." #region Obtain handle to the target process that we'll spawn our child proc from $ProcHandle = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::OpenProcess( $PROCESS_CREATE_PROCESS -bor $PROCESS_DUP_HANDLE, # processAccess - The minimum access rights required to spawn a child process. $False, # bInheritHandle $ParentId # processId - i.e. the process ID of the process we want to spawn a child process from );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($ProcHandle -eq [IntPtr]::Zero) { # Free unmanaged memory and close process handle [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcThreadAttributeListPtr) $null = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::DeleteProcThreadAttributeList($StartupInfoEx.lpAttributeList) throw $LastError } #endregion #region Allocate a pointer to hold the value of the returned process handle $PointerToProcessHandle = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([IntPtr]::Size) [System.Runtime.InteropServices.Marshal]::WriteIntPtr($PointerToProcessHandle, $ProcHandle) Write-Verbose "Specifying process ID $ParentId as target parent process ID." # Supply the process handle to the ProcThreadAttribute list $Result = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::UpdateProcThreadAttribute( $StartupInfoEx.lpAttributeList, # lpAttributeList 0, # dwFlags $PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, # Attribute $PointerToProcessHandle, # lpValue [IntPtr]::Size, # cbSize [IntPtr]::Zero, # lpPreviousValue [IntPtr]::Zero # lpReturnSize ) if ($Result -eq $False) { # Free unmanaged memory and close process handle [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcThreadAttributeListPtr) [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PointerToProcessHandle) $null = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::DeleteProcThreadAttributeList($StartupInfoEx.lpAttributeList) $null = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::CloseHandle($ProcHandle) throw $LastError } #endregion #region Start process as child of target parent $StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][AtomicTestHarnesses_T1134_004.ProcessNativeMethods+STARTUPINFOEX]) $StartupInfoEx.StartupInfo = $StartupInfo Write-Verbose "Spawning process as a child of parent process ID $ParentId." $Result = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::CreateProcess( [IntPtr]::Zero, # lpApplicationName $CraftedCommandLineString, # lpCommandLine [IntPtr]::Zero, # lpProcessAttributes [IntPtr]::Zero, # lpThreadAttributes $False, # bInheritHandles $EXTENDED_STARTUPINFO_PRESENT -bor $CREATE_NO_WINDOW, # dwCreationFlags [IntPtr]::Zero, # lpEnvironment $PWD.Path, # lpCurrentDirectory [ref] $StartupInfoEx, # lpStartupInfo [ref] $ProcessInfo # lpProcessInformation );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() # Free unmanaged memory and close process handle [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcThreadAttributeListPtr) [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PointerToProcessHandle) $null = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::DeleteProcThreadAttributeList($StartupInfoEx.lpAttributeList) $null = [AtomicTestHarnesses_T1134_004.ProcessNativeMethods]::CloseHandle($ProcHandle) if ($Result -eq $False) { throw $LastError } #endregion # Get process information for the spawned process $WMIProcessInfo = Get-CimInstance -ClassName Win32_Process -Property ParentProcessId, ProcessId, CommandLine, ExecutablePath -Filter "ProcessId = $($ProcessInfo.dwProcessId)" -ErrorAction SilentlyContinue # Get process information for the targeted parent process $WMIParentProcessInfo = Get-CimInstance -ClassName Win32_Process -Property ProcessId, CommandLine, ExecutablePath -Filter "ProcessId = $($WMIProcessInfo.ParentProcessId)" -ErrorAction SilentlyContinue if (-not $WMIProcessInfo) { Write-Error "Failed to obtain spawned process info for process ID $($ProcessInfo.dwProcessId)." -ErrorAction Stop } if (-not $WMIParentProcessInfo) { Write-Error "Failed to obtain parent process info for process ID $ParentId." -ErrorAction Stop } $CurrentProcessInfo = Get-CimInstance -ClassName Win32_Process -Property CommandLine, ExecutablePath -Filter "ProcessId = $PID" -ErrorAction SilentlyContinue [PSCustomObject] @{ TechniqueID = 'T1134.004' TestSuccess = $True TestGuid = $TestGuidToUse ProcessId = $ProcessInfo.dwProcessId ProcessPath = $WMIProcessInfo.ExecutablePath ProcessCommandLine = $WMIProcessInfo.CommandLine ParentProcessId = $WMIParentProcessInfo.ProcessId ParentProcessPath = $WMIParentProcessInfo.ExecutablePath ParentProcessCommandLine = $WMIParentProcessInfo.CommandLine SpoofingProcessId = $PID SpoofingProcessPath = $CurrentProcessInfo.ExecutablePath SpoofingProcessCommandLine = $CurrentProcessInfo.CommandLine } } |