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

Included in:
Msf::Handler::ReverseHttp, Msf::Payload::UUID::Options, 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_CONN =

Java

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_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


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 91

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


68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 68

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


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 42

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


126
127
128
129
130
131
132
# File 'lib/rex/payloads/meterpreter/uri_checksum.rb', line 126

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