Skip to content

Commit 858d9b1

Browse files
author
HD Moore
committed
Introduce Rex::Text.(en|de)code_base64url and use it for uri_checksum
1 parent c0bf51e commit 858d9b1

File tree

2 files changed

+34
-32
lines changed

2 files changed

+34
-32
lines changed

lib/msf/core/handler/reverse_http/uri_checksum.rb

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ def process_uri_resource(uri_match)
5454

5555
# This allows 'random' strings to be used as markers for
5656
# the INIT and CONN request types, based on a checksum
57-
uri_strip, uri_conn = uri_match.split('_', 2)
5857
uri_strip.sub!(/^\//, '')
5958
uri_check = Rex::Text.checksum8(uri_strip)
6059

@@ -67,57 +66,46 @@ def process_uri_resource(uri_match)
6766
when URI_CHECKSUM_INITJ
6867
uri_match = "/INITJM"
6968
when URI_CHECKSUM_CONN
70-
uri_match = "/CONN_" + ( uri_conn || Rex::Text.rand_text_alphanumeric(16) )
69+
uri_match = "/CONN_" + ( uri_conn || Rex::Text.rand_text_base64url(16) )
7170
end
7271

7372
uri_match
7473
end
7574

76-
# Create a URI that matches a given checksum
77-
#
78-
# @param sum [Fixnum] The checksum value you are trying to create a URI for
79-
# @param len [Fixnum] An optional length value for the created URI
80-
# @return [String] The URI string that checksums to the given value
81-
def generate_uri_checksum(sum,len=nil)
82-
return generate_uri_checksum_with_length(sum, len) if len
83-
84-
chk = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
85-
32.times do
86-
uri = Rex::Text.rand_text_alphanumeric(3)
87-
chk.sort_by {rand}.each do |x|
88-
return(uri + x) if Rex::Text.checksum8(uri + x) == sum
89-
end
90-
end
91-
92-
# Otherwise return one of the pre-calculated strings
93-
return URI_CHECKSUM_PRECALC[sum]
94-
end
95-
9675
# Create an arbitrary length URI that matches a given checksum
9776
#
98-
# @param sum [Fixnum] The checksum value you are trying to create a URI for
99-
# @param len [Fixnum] The length of the created URI
77+
# @param sum [Fixnum] The checksum value that the generated URI should match
78+
# @param len [Fixnum] The length of the URI to generate
79+
# @param prefix [String] The optional prefix to use to build the URI
10080
# @return [String] The URI string that checksums to the given value
101-
def generate_uri_checksum_with_length(sum, len)
81+
def generate_uri_checksum(sum, len=5, prefix="")
82+
10283
# Lengths shorter than 4 bytes are unable to match all possible checksums
10384
# Lengths of exactly 4 are relatively slow to find for high checksum values
10485
# Lengths of 5 or more bytes find a matching checksum fairly quickly (~80ms)
105-
raise ArgumentError, "Length must be 5 bytes or greater" if len < 5
86+
if len < 5
87+
raise ArgumentError, "Length must be 5 bytes or greater"
88+
end
89+
90+
gen_len = len-prefix.length
91+
if gen_len < 5
92+
raise ArgumentError, "Prefix must be at least 5 bytes smaller than total length"
93+
end
10694

107-
# Funny enough, this was more efficient than calculating checksum offsets
108-
if len < 40
95+
# Brute force a matching checksum for shorter URIs
96+
if gen_len < 40
10997
loop do
110-
uri = Rex::Text.rand_text_alphanumeric(len)
98+
uri = prefix + Rex::Text.rand_text_base64url(gen_len)
11199
return uri if Rex::Text.checksum8(uri) == sum
112100
end
113101
end
114102

115-
# The rand_text_alphanumeric() method becomes a bottleneck at around 40 bytes
103+
# The rand_text_base64url() method becomes a bottleneck at around 40 bytes
116104
# Calculating a static prefix flattens out the average runtime for longer URIs
117-
prefix = Rex::Text.rand_text_alphanumeric(len-20)
105+
prefix << Rex::Text.rand_text_base64url(gen_len-20)
118106

119107
loop do
120-
uri = prefix + Rex::Text.rand_text_alphanumeric(20)
108+
uri = prefix + Rex::Text.rand_text_base64url(20)
121109
return uri if Rex::Text.checksum8(uri) == sum
122110
end
123111
end

lib/rex/text.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ module Text
4343
LowerAlpha = "abcdefghijklmnopqrstuvwxyz"
4444
Numerals = "0123456789"
4545
Base32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
46+
Base64 = UpperAlpha + LowerAlpha + Numerals + '+/'
47+
Base64Url = UpperAlpha + LowerAlpha + Numerals + '-_'
4648
Alpha = UpperAlpha + LowerAlpha
4749
AlphaNumeric = Alpha + Numerals
4850
HighAscii = [*(0x80 .. 0xff)].pack("C*")
@@ -1288,6 +1290,18 @@ def self.rand_text_highascii(len, bad='')
12881290
rand_base(len, bad, *foo )
12891291
end
12901292

1293+
# Generate random bytes of base64 data
1294+
def self.rand_text_base64(len, bad='')
1295+
foo = Base64.unpack('C*').map{ |c| c.chr }
1296+
rand_base(len, bad, *foo )
1297+
end
1298+
1299+
# Generate random bytes of base64url data
1300+
def self.rand_text_base64url(len, bad='')
1301+
foo = Base64Url.unpack('C*').map{ |c| c.chr }
1302+
rand_base(len, bad, *foo )
1303+
end
1304+
12911305
# Generate a random GUID
12921306
#
12931307
# @example

0 commit comments

Comments
 (0)