1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-11-12 11:52:01 +01:00

fixed up encoding, made payload generation pimper

git-svn-id: file:///home/svn/incoming/trunk@2746 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Matt Miller 2005-07-13 21:09:07 +00:00
parent d4f569dddf
commit 72ad97dfd1
10 changed files with 104 additions and 68 deletions

View File

@ -19,13 +19,14 @@ class Payload
# #
# opts can have: # opts can have:
# #
# Encoder => A encoder module instance. # Encoder => A encoder module instance.
# Badchars => A string of bad characters. # Badchars => A string of bad characters.
# Format => The format to represent the data as: ruby, perl, c, raw # Format => The format to represent the data as: ruby, perl, c, raw
# Options => A hash of options to set. # Options => A hash of options to set.
# OptionStr => A string of options in VAR=VAL form separated by # OptionStr => A string of options in VAR=VAL form separated by
# whitespace. # whitespace.
# NoComment => Disables prepention of a comment # NoComment => Disables prepention of a comment
# NopSledSize => The number of NOPs to use
# #
# raises: # raises:
# #
@ -43,30 +44,28 @@ class Payload
end end
# Generate the payload # Generate the payload
buf = payload.generate e = EncodedPayload.create(payload,
'BadChars' => opts['BadChars'],
# If an encoder was specified, encode the generated payload 'MinNops' => opts['NopSledSize'],
if (opts['Encoder']) 'Encoder' => opts['Encoder'])
buf = opts['Encoder'].encode(buf, opts['Badchars'])
end
fmt = opts['Format'] || 'raw' fmt = opts['Format'] || 'raw'
# Save off the original payload length # Save off the original payload length
len = buf.length len = e.encoded.length
# Serialize the generated payload to some sort of format # Serialize the generated payload to some sort of format
buf = Buffer.transform(buf, fmt) buf = Buffer.transform(e.encoded, fmt)
# Prepend a comment # Prepend a comment
if (fmt != 'raw' and opts['NoComment'] != true) if (fmt != 'raw' and opts['NoComment'] != true)
((ds = payload.datastore.to_s) and ds.length > 0) ? ds += "\n" : ds = '' ((ds = payload.datastore.to_s) and ds.length > 0) ? ds += "\n" : ds = ''
buf = Buffer.comment( buf = Buffer.comment(
"#{payload.refname} - #{len} bytes\n" + "#{payload.refname} - #{len} bytes\n" +
"http://www.metasploit.com\n" + "http://www.metasploit.com\n" +
"#{ds}" + "#{ds}" +
((opts['Encoder']) ? "Encoder=" + opts['Encoder'].refname + "\n" : ''), fmt) + buf ((e.encoder) ? "Encoder: #{e.encoder.refname}\n" : '') +
((e.nop) ? "NOP generator: #{e.nop.refname}\n" : ''), fmt) + buf
end end
return buf return buf

View File

@ -24,6 +24,9 @@ require 'msf/core/framework'
require 'msf/core/session_manager' require 'msf/core/session_manager'
require 'msf/core/session' require 'msf/core/session'
# Wrappers
require 'msf/core/encoded_payload'
# Pseudo-modules # Pseudo-modules
require 'msf/core/handler' require 'msf/core/handler'

View File

