diff --git a/lib/msf/core/encoder.rb b/lib/msf/core/encoder.rb index b1b17cab23..b8ebff5635 100644 --- a/lib/msf/core/encoder.rb +++ b/lib/msf/core/encoder.rb @@ -308,7 +308,12 @@ class Encoder < Module while (offset < state.buf.length) block = state.buf[offset, decoder_block_size] - state.encoded += encode_block(state, + # Append here (String#<<) instead of creating a new string with + # String#+ because the allocations kill performance with large + # buffers. This isn't usually noticeable on most shellcode, but + # when doing stage encoding on meterpreter (~750k bytes) the + # difference is 2 orders of magnitude. + state.encoded << encode_block(state, block + ("\x00" * (decoder_block_size - block.length))) offset += decoder_block_size diff --git a/lib/msf/core/encoder/xor.rb b/lib/msf/core/encoder/xor.rb index 69b6db7d30..4c351ac45e 100644 --- a/lib/msf/core/encoder/xor.rb +++ b/lib/msf/core/encoder/xor.rb @@ -26,6 +26,9 @@ class Msf::Encoder::Xor < Msf::Encoder # Finds keys that are incompatible with the supplied bad character list. # def find_bad_keys(buf, badchars) + # Short circuit if there are no badchars + return super if badchars.length == 0 + bad_keys = Array.new(decoder_key_size) { Hash.new } byte_idx = 0 diff --git a/lib/msf/core/payload/stager.rb b/lib/msf/core/payload/stager.rb index 45674bc0f1..7a66ae3203 100644 --- a/lib/msf/core/payload/stager.rb +++ b/lib/msf/core/payload/stager.rb @@ -1,5 +1,6 @@ # -*- coding: binary -*- require 'msf/core' +require 'msf/core/option_container' ### # @@ -8,6 +9,17 @@ require 'msf/core' ### module Msf::Payload::Stager + def initialize(info={}) + super + + register_advanced_options( + [ + Msf::OptBool.new("EnableStageEncoding", [ false, "Encode the second stage payload", false ]), + Msf::OptString.new("StageEncoder", [ false, "Encoder to use if EnableStageEncoding is set", nil ]), + ], Msf::Payload::Stager) + + end + # # Sets the payload type to a stager. # @@ -65,6 +77,11 @@ module Msf::Payload::Stager true end + def encode_stage? + # Convert to string in case it hasn't been normalized + !!(datastore['EnableStageEncoding'].to_s == "true") + end + # # Generates the stage payload and substitutes all offsets. # @@ -75,8 +92,8 @@ module Msf::Payload::Stager # Substitute variables in the stage substitute_vars(p, stage_offsets) if (stage_offsets) - # Encode the stage of stage encoding is enabled - #p = encode_stage(p) + # Encode the stage if stage encoding is enabled + p = encode_stage(p) return p end @@ -101,14 +118,15 @@ module Msf::Payload::Stager p = (self.stage_prefix || '') + p end + sending_msg = "Sending #{encode_stage? ? "encoded ":""}stage" + sending_msg << " (#{p.length} bytes)" # The connection should always have a peerhost (even if it's a # tunnel), but if it doesn't, erroring out here means losing the # session, so make sure it does, just to be safe. if conn.respond_to? :peerhost - print_status("Sending stage (#{p.length} bytes) to #{conn.peerhost}") - else - print_status("Sending stage (#{p.length} bytes)") + sending_msg << " to #{conn.peerhost}" end + print_status(sending_msg) # Send the stage conn.put(p) @@ -146,15 +164,20 @@ module Msf::Payload::Stager # Encodes the stage prior to transmission def encode_stage(stg) + return stg unless encode_stage? - # If DisableStageEncoding is set, we do not encode the stage - return stg if datastore['DisableStageEncoding'] =~ /^(y|1|t)/i + if datastore["StageEncoder"].nil? or datastore["StageEncoder"].empty? + stage_enc_mod = nil + else + stage_enc_mod = datastore["StageEncoder"] + end # Generate an encoded version of the stage. We tell the encoding system # to save edi to ensure that it does not get clobbered. encp = Msf::EncodedPayload.create( self, 'Raw' => stg, + 'Encoder' => stage_enc_mod, 'SaveRegisters' => ['edi'], 'ForceEncode' => true) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index e1d17f3d92..b1deb09058 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2365,6 +2365,7 @@ class Core return option_values_payloads() if opt.upcase == 'PAYLOAD' return option_values_targets() if opt.upcase == 'TARGET' return option_values_nops() if opt.upcase == 'NOPS' + return option_values_encoders() if opt.upcase == 'StageEncoder' end # Well-known option names specific to auxiliaries diff --git a/modules/encoders/x86/shikata_ga_nai.rb b/modules/encoders/x86/shikata_ga_nai.rb index 6cfeec98ea..ffc602db84 100644 --- a/modules/encoders/x86/shikata_ga_nai.rb +++ b/modules/encoders/x86/shikata_ga_nai.rb @@ -111,10 +111,10 @@ protected # Clear the counter register clear_register = Rex::Poly::LogicalBlock.new('clear_register', - "\x31\xc9", - "\x29\xc9", - "\x33\xc9", - "\x2b\xc9") + "\x31\xc9", # xor ecx,ecx + "\x29\xc9", # sub ecx,ecx + "\x33\xc9", # xor ecx,ecx + "\x2b\xc9") # sub ecx,ecx # Initialize the counter after zeroing it init_counter = Rex::Poly::LogicalBlock.new('init_counter') @@ -126,8 +126,10 @@ protected if (length <= 255) init_counter.add_perm("\xb1" + [ length ].pack('C')) - else + elsif (length <= 65536) init_counter.add_perm("\x66\xb9" + [ length ].pack('v')) + else + init_counter.add_perm("\xb9" + [ length ].pack('V')) end # Key initialization block