Module: Rex::Payloads::Meterpreter::UriChecksum

Included in:
Msf::Handler::ReverseHttp, Msf::Payload::UUID::Options, Config, Rex::Post::Meterpreter::ClientCore
Defined in:
lib/rex/payloads/meterpreter/uri_checksum.rb

Constant Summary collapse

URI_CHECKSUM_INITW =

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

92
URI_CHECKSUM_INITN =

Windows

92
URI_CHECKSUM_INITP =

Native (same as Windows)

80
URI_CHECKSUM_INITJ =

Python

88
URI_CHECKSUM_INITPH =

Java

84
URI_CHECKSUM_CONN =

PHP

98
URI_CHECKSUM_INIT_CONN =

Existing session

95
URI_CHECKSUM_MODES =

Mapping between checksums and modes

Hash[
  URI_CHECKSUM_INITN,      :init_native,
  URI_CHECKSUM_INITP,      :init_python,
  URI_CHECKSUM_INITJ,      :init_java,
  URI_CHECKSUM_INITPH,     :init_php,
  URI_CHECKSUM_INIT_CONN,  :init_connect,
  URI_CHECKSUM_CONN,       :connect
]
URI_CHECKSUM_MIN_LEN =
5
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

128
URI_CHECKSUM_UUID_MIN_LEN =
URI_CHECKSUM_MIN_LEN + Msf::Payload::UUID::UriLength

Instance Method Summary collapse

Instance Method Details

#generate_uri_checksum(sum, len = 5, prefix = "") ⇒ String

Create an arbitrary length URI that matches a given checksum

Parameters:

  • sum (Integer)

    The checksum value that the generated URI should match

  • len (Integer) (defaults to: 5)

    The length of the URI to generate

  • prefix (String) (defaults to: "")

    The optional prefix to use to build the URI

Returns:

  • (String)

    The URI string that checksums to the given value



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 117

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) ⇒ String

Create a URI that matches the specified checksum and payload uuid

Parameters:

  • sum (Integer)

    A checksum mode value to use for the generated url

  • uuid (Msf::Payload::UUID)

    A valid UUID object

  • len (Integer) (defaults to: nil)

    An optional URI length value, including the leading slash

Returns:

  • (String)

    The URI string for connections



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 94

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

Map “random” cookies to static strings.

Parameters:

  • cookie (String)

    The Cookie header string from the HTTP request.

Returns:

  • (Hash)

    The attributes extracted from the URI



85
86
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 85

def process_cookie_resource(cookie)
end

#process_query_string_resource(query_string) ⇒ Hash

Map “random” get params to static strings.

Parameters:

  • The (String)

    query string from the HTTP request.

Returns:

  • (Hash)

    The attributes extracted from the URI



78
79
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 78

def process_query_string_resource(query_string)
end

#process_uri_resource(uri) ⇒ Hash

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

Parameters:

  • uri (String)

    The URI string from the HTTP request

Returns:

  • (Hash)

    The attributes extracted from the URI



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 59

def process_uri_resource(uri)
  # look for the UUID anywhere in the given URI, excluding the query string
  uri.split('?')[0].split('/').each {|u|
    # Ignore non-base64url characters in the URL
    uri_bare = u.gsub(/[^a-zA-Z0-9_\-]/, '')
    h = process_uuid_string(uri_bare)
    return h if h[:uuid]
  }

  # No embedded UUID was found in any URI segment; fall back to a
  # checksum-based mode lookup against the whole URI, e.g. for the
  # INITW/INITJ/CONN handshake requests which don't carry a UUID yet.
  process_uuid_string(uri.gsub(/[^a-zA-Z0-9_\-]/, ''))
end

#process_uuid_string(uri_bare) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 38

def process_uuid_string(uri_bare)
  # 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) ⇒ Integer

Return the numerical checksum for a given mode symbol

Parameters:

  • mode (Symbol)

    The mode symbol to lookup (:connect, :init_native, :init_python, :init_java)

Returns:

  • (Integer)

    The URI checksum value corresponding with the mode



152
153
154
155
156
157
158
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 152

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