@ -18,9 +18,9 @@ class EncodedPayload
# #
# Creates an encoded payload instance # Creates an encoded payload instance
# #
def self.create(framework, pinst, reqs) def self.create(pinst, reqs)
# Create the encoded payload instance # Create the encoded payload instance
p = EncodedPayload.new(framework, pinst, reqs) p = EncodedPayload.new(pinst.framework, pinst, reqs)
p.generate p.generate
@ -37,10 +37,12 @@ class EncodedPayload
# Generate the full encoded payload # Generate the full encoded payload
# #
def generate def generate
raw = nil self.raw = nil
encoded = nil self.encoded = nil
nop_sled_size = 0 self.nop_sled_size = 0
nop_sled = nil self.nop_sled = nil
self.encoder = nil
self.nop = nil
# Generate the raw version of the payload first # Generate the raw version of the payload first
generate_raw() generate_raw()
@ -52,7 +54,7 @@ class EncodedPayload
generate_sled() generate_sled()
# Finally, set the complete payload definition # Finally, set the complete payload definition
encoded = (nop_sled || '') + encoded self.encoded = (self.nop_sled || '') + self.encoded
# Return the complete payload # Return the complete payload
return encoded return encoded
@ -63,7 +65,7 @@ class EncodedPayload
# raw attribute. # raw attribute.
# #
def generate_raw def generate_raw
raw = (reqs['Prepend'] || '') + pisnt.generate + (reqs['Append'] || '') self.raw = (reqs['Prepend'] || '') + pinst.generate + (reqs['Append'] || '')
end end
# #
@ -74,32 +76,44 @@ class EncodedPayload
# If the exploit has bad characters, we need to run the list of encoders # If the exploit has bad characters, we need to run the list of encoders
# in ranked precedence and try to encode without them. # in ranked precedence and try to encode without them.
if (reqs['BadChars']) if (reqs['BadChars'])
pinst.compatible_encoders.each { |enc| encoders = pinst.compatible_encoders
encoder = enc.new
# If the caller had a preferred encoder, try to find it and prefix it
if ((reqs['Encoder']) and
(preferred = framework.encoders[reqs['Encoder']]))
encoders.unshift(preferred)
elsif (reqs['Encoder'])
wlog("#{pinst.refname}: Failed to find preferred encoder #{reqs['Encoder']}")
end
encoders.each { |encmod|
self.encoder = encmod.new
# Try encoding with the current encoder # Try encoding with the current encoder
begin begin
p.encoded = encoder.encode(raw, reqs['BadChars']) self.encoded = self.encoder.encode(self.raw, reqs['BadChars'])
rescue rescue
wlog("#{pinst.refname}: Failed to encode payload with encoder #{encoder.refname}: #{$!}", wlog("#{pinst.refname}: Failed to encode payload with encoder #{encoder.refname}: #{$!}",
'core', LEV_1) 'core', LEV_1)
next
end end
# Minimum number of NOPs to use
min = reqs['MinNops'] || 0
# Check to see if we have enough room for the minimum requirements # Check to see if we have enough room for the minimum requirements
if ((reqs['Space']) and if ((reqs['Space']) and
(reqs['Space'] < encoded.length + min)) (reqs['Space'] < self.encoded.length + min))
wlog("#{pinst.refname}: Encoded payload version is too large with encoder #{encoder.refname}", wlog("#{pinst.refname}: Encoded payload version is too large with encoder #{encoder.refname}",
'core', LEV_1) 'core', LEV_1)
next next
end end
break
} }
# If the encoded payload is nil, raise an exception saying that we # If the encoded payload is nil, raise an exception saying that we
# suck at life. # suck at life.
if (encoded == nil) if (self.encoded == nil)
encoder = nil
raise NoEncodersSucceededError, raise NoEncodersSucceededError,
"#{pinst.refname}: All encoders failed to encode.", "#{pinst.refname}: All encoders failed to encode.",
caller caller
@ -107,11 +121,11 @@ class EncodedPayload
# If there are no bad characters, then the raw is the same as the # If there are no bad characters, then the raw is the same as the
# encoded # encoded
else else
encoded = raw self.encoded = raw
end end
# Prefix the prepend encoder value # Prefix the prepend encoder value
encoded = (reqs['PrependEncoder'] || '') + encoded self.encoded = (reqs['PrependEncoder'] || '') + self.encoded
end end
# #
@ -121,48 +135,52 @@ class EncodedPayload
min = reqs['MinNops'] || 0 min = reqs['MinNops'] || 0
space = reqs['Space'] space = reqs['Space']
nop_sled_size = 0 self.nop_sled_size = min
# Calculate the number of NOPs to pad out the buffer with based on the # Calculate the number of NOPs to pad out the buffer with based on the
# requirements. If there was a space requirement, check to see if # requirements. If there was a space requirement, check to see if
# there's any room at all left for a sled. # there's any room at all left for a sled.
if ((space) and if ((space) and
(space > encoded.length)) (space > encoded.length))
nop_sled_size = reqs['Space'] - encoded.length self.nop_sled_size = reqs['Space'] - self.encoded.length
end end
# If the maximum number of NOPs has been exceeded, wrap it back down. # If the maximum number of NOPs has been exceeded, wrap it back down.
if ((reqs['MaxNops']) and if ((reqs['MaxNops']) and
(reqs['MaxNops'] > sled_size)) (reqs['MaxNops'] > sled_size))
nop_sled_size = reqs['MaxNops'] self.nop_sled_size = reqs['MaxNops']
end end
# Now construct the actual sled # Now construct the actual sled
if (nop_sled_size > 0) if (self.nop_sled_size > 0)
pinst.compatible_nops.each { |nopmod| pinst.compatible_nops.each { |nopmod|
# Create an instance of the nop module # Create an instance of the nop module
nop = nopmod.new self.nop = nopmod.new
begin begin
nop_sled = nop.generate_sled(nop_sled_size, self.nop_sled = nop.generate_sled(nop_sled_size,
'BadChars' => reqs['BadChars'], 'BadChars' => reqs['BadChars'],
'SaveRegisters' => reqs['SaveRegisters']) 'SaveRegisters' => reqs['SaveRegisters'])
rescue rescue
self.nop = nil
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}", dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
'core', LEV_1) 'core', LEV_1)
end end
break
} }
if (nop_sled == nil) if (self.nop_sled == nil)
raise NoNopsSucceededError, raise NoNopsSucceededError,
"#{pinst.refname}: All NOP generators failed to construct sled for.", "#{pinst.refname}: All NOP generators failed to construct sled for.",
caller caller
end end
else else
nop_sled = '' self.nop_sled = ''
end end
return nop_sled return self.nop_sled
end end
# #
@ -182,6 +200,14 @@ class EncodedPayload
# The NOP sled itself # The NOP sled itself
# #
attr_reader :nop_sled attr_reader :nop_sled
#
# The encoder that was used
#
attr_reader :encoder
#
# The NOP generator that was used
#
attr_reader :nop
protected protected
@ -190,6 +216,8 @@ protected
attr_writer :nop_sled_size attr_writer :nop_sled_size
attr_writer :nop_sled attr_writer :nop_sled
attr_writer :payload attr_writer :payload
attr_writer :encoder
attr_writer :nop
# #
# The payload instance used to generate the payload # The payload instance used to generate the payload

View File

@ -116,10 +116,7 @@ class Encoder < Module
end end
end end
# Update the state with default decoder information init_state(state)
state.decoder_key_offset = decoder_key_offset
state.decoder_key_size = decoder_key_size
state.decoder_key_pack = decoder_key_pack
# Save the buffer in the encoding state # Save the buffer in the encoding state
state.badchars = badchars state.badchars = badchars
@ -200,6 +197,13 @@ class Encoder < Module
protected protected
def init_state(state)
# Update the state with default decoder information
state.decoder_key_offset = decoder_key_offset
state.decoder_key_size = decoder_key_size
state.decoder_key_pack = decoder_key_pack
end
def find_key(buf, badchars) def find_key(buf, badchars)
key_bytes = [ ] key_bytes = [ ]
cur_key = [ ] cur_key = [ ]

View File

@ -30,6 +30,8 @@ class Msf::Encoder::XorAdditiveFeedback < Msf::Encoder::Xor
key_bytes = integer_to_key_bytes(super(buf, badchars)) key_bytes = integer_to_key_bytes(super(buf, badchars))
state = Msf::EncoderState.new state = Msf::EncoderState.new
valid = false valid = false
init_state(state)
# Save the original key_bytes so we can tell if we loop around # Save the original key_bytes so we can tell if we loop around
orig_key_bytes = key_bytes.dup orig_key_bytes = key_bytes.dup

View File

@ -179,7 +179,7 @@ class Exploit < Msf::Module
reqs['SaveRegisters'] = nop_save_registers reqs['SaveRegisters'] = nop_save_registers
# Set the encoded payload to the result of the encoding process # Set the encoded payload to the result of the encoding process
encoded_payload = EncodedPayload.create(framework, real_payload, reqs) encoded_payload = EncodedPayload.create(real_payload, reqs)
return encoded_payload return encoded_payload
end end

View File

@ -107,8 +107,8 @@ protected
b_name, b_mod = b b_name, b_mod = b
# Extract the ranking between the two modules # Extract the ranking between the two modules
a_rank = a.const_defined?('Rank') ? a.const_get('Rank') : NormalRanking a_rank = a[1].const_defined?('Rank') ? a[1].const_get('Rank') : NormalRanking
b_rank = b.const_defined?('Rank') ? b.const_get('Rank') : NormalRanking b_rank = b[1].const_defined?('Rank') ? b[1].const_get('Rank') : NormalRanking
# Compare their relevant rankings. Since we want highest to lowest, # Compare their relevant rankings. Since we want highest to lowest,
# we compare b_rank to a_rank in terms of higher/lower precedence # we compare b_rank to a_rank in terms of higher/lower precedence

View File

@ -170,8 +170,8 @@ class Payload < Msf::Module
encoders = [] encoders = []
framework.encoders.each_module_ranked( framework.encoders.each_module_ranked(
'Arch' => self.arch) { |entry| 'Arch' => self.arch) { |name, mod|
encoders << entry[1] encoders << mod
} }
return encoders return encoders
@ -184,8 +184,8 @@ class Payload < Msf::Module
nops = [] nops = []
framework.nops.each_module_ranked( framework.nops.each_module_ranked(
'Arch' => self.arch) { |entry| 'Arch' => self.arch) { |name, mod|
nops << entry[1] nops << mod
} }
return nops return nops

View File

@ -12,6 +12,7 @@ class Payload
"-t" => [ true, "The output type: ruby, perl, c, or raw." ], "-t" => [ true, "The output type: ruby, perl, c, or raw." ],
"-e" => [ true, "The name of the encoder module to use." ], "-e" => [ true, "The name of the encoder module to use." ],
"-o" => [ true, "A space separated list of options in VAR=VAL format." ], "-o" => [ true, "A space separated list of options in VAR=VAL format." ],
"-s" => [ true, "NOP sled length." ],
"-h" => [ false, "Help banner." ]) "-h" => [ false, "Help banner." ])
include Msf::Ui::Console::ModuleCommandDispatcher include Msf::Ui::Console::ModuleCommandDispatcher
@ -29,9 +30,9 @@ class Payload
# Parse the arguments # Parse the arguments
encoder_name = nil encoder_name = nil
sled_size = nil
option_str = nil option_str = nil
badchars = nil badchars = nil
encoder = nil
type = "ruby" type = "ruby"
@@generate_opts.parse(args) { |opt, idx, val| @@generate_opts.parse(args) { |opt, idx, val|
@ -44,6 +45,8 @@ class Payload
encoder_name = val encoder_name = val
when '-o' when '-o'
option_str = val option_str = val
when '-s'
sled_size = val.to_i
when '-h' when '-h'
print( print(
"Usage: generate [options]\n\n" + "Usage: generate [options]\n\n" +
@ -53,21 +56,15 @@ class Payload
end end
} }
# If an encoder name was specified, try to instantiate it
if ((encoder_name) and
(encoder = framework.modules.create(encoder_name)) == nil)
print_error("Invalid encoder specified: #{encoder_name}")
return false
end
# Generate the payload # Generate the payload
begin begin
buf = Msf::Simple::Payload.generate( buf = Msf::Simple::Payload.generate(
mod, mod,
'Badchars' => badchars, 'BadChars' => badchars,
'Encoder' => encoder, 'Encoder' => encoder_name,
'Format' => type, 'Format' => type,
'OptionStr' => option_str) 'NopSledSize' => sled_size,
'OptionStr' => option_str)
rescue rescue
print_error("Payload generation failed: #{$!}") print_error("Payload generation failed: #{$!}")
return false return false

View File

@ -1,4 +1,7 @@
#!/usr/bin/ruby -I../lib #!/usr/bin/ruby
# Ghetto include for lib relative to the msfconsole script
$:.unshift(File.join(File.dirname(__FILE__), '../lib'))
require 'rex' require 'rex'
require 'msf/ui' require 'msf/ui'