module Rex::Payloads::Meterpreter::UriChecksum

Constants

URI_CHECKSUM_CONN
URI_CHECKSUM_CONN_MAX_LEN

Limit how long :connect URLs are to stay within 256 bytes when including the hostname, colon, port, and leading slash

URI_CHECKSUM_INITJ
URI_CHECKSUM_INITN
URI_CHECKSUM_INITP
URI_CHECKSUM_INITW

Define 8-bit checksums for matching URLs These are based on charset frequency

URI_CHECKSUM_INIT_CONN
URI_CHECKSUM_MIN_LEN
URI_CHECKSUM_MODES

Mapping between checksums and modes

URI_CHECKSUM_UUID_MIN_LEN

Public Instance Methods

generate_uri_checksum(sum, len=5, prefix="") click to toggle source

Create an arbitrary length URI that matches a given checksum

@param sum [Fixnum] The checksum value that the generated URI should match @param len [Fixnum] The length of the URI to generate @param prefix [String] The optional prefix to use to build the URI @return [String] The URI string that checksums to the given value

# File lib/rex/payloads/meterpreter/uri_checksum.rb, line 90
def generate_uri_checksum(sum, len=5, prefix="")
  # Lengths shorter than 4 bytes are unable to match all possible checksums
  # Lengths of exactly 4 are relatively slow to find for high checksum values
  # Lengths of 5 or more bytes find a matching checksum fairly quickly (~80ms)
  if len < URI_CHECKSUM_MIN_LEN
    raise ArgumentError, "Length must be #{URI_CHECKSUM_MIN_LEN} bytes or greater"
  end

  gen_len = len-prefix.length
  if gen_len < URI_CHECKSUM_MIN_LEN
    raise ArgumentError, "Prefix must be at least {URI_CHECKSUM_MIN_LEN} bytes smaller than total length"
  end

  # Brute force a matching checksum for shorter URIs
  if gen_len < 40
    loop do
      uri = prefix + Rex::Text.rand_text_base64url(gen_len)
      return uri if Rex::Text.checksum8(uri) == sum
    end
  end

  # The rand_text_base64url() method becomes a bottleneck at around 40 bytes
  # Calculating a static prefix flattens out the average runtime for longer URIs
  prefix << Rex::Text.rand_text_base64url(gen_len-20)

  loop do
    uri = prefix + Rex::Text.rand_text_base64url(20)
    return uri if Rex::Text.checksum8(uri) == sum
  end
end
generate_uri_uuid(sum, uuid, len=nil) click to toggle source

Create a URI that matches the specified checksum and payload uuid

@param sum [Fixnum] A checksum mode value to use for the generated url @param uuid [Msf::Payload::UUID] A valid UUID object @param len [Fixnum] An optional URI length value, including the leading slash @return [String] The URI string for connections

# File lib/rex/payloads/meterpreter/uri_checksum.rb, line 67
def generate_uri_uuid(sum, uuid, len=nil)
  curl_uri_len = URI_CHECKSUM_UUID_MIN_LEN+rand(URI_CHECKSUM_CONN_MAX_LEN-URI_CHECKSUM_UUID_MIN_LEN)
  curl_prefix  = uuid.to_uri

  if len
    # Subtract a byte to take into account the leading /
    curl_uri_len = len - 1
  end

  if curl_uri_len < URI_CHECKSUM_UUID_MIN_LEN
    raise ArgumentError, "Length must be #{URI_CHECKSUM_UUID_MIN_LEN+1} bytes or greater"
  end

  # Pad out the URI and make the checksum match the specified sum
  "/" + generate_uri_checksum(sum, curl_uri_len, curl_prefix)
end
process_uri_resource(uri) click to toggle source

Map “random” URIs to static strings, allowing us to randomize the URI sent in the first request.

@param uri [String] The URI string from the HTTP request @return [Hash] The attributes extracted from the URI

# File lib/rex/payloads/meterpreter/uri_checksum.rb, line 41
def process_uri_resource(uri)

  # Ignore non-base64url characters in the URL
  uri_bare = uri.gsub(/[^a-zA-Z0-9_\-]/, '')

  # Figure out the mode based on the checksum
  uri_csum = Rex::Text.checksum8(uri_bare)

  # Extract the UUID if the URI is long enough
  uri_uuid = nil
  if uri_bare.length >= URI_CHECKSUM_UUID_MIN_LEN
    uri_uuid = Msf::Payload::UUID.new(uri: uri_bare)
  end

  uri_mode = URI_CHECKSUM_MODES[uri_csum]

  # Return a hash of URI attributes
  { uri: uri_bare, sum: uri_csum, uuid: uri_uuid, mode: uri_mode }
end
uri_checksum_lookup(mode) click to toggle source

Return the numerical checksum for a given mode symbol

@param mode [Symbol] The mode symbol to lookup (:connect, :init_native, :init_python, :init_java) @return [Fixnum] The URI checksum value corresponding with the mode

# File lib/rex/payloads/meterpreter/uri_checksum.rb, line 125
def uri_checksum_lookup(mode)
  sum = URI_CHECKSUM_MODES.keys.select{|ksum| URI_CHECKSUM_MODES[ksum] == mode}.first
  unless sum
    raise ArgumentError, "Unknown checksum mode: #{mode}"
  end
  sum
end