1
1
# -*- coding: binary -*-
2
+
3
+ require 'msf/core/payload/uuid'
4
+
2
5
module Msf
3
6
module Handler
4
7
module ReverseHttp
@@ -9,67 +12,85 @@ module UriChecksum
9
12
# These are based on charset frequency
10
13
#
11
14
URI_CHECKSUM_INITW = 92 # Windows
15
+ URI_CHECKSUM_INITN = 92 # Native (same as Windows)
12
16
URI_CHECKSUM_INITP = 80 # Python
13
17
URI_CHECKSUM_INITJ = 88 # Java
14
- URI_CHECKSUM_CONN = 98
18
+ URI_CHECKSUM_CONN = 98 # Existing session
15
19
16
- #
17
- # Precalculated checkums as fallback
18
- #
19
- URI_CHECKSUM_PRECALC = [
20
- "Zjjaq" , "pIlfv" , "UvoxP" , "sqnx9" , "zvoVO" , "Pajqy" , "7ziuw" , "vecYp" , "yfHsn" , "YLzzp" ,
21
- "cEzvr" , "abmri" , "9tvwr" , "vTarp" , "ocrgc" , "mZcyl" , "xfcje" , "nihqa" , "40F17" , "zzTWt" ,
22
- "E3192" , "wygVh" , "pbqij" , "rxdVs" , "ajtsf" , "wvuOh" , "hwRwr" , "pUots" , "rvzoK" , "vUwby" ,
23
- "tLzyk" , "zxbuV" , "niaoy" , "ukxtU" , "vznoU" , "zuxyC" , "ymvag" , "Jxtxw" , "404KC" , "DE563" ,
24
- "0A7G9" , "yorYv" , "zzuqP" , "czhwo" , "949N8" , "a1560" , "5A2S3" , "Q652A" , "KR201" , "uixtg" ,
25
- "U0K02" , "4EO56" , "H88H4" , "5M8E6" , "zudkx" , "ywlsh" , "luqmy" , "09S4I" , "L0GG0" , "V916E" ,
26
- "KFI11" , "A4BN8" , "C3E2Q" , "UN804" , "E75HG" , "622eB" , "1OZ71" , "kynyx" , "0RE7F" , "F8CR2" ,
27
- "1Q2EM" , "txzjw" , "5KD1S" , "GLR40" , "11BbD" , "MR8B2" , "X4V55" , "W994P" , "13d2T" , "6J4AZ" ,
28
- "HD2EM" , "766bL" , "8S4MF" , "MBX39" , "UJI57" , "eIA51" , "9CZN2" , "WH6AA" , "a6BF9" , "8B1Gg" ,
29
- "J2N6Z" , "144Kw" , "7E37v" , "9I7RR" , "PE6MF" , "K0c4M" , "LR3IF" , "38p3S" , "39ab3" , "O0dO1" ,
30
- "k8H8A" , "0Fz3B" , "o1PE1" , "h7OI0" , "C1COb" , "bMC6A" , "8fU4C" , "3IMSO" , "8DbFH" , "2YfG5" ,
31
- "bEQ1E" , "MU6NI" , "UCENE" , "WBc0E" , "T1ATX" , "tBL0A" , "UGPV2" , "j3CLI" , "7FXp1" , "yN07I" ,
32
- "YE6k9" , "KTMHE" , "a7VBJ" , "0Uq3R" , "70Ebn" , "H2PqB" , "83edJ" , "0w5q2" , "72djI" , "wA5CQ" ,
33
- "KF0Ix" , "i7AZH" , "M9tU5" , "Hs3RE" , "F9m1i" , "7ecBF" , "zS31W" , "lUe21" , "IvCS5" , "j97nC" ,
34
- "CNtR5" , "1g8gV" , "7KwNG" , "DB7hj" , "ORFr7" , "GCnUD" , "K58jp" , "5lKo8" , "GPIdP" , "oMIFJ" ,
35
- "2xYb1" , "LQQPY" , "FGQlN" , "l5COf" , "dA3Tn" , "v9RWC" , "VuAGI" , "3vIr9" , "aO3zA" , "CIfx5" ,
36
- "Gk6Uc" , "pxL94" , "rKYJB" , "TXAFp" , "XEOGq" , "aBOiJ" , "qp6EJ" , "YGbq4" , "dR8Rh" , "g0SVi" ,
37
- "iMr6L" , "HMaIl" , "yOY1Z" , "UXr5Y" , "PJdz6" , "OQdt7" , "EmZ1s" , "aLIVe" , "cIeo2" , "mTTNP" ,
38
- "eVKy5" , "hf5Co" , "gFHzG" , "VhTWN" , "DvAWf" , "RgFJp" , "MoaXE" , "Mrq4W" , "hRQAp" , "hAzYA" ,
39
- "oOSWV" , "UKMme" , "oP0Zw" , "Mxd6b" , "RsRCh" , "dlk7Q" , "YU6zf" , "VPDjq" , "ygERO" , "dZZcL" ,
40
- "dq5qM" , "LITku" , "AZIxn" , "bVwPL" , "jGvZK" , "XayKP" , "rTYVY" , "Vo2ph" , "dwJYR" , "rLTlS" ,
41
- "BmsfJ" , "Dyv1o" , "j9Hvs" , "w0wVa" , "iDnBy" , "uKEgk" , "uosI8" , "2yjuO" , "HiOue" , "qYi4t" ,
42
- "7nalj" , "ENekz" , "rxca0" , "rrePF" , "cXmtD" , "Xlr2y" , "S7uxk" , "wJqaP" , "KmYyZ" , "cPryG" ,
43
- "kYcwH" , "FtDut" , "xm1em" , "IaymY" , "fr6ew" , "ixDSs" , "YigPs" , "PqwBs" , "y2rkf" , "vwaTM" ,
44
- "aq7wp" , "fzc4z" , "AyzmQ" , "epJbr" , "culLd" , "CVtnz" , "tPjPx" , "nfry8" , "Nkpif" , "8kuzg" ,
45
- "zXvz8" , "oVQly" , "1vpnw" , "jqaYh" , "2tztj" , "4tslx"
20
+ # Mapping between checksums and modes
21
+ URI_CHECKSUM_MODES = Hash [
22
+ URI_CHECKSUM_INITN , :init_native ,
23
+ URI_CHECKSUM_INITP , :init_python ,
24
+ URI_CHECKSUM_INITJ , :init_java ,
25
+ URI_CHECKSUM_CONN , :connect
46
26
]
47
27
28
+ URI_CHECKSUM_MIN_LEN = 5
29
+
30
+ # Limit how long :connect URLs are to stay within 256 bytes when including
31
+ # the hostname, colon, port, and leading slash
32
+ URI_CHECKSUM_CONN_MAX_LEN = 128
33
+
34
+ URI_CHECKSUM_UUID_MIN_LEN = URI_CHECKSUM_MIN_LEN + Msf ::Payload ::UUID ::UriLength
35
+
48
36
# Map "random" URIs to static strings, allowing us to randomize
49
37
# the URI sent in the first request.
50
38
#
51
- # @param uri_match [String] The URI string to convert back to the original static value
52
- # @return [String] The static URI value derived from the checksum
53
- def process_uri_resource ( uri_match )
54
-
55
- # This allows 'random' strings to be used as markers for
56
- # the INIT and CONN request types, based on a checksum
57
- uri_strip . sub! ( /^\/ / , '' )
58
- uri_check = Rex ::Text . checksum8 ( uri_strip )
59
-
60
- # Match specific checksums and map them to static URIs
61
- case uri_check
62
- when URI_CHECKSUM_INITW
63
- uri_match = "/INITM"
64
- when URI_CHECKSUM_INITP
65
- uri_match = "/INITPY"
66
- when URI_CHECKSUM_INITJ
67
- uri_match = "/INITJM"
68
- when URI_CHECKSUM_CONN
69
- uri_match = "/CONN_" + ( uri_conn || Rex ::Text . rand_text_base64url ( 16 ) )
39
+ # @param uri_match [String] The URI string from the HTTP request
40
+ # @return [Hash] The attributes extracted from the URI
41
+ def process_uri_resource ( uri )
42
+
43
+ # Ignore non-base64url characters in the URL
44
+ uri_bare = uri . gsub ( /[^a-zA-Z0-9_\- ]/ , '' )
45
+
46
+ # Figure out the mode based on the checksum
47
+ uri_csum = Rex ::Text . checksum8 ( uri_bare )
48
+
49
+ uri_uuid = nil
50
+
51
+ if uri_bare . length >= URI_CHECKSUM_UUID_MIN_LEN
52
+ uri_uuid =
53
+ Msf ::Payload ::UUID . payload_uuid_parse_raw (
54
+ Rex ::Text . decode_base64url (
55
+ uri_bare [ 0 , Msf ::Payload ::UUID ::UriLength ] ) )
56
+
57
+ # Verify the uri_uuid fields and unset everything but
58
+ # the unique ID itself unless it looks wonky.
59
+ if uri_uuid [ :timestamp ] > ( Time . now . utc . to_i + ( 24 *3600 *365 ) ) ||
60
+ uri_uuid [ :timestamp ] < ( Time . now . utc . to_i - ( 24 *3600 *365 ) ) ||
61
+ ( uri_uuid [ :arch ] . nil? && uri_uuid [ :platform ] . nil? )
62
+ uri_uuid = { puid : uri_uuid [ :puid ] }
63
+ end
70
64
end
71
65
72
- uri_match
66
+ uri_mode = URI_CHECKSUM_MODES [ uri_csum ]
67
+
68
+ # Return a hash of URI attributes to the caller
69
+ {
70
+ uri : uri_bare ,
71
+ sum : uri_csum ,
72
+ uuid : uri_uuid ,
73
+ mode : uri_mode
74
+ }
75
+ end
76
+
77
+ # Create a URI that matches the :connect mode with optional UUID, Arch, and Platform
78
+ #
79
+ # @param uuid [Hash] An optional hash with the UUID parameters
80
+ # @param arch [String] An optional architecture name to use if no UUID is provided
81
+ # @param platform [String] An optional platform name to use if no UUID is provided
82
+ # @return [String] The URI string that checksums to the given value
83
+ def generate_uri_connect_uuid ( uuid = nil , arch = nil , platform = nil )
84
+ curl_uri_len = URI_CHECKSUM_UUID_MIN_LEN +rand ( URI_CHECKSUM_CONN_MAX_LEN -URI_CHECKSUM_UUID_MIN_LEN )
85
+ curl_prefix = Rex ::Text . encode_base64url (
86
+ Msf ::Payload ::UUID . payload_uuid_generate_raw (
87
+ uuid : uuid [ :puid ] ,
88
+ arch : uuid [ :arch ] || arch ,
89
+ platform : uuid [ :platform ] || platform ,
90
+ timestamp : uuid [ :timestamp ] ) )
91
+
92
+ # Pad out the URI and make the checksum match :connect
93
+ "/" + generate_uri_checksum ( URI_CHECKSUM_CONN , curl_uri_len , curl_prefix )
73
94
end
74
95
75
96
# Create an arbitrary length URI that matches a given checksum
@@ -83,13 +104,13 @@ def generate_uri_checksum(sum, len=5, prefix="")
83
104
# Lengths shorter than 4 bytes are unable to match all possible checksums
84
105
# Lengths of exactly 4 are relatively slow to find for high checksum values
85
106
# Lengths of 5 or more bytes find a matching checksum fairly quickly (~80ms)
86
- if len < 5
87
- raise ArgumentError , "Length must be 5 bytes or greater"
107
+ if len < URI_CHECKSUM_MIN_LEN
108
+ raise ArgumentError , "Length must be #{ URI_CHECKSUM_MIN_LEN } bytes or greater"
88
109
end
89
110
90
111
gen_len = len -prefix . length
91
- if gen_len < 5
92
- raise ArgumentError , "Prefix must be at least 5 bytes smaller than total length"
112
+ if gen_len < URI_CHECKSUM_MIN_LEN
113
+ raise ArgumentError , "Prefix must be at least {URI_CHECKSUM_MIN_LEN} bytes smaller than total length"
93
114
end
94
115
95
116
# Brute force a matching checksum for shorter URIs
0 commit comments