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

View File

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

View File

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

View File

@ -116,10 +116,7 @@ class Encoder < Module
end
end
# 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
init_state(state)
# Save the buffer in the encoding state
state.badchars = badchars
@ -200,6 +197,13 @@ class Encoder < Module
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)
key_bytes = [ ]
cur_key = [ ]

View File

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

View File

@ -179,7 +179,7 @@ class Exploit < Msf::Module
reqs['SaveRegisters'] = nop_save_registers
# 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
end

View File

@ -107,8 +107,8 @@ protected
b_name, b_mod = b
# Extract the ranking between the two modules
a_rank = a.const_defined?('Rank') ? a.const_get('Rank') : NormalRanking
b_rank = b.const_defined?('Rank') ? b.const_get('Rank') : NormalRanking
a_rank = a[1].const_defined?('Rank') ? a[1].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,
# we compare b_rank to a_rank in terms of higher/lower precedence

View File

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

View File

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