Make stage encoding possible
* Fixes a bug in shikata where input greater than 0xffff length would still use 16-bit counter * Short circuits finding bad xor keys if there are no bad characters to avoid * Fixes huge performance issue with large inputs to xor-based encoders due to the use of String#+ instead of String#<< in a loop. It now takes ~3 seconds on modern hardware to encode a 750kB buffer with shikata where it used to take more than 10 minutes. The decoding side takes a similar amount of time and will increase the wait between sending the second stage and opening a usable session by several seconds. I believe this addresses the intent of pull request 905 [See #905]bug/bundler_fix
parent
4703a6f737
commit
b3b68c1b90
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue