librex 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -5
- data/Rakefile +26 -0
- data/lib/rex/compat.rb +1 -1
- data/lib/rex/exploitation/javascriptosdetect.rb +125 -62
- data/lib/rex/file.rb +15 -0
- data/lib/rex/io/stream.rb +1 -1
- data/lib/rex/parser/nmap_xml.rb +6 -0
- data/lib/rex/poly/block.rb +9 -0
- data/lib/rex/post/meterpreter/client.rb +0 -8
- data/lib/rex/post/meterpreter/extensions/priv/priv.rb +6 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_advapi32.rb +49 -35
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb +26 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +9 -2
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +630 -0
- data/lib/rex/post/meterpreter/packet.rb +3 -1
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +143 -57
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +6 -0
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +9 -3
- data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +6 -4
- data/lib/rex/proto.rb +1 -0
- data/lib/rex/proto/dhcp/server.rb +4 -2
- data/lib/rex/proto/http/packet.rb +5 -6
- data/lib/rex/proto/ntlm.rb +7 -0
- data/lib/rex/proto/ntlm.rb.ut.rb +177 -0
- data/lib/rex/proto/ntlm/base.rb +326 -0
- data/lib/rex/proto/ntlm/constants.rb +74 -0
- data/lib/rex/proto/ntlm/crypt.rb +340 -0
- data/lib/rex/proto/ntlm/exceptions.rb +9 -0
- data/lib/rex/proto/ntlm/message.rb +533 -0
- data/lib/rex/proto/ntlm/utils.rb +358 -0
- data/lib/rex/proto/smb/client.rb +548 -86
- data/lib/rex/proto/smb/client.rb.ut.rb +4 -4
- data/lib/rex/proto/smb/constants.rb +7 -24
- data/lib/rex/proto/smb/crypt.rb +12 -71
- data/lib/rex/proto/smb/exceptions.rb +12 -0
- data/lib/rex/proto/smb/simpleclient.rb +17 -5
- data/lib/rex/proto/smb/utils.rb +3 -460
- data/lib/rex/proto/tftp/server.rb +2 -2
- data/lib/rex/script/base.rb +2 -2
- data/lib/rex/socket.rb +12 -0
- data/lib/rex/socket.rb.ut.rb +31 -10
- data/lib/rex/socket/ssl_tcp_server.rb.ut.rb +15 -5
- data/lib/rex/text.rb +55 -4
- data/lib/rex/ui/output.rb +0 -2
- data/lib/rex/ui/text/dispatcher_shell.rb +95 -10
- data/lib/rex/ui/text/output/buffer.rb +0 -4
- data/lib/rex/ui/text/shell.rb +8 -0
- data/lib/rex/ui/text/table.rb +21 -1
- metadata +15 -19
- data/lib/rex/proto/smb/crypt.rb.ut.rb +0 -20
@@ -50,7 +50,7 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
|
|
50
50
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
51
51
|
|
52
52
|
# puts "[*] Authenticating with NTLMv2..."
|
53
|
-
ok = c.
|
53
|
+
ok = c.session_setup_with_ntlmssp($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
|
54
54
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
55
55
|
assert_not_equal(c.auth_user_id, 0)
|
56
56
|
|
@@ -122,7 +122,7 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
|
|
122
122
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
123
123
|
|
124
124
|
# puts "[*] Authenticating with NTLMv2..."
|
125
|
-
ok = c.
|
125
|
+
ok = c.session_setup_with_ntlmssp($_REX_TEXT_SMB_USER, $_REX_TEXT_SMB_PASS)
|
126
126
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
127
127
|
assert_not_equal(c.auth_user_id, 0)
|
128
128
|
|
@@ -171,11 +171,11 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase
|
|
171
171
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
172
172
|
|
173
173
|
# puts "[*] Authenticating with NTLMv2..."
|
174
|
-
ok = c.
|
174
|
+
ok = c.session_setup_with_ntlmssp
|
175
175
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
176
176
|
|
177
177
|
# puts "[*] Authenticating with NTLMv1..."
|
178
|
-
ok = c.
|
178
|
+
ok = c.session_setup_no_ntlmssp
|
179
179
|
assert_kind_of(Rex::Struct2::CStruct, ok)
|
180
180
|
|
181
181
|
# puts "[*] Authenticating with clear text passwords..."
|
@@ -263,7 +263,11 @@ FILE_VOLUME_IS_COMPRESSED = 0x00008000
|
|
263
263
|
|
264
264
|
|
265
265
|
# SMB Error Codes
|
266
|
-
|
266
|
+
SMB_STATUS_SUCCESS = 0x00000000
|
267
|
+
SMB_ERROR_BUFFER_OVERFLOW = 0x80000005
|
268
|
+
SMB_STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016
|
269
|
+
SMB_STATUS_ACCESS_DENIED = 0xC0000022
|
270
|
+
SMB_STATUS_LOGON_FAILURE = 0xC000006D
|
267
271
|
|
268
272
|
# SMB Dialect Compatibility
|
269
273
|
DIALECT = {}
|
@@ -548,7 +552,7 @@ SMB_SETUP_NTLMV1_HDR_PKT = Rex::Struct2::CStructTemplate.new(
|
|
548
552
|
SMB_SETUP_NTLMV1_PKT = self.make_nbs(SMB_SETUP_NTLMV1_HDR_PKT)
|
549
553
|
|
550
554
|
|
551
|
-
# A SMB template for SMB Session Setup requests (
|
555
|
+
# A SMB template for SMB Session Setup requests (When extended security is being used)
|
552
556
|
SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new(
|
553
557
|
[ 'template', 'SMB', SMB_HDR ],
|
554
558
|
[ 'uint8', 'AndX', 0 ],
|
@@ -569,7 +573,7 @@ SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new(
|
|
569
573
|
SMB_SETUP_NTLMV2_PKT = self.make_nbs(SMB_SETUP_NTLMV2_HDR_PKT)
|
570
574
|
|
571
575
|
|
572
|
-
# A SMB template for SMB Session Setup responses (
|
576
|
+
# A SMB template for SMB Session Setup responses (When extended security is being used)
|
573
577
|
SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new(
|
574
578
|
[ 'template', 'SMB', SMB_HDR ],
|
575
579
|
[ 'uint8', 'AndX', 0 ],
|
@@ -1035,27 +1039,6 @@ SMB_SEARCH_HDR_PKT = Rex::Struct2::CStructTemplate.new(
|
|
1035
1039
|
)
|
1036
1040
|
SMB_SEARCH_PKT = self.make_nbs(SMB_SEARCH_HDR_PKT)
|
1037
1041
|
|
1038
|
-
# NTLMSSP Message Flags
|
1039
|
-
NEGOTIATE_UNICODE = 0x00000001 # Only set if Type 1 contains it - this or oem, not both
|
1040
|
-
NEGOTIATE_OEM = 0x00000002 # Only set if Type 1 contains it - this or unicode, not both
|
1041
|
-
REQUEST_TARGET = 0x00000004 # If set in Type 1, must return domain or server
|
1042
|
-
NEGOTIATE_SIGN = 0x00000010 # Session signature required
|
1043
|
-
NEGOTIATE_SEAL = 0x00000020 # Session seal required
|
1044
|
-
NEGOTIATE_LMKEY = 0x00000080 # LM Session Key should be used for signing and sealing
|
1045
|
-
NEGOTIATE_NTLM = 0x00000200 # NTLM auth is supported
|
1046
|
-
NEGOTIATE_ANONYMOUS = 0x00000800 # Anonymous context used
|
1047
|
-
NEGOTIATE_DOMAIN = 0x00001000 # Sent in Type1, client gives domain info
|
1048
|
-
NEGOTIATE_WORKSTATION = 0x00002000 # Sent in Type1, client gives workstation info
|
1049
|
-
NEGOTIATE_LOCAL_CALL = 0x00004000 # Server and client are on same machine
|
1050
|
-
NEGOTIATE_ALWAYS_SIGN = 0x00008000 # Add signatures to packets
|
1051
|
-
TARGET_TYPE_DOMAIN = 0x00010000 # If REQUEST_TARGET, we're adding the domain name
|
1052
|
-
TARGET_TYPE_SERVER = 0x00020000 # If REQUEST_TARGET, we're adding the server name
|
1053
|
-
TARGET_TYPE_SHARE = 0x00040000 # Supposed to denote "a share" but for a webserver?
|
1054
|
-
NEGOTIATE_NTLM2_KEY = 0x00080000 # NTLMv2 Signature and Key exchanges
|
1055
|
-
NEGOTIATE_TARGET_INFO = 0x00800000 # Server set when sending Target Information Block
|
1056
|
-
NEGOTIATE_128 = 0x20000000 # 128-bit encryption supported
|
1057
|
-
NEGOTIATE_KEY_EXCH = 0x40000000 # Client will supply encrypted master key in Session Key field of Type3 msg
|
1058
|
-
NEGOTIATE_56 = 0x80000000 # 56-bit encryption supported
|
1059
1042
|
|
1060
1043
|
end
|
1061
1044
|
end
|
data/lib/rex/proto/smb/crypt.rb
CHANGED
@@ -12,82 +12,23 @@ class Crypt
|
|
12
12
|
@@loaded_openssl = true
|
13
13
|
rescue ::Exception
|
14
14
|
end
|
15
|
-
|
16
|
-
begin
|
17
|
-
|
18
|
-
def self.lanman_des(pass, chal)
|
19
|
-
e_p24( [ e_p16( [ pass.upcase()[0,14] ].pack('a14') ) ].pack('a21'), chal)
|
20
|
-
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
des_hash(stat, pass[0,7]) << des_hash(stat, pass[7,7])
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.e_p24(pass, chal)
|
28
|
-
des_hash(chal, pass[0,7]) << des_hash(chal, pass[7,7]) << des_hash(chal, pass[14,7])
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.des_hash(data, ckey)
|
16
|
+
# Return a signed SMB packet
|
17
|
+
def self.sign_smb_packet(mackey, sequence_counter, data)
|
32
18
|
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
def self.des_56_to_64(ckey56s)
|
40
|
-
ckey64 = []
|
41
|
-
ckey56 = ckey56s.unpack('C*')
|
42
|
-
ckey64[0] = ckey56[0]
|
43
|
-
ckey64[1] = ((ckey56[0] << 7) & 0xFF) | (ckey56[1] >> 1)
|
44
|
-
ckey64[2] = ((ckey56[1] << 6) & 0xFF) | (ckey56[2] >> 2)
|
45
|
-
ckey64[3] = ((ckey56[2] << 5) & 0xFF) | (ckey56[3] >> 3)
|
46
|
-
ckey64[4] = ((ckey56[3] << 4) & 0xFF) | (ckey56[4] >> 4)
|
47
|
-
ckey64[5] = ((ckey56[4] << 3) & 0xFF) | (ckey56[5] >> 5)
|
48
|
-
ckey64[6] = ((ckey56[5] << 2) & 0xFF) | (ckey56[6] >> 6)
|
49
|
-
ckey64[7] = (ckey56[6] << 1) & 0xFF
|
50
|
-
ckey64.pack('C*')
|
19
|
+
seq = Rex::Text::pack_int64le(sequence_counter)
|
20
|
+
netbios_hdr = data.slice!(0,4)
|
21
|
+
data[14,8] = seq
|
22
|
+
signature = OpenSSL::Digest::MD5.digest(mackey + data)[0,8]
|
23
|
+
data[14,8] = signature
|
24
|
+
netbios_hdr + data
|
51
25
|
end
|
52
26
|
|
53
|
-
def self.
|
54
|
-
|
27
|
+
def self.is_signature_correct?(mackey, sequence_counter, data)
|
28
|
+
signature1 = data[18,8]
|
29
|
+
signature2 = sign_smb_packet(mackey, sequence_counter, data.dup)[18,8]
|
30
|
+
return signature1 == signature2
|
55
31
|
end
|
56
|
-
|
57
|
-
def self.md4_hash(data)
|
58
|
-
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
59
|
-
digest = OpenSSL::Digest::MD4.digest(data)
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.md5_hash(data)
|
63
|
-
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
64
|
-
digest = OpenSSL::Digest::MD5.digest(data)
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.lm2nt(pass, ntlm)
|
68
|
-
res = nil
|
69
|
-
Rex::Text.permute_case( pass.upcase ).each do |word|
|
70
|
-
if(md4_hash(Rex::Text.to_unicode(word)) == ntlm)
|
71
|
-
res = word
|
72
|
-
break
|
73
|
-
end
|
74
|
-
end
|
75
|
-
res
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.lmchal2ntchal(pass, ntlm, challenge)
|
79
|
-
res = nil
|
80
|
-
Rex::Text.permute_case( pass.upcase ).each do |word|
|
81
|
-
if(ntlm_md4(word,challenge) == ntlm)
|
82
|
-
res = word
|
83
|
-
break
|
84
|
-
end
|
85
|
-
end
|
86
|
-
res
|
87
|
-
end
|
88
|
-
|
89
|
-
rescue LoadError
|
90
|
-
end
|
91
32
|
|
92
33
|
end
|
93
34
|
end
|
@@ -829,6 +829,18 @@ class NTLM2MissingChallenge < Error
|
|
829
829
|
end
|
830
830
|
end
|
831
831
|
|
832
|
+
class SigningError < Error
|
833
|
+
def to_s
|
834
|
+
"Unable to handle signing in this situation"
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
class IncorrectSigningError < Error
|
839
|
+
def to_s
|
840
|
+
"The signature sent by the server is not correct"
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
832
844
|
class SimpleClientError < Error
|
833
845
|
attr_accessor :source, :fatal
|
834
846
|
end
|
@@ -179,14 +179,24 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
|
|
179
179
|
self.shares = { }
|
180
180
|
end
|
181
181
|
|
182
|
-
def login(name = '', user = '', pass = '', domain = ''
|
182
|
+
def login( name = '', user = '', pass = '', domain = '',
|
183
|
+
verify_signature = false, usentlmv2 = false, usentlm2_session = true,
|
184
|
+
send_lm = true, use_lanman_key = false, send_ntlm = true,
|
185
|
+
native_os = 'Windows 2000 2195', native_lm = 'Windows 2000 5.0')
|
183
186
|
|
184
187
|
begin
|
185
188
|
|
186
189
|
if (self.direct != true)
|
187
190
|
self.client.session_request(name)
|
188
191
|
end
|
189
|
-
|
192
|
+
self.client.native_os = native_os
|
193
|
+
self.client.native_lm = native_lm
|
194
|
+
self.client.verify_signature = verify_signature
|
195
|
+
self.client.use_ntlmv2 = usentlmv2
|
196
|
+
self.client.usentlm2_session = usentlm2_session
|
197
|
+
self.client.send_lm = send_lm
|
198
|
+
self.client.use_lanman_key = use_lanman_key
|
199
|
+
self.client.send_ntlm = send_ntlm
|
190
200
|
self.client.negotiate
|
191
201
|
ok = self.client.session_setup(user, pass, domain)
|
192
202
|
rescue ::Interrupt
|
@@ -233,7 +243,7 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
|
|
233
243
|
|
234
244
|
def login_split_next_ntlm1(user, domain, hash_lm, hash_nt)
|
235
245
|
begin
|
236
|
-
ok = self.client.
|
246
|
+
ok = self.client.session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt)
|
237
247
|
rescue ::Interrupt
|
238
248
|
raise $!
|
239
249
|
rescue ::Exception => e
|
@@ -261,14 +271,16 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
|
|
261
271
|
self.shares.delete(share)
|
262
272
|
end
|
263
273
|
|
264
|
-
|
274
|
+
|
275
|
+
def open(path, perm, chunk_size = 48000)
|
265
276
|
mode = UTILS.open_mode_to_mode(perm)
|
266
277
|
access = UTILS.open_mode_to_access(perm)
|
267
278
|
|
268
279
|
ok = self.client.open(path, mode, access)
|
269
280
|
file_id = ok['Payload'].v['FileID']
|
270
|
-
|
271
281
|
fh = OpenFile.new(self.client, path, self.client.last_tree_id, file_id)
|
282
|
+
fh.chunk_size = chunk_size
|
283
|
+
fh
|
272
284
|
end
|
273
285
|
|
274
286
|
def delete(*args)
|
data/lib/rex/proto/smb/utils.rb
CHANGED
@@ -96,468 +96,11 @@ CONST = Rex::Proto::SMB::Constants
|
|
96
96
|
return decoded
|
97
97
|
end
|
98
98
|
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
def self.asn1encode(str = '')
|
103
|
-
res = ''
|
104
|
-
|
105
|
-
# If the high bit of the first byte is 1, it contains the number of
|
106
|
-
# length bytes that follow
|
107
|
-
|
108
|
-
case str.length
|
109
|
-
when 0 .. 0x7F
|
110
|
-
res = [str.length].pack('C') + str
|
111
|
-
when 0x80 .. 0xFF
|
112
|
-
res = [0x81, str.length].pack('CC') + str
|
113
|
-
when 0x100 .. 0xFFFF
|
114
|
-
res = [0x82, str.length].pack('Cn') + str
|
115
|
-
when 0x10000 .. 0xffffff
|
116
|
-
res = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
|
117
|
-
when 0x1000000 .. 0xffffffff
|
118
|
-
res = [0x84, str.length].pack('CN') + str
|
119
|
-
else
|
120
|
-
raise "ASN1 str too long"
|
121
|
-
end
|
122
|
-
return res
|
99
|
+
# Determine whether the password is a known hash format
|
100
|
+
def self.is_pass_ntlm_hash?(str)
|
101
|
+
str.downcase =~ /^[0-9a-f]{32}:[0-9a-f]{32}$/
|
123
102
|
end
|
124
|
-
|
125
|
-
def self.make_ntlmv2_secblob_init(domain = 'WORKGROUP', name = 'WORKSTATION', flags=0x80201)
|
126
|
-
blob =
|
127
|
-
"\x60" + self.asn1encode(
|
128
|
-
"\x06" + self.asn1encode(
|
129
|
-
"\x2b\x06\x01\x05\x05\x02"
|
130
|
-
) +
|
131
|
-
"\xa0" + self.asn1encode(
|
132
|
-
"\x30" + self.asn1encode(
|
133
|
-
"\xa0" + self.asn1encode(
|
134
|
-
"\x30" + self.asn1encode(
|
135
|
-
"\x06" + self.asn1encode(
|
136
|
-
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
|
137
|
-
)
|
138
|
-
)
|
139
|
-
) +
|
140
|
-
"\xa2" + self.asn1encode(
|
141
|
-
"\x04" + self.asn1encode(
|
142
|
-
"NTLMSSP\x00" +
|
143
|
-
[1, flags].pack('VV') +
|
144
|
-
|
145
|
-
[
|
146
|
-
domain.length, #length
|
147
|
-
domain.length, #max length
|
148
|
-
32
|
149
|
-
].pack('vvV') +
|
150
|
-
|
151
|
-
[
|
152
|
-
name.length, #length
|
153
|
-
name.length, #max length
|
154
|
-
domain.length + 32
|
155
|
-
].pack('vvV') +
|
156
|
-
|
157
|
-
domain + name
|
158
|
-
)
|
159
|
-
)
|
160
|
-
)
|
161
|
-
)
|
162
|
-
)
|
163
|
-
|
164
|
-
return blob
|
165
|
-
end
|
166
|
-
|
167
|
-
def self.make_ntlmv2_secblob_auth(domain, name, user, lmv2, ntlm, flags = 0x080201)
|
168
|
-
|
169
|
-
lmv2 ||= "\x00" * 24
|
170
|
-
ntlm ||= "\x00" * 24
|
171
|
-
|
172
|
-
domain_uni = Rex::Text.to_unicode(domain)
|
173
|
-
user_uni = Rex::Text.to_unicode(user)
|
174
|
-
name_uni = Rex::Text.to_unicode(name)
|
175
|
-
session = ''
|
176
|
-
|
177
|
-
ptr = 64
|
178
|
-
blob =
|
179
|
-
"\xa1" + self.asn1encode(
|
180
|
-
"\x30" + self.asn1encode(
|
181
|
-
"\xa2" + self.asn1encode(
|
182
|
-
"\x04" + self.asn1encode(
|
183
|
-
|
184
|
-
"NTLMSSP\x00" +
|
185
|
-
[ 3 ].pack('V') +
|
186
|
-
|
187
|
-
[ # Lan Manager Response
|
188
|
-
lmv2.length,
|
189
|
-
lmv2.length,
|
190
|
-
(ptr)
|
191
|
-
].pack('vvV') +
|
192
|
-
|
193
|
-
[ # NTLM Manager Response
|
194
|
-
ntlm.length,
|
195
|
-
ntlm.length,
|
196
|
-
(ptr += lmv2.length)
|
197
|
-
].pack('vvV') +
|
198
|
-
|
199
|
-
[ # Domain Name
|
200
|
-
domain_uni.length,
|
201
|
-
domain_uni.length,
|
202
|
-
(ptr += ntlm.length)
|
203
|
-
].pack('vvV') +
|
204
|
-
|
205
|
-
[ # Username
|
206
|
-
user_uni.length,
|
207
|
-
user_uni.length,
|
208
|
-
(ptr += domain_uni.length)
|
209
|
-
].pack('vvV') +
|
210
|
-
|
211
|
-
[ # Hostname
|
212
|
-
name_uni.length,
|
213
|
-
name_uni.length,
|
214
|
-
(ptr += user_uni.length)
|
215
|
-
].pack('vvV') +
|
216
|
-
|
217
|
-
[ # Session Key (none)
|
218
|
-
session.length,
|
219
|
-
session.length,
|
220
|
-
(ptr += name_uni.length)
|
221
|
-
].pack('vvV') +
|
222
|
-
|
223
|
-
[ flags ].pack('V') +
|
224
|
-
|
225
|
-
lmv2 +
|
226
|
-
ntlm +
|
227
|
-
domain_uni +
|
228
|
-
user_uni +
|
229
|
-
name_uni +
|
230
|
-
session + "\x00"
|
231
|
-
)
|
232
|
-
)
|
233
|
-
)
|
234
|
-
)
|
235
|
-
return blob
|
236
|
-
end
|
237
|
-
|
238
|
-
|
239
|
-
def self.make_negotiate_secblob_resp(account, domain)
|
240
|
-
blob =
|
241
|
-
"\x60" + self.asn1encode(
|
242
|
-
"\x06" + self.asn1encode(
|
243
|
-
"\x2b\x06\x01\x05\x05\x02"
|
244
|
-
) +
|
245
|
-
"\xa0" + self.asn1encode(
|
246
|
-
"\x30" + self.asn1encode(
|
247
|
-
"\xa0" + self.asn1encode(
|
248
|
-
"\x30" + self.asn1encode(
|
249
|
-
"\x06" + self.asn1encode(
|
250
|
-
"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"
|
251
|
-
) +
|
252
|
-
"\x06" + self.asn1encode(
|
253
|
-
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
|
254
|
-
) +
|
255
|
-
"\x06" + self.asn1encode(
|
256
|
-
"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"
|
257
|
-
) +
|
258
|
-
"\x06" + self.asn1encode(
|
259
|
-
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
|
260
|
-
)
|
261
|
-
)
|
262
|
-
) +
|
263
|
-
"\xa3" + self.asn1encode(
|
264
|
-
"\x30" + self.asn1encode(
|
265
|
-
"\xa0" + self.asn1encode(
|
266
|
-
"\x1b" + self.asn1encode(
|
267
|
-
account + '@' + domain
|
268
|
-
)
|
269
|
-
)
|
270
|
-
)
|
271
|
-
)
|
272
|
-
)
|
273
|
-
)
|
274
|
-
)
|
275
103
|
|
276
|
-
return blob
|
277
|
-
end
|
278
|
-
|
279
|
-
def self.make_ntlmv2_secblob_chall(win_domain, dns_domain, win_name, dns_name, chall, flags)
|
280
|
-
|
281
|
-
win_domain = Rex::Text.to_unicode(win_domain)
|
282
|
-
dns_domain = Rex::Text.to_unicode(dns_domain)
|
283
|
-
win_name = Rex::Text.to_unicode(win_name)
|
284
|
-
dns_name = Rex::Text.to_unicode(dns_name)
|
285
|
-
|
286
|
-
addr_list = ''
|
287
|
-
addr_list << [2, win_domain.length].pack('vv') + win_domain
|
288
|
-
addr_list << [1, win_name.length].pack('vv') + win_name
|
289
|
-
addr_list << [4, dns_domain.length].pack('vv') + dns_domain
|
290
|
-
addr_list << [3, dns_name.length].pack('vv') + dns_name
|
291
|
-
addr_list << [5, dns_domain.length].pack('vv') + dns_domain
|
292
|
-
addr_list << [0, 0].pack('vv')
|
293
|
-
|
294
|
-
ptr = 0
|
295
|
-
blob =
|
296
|
-
"\xa1" + self.asn1encode(
|
297
|
-
"\x30" + self.asn1encode(
|
298
|
-
"\xa0" + self.asn1encode(
|
299
|
-
"\x0a" + self.asn1encode(
|
300
|
-
"\x01"
|
301
|
-
)
|
302
|
-
) +
|
303
|
-
"\xa1" + self.asn1encode(
|
304
|
-
"\x06" + self.asn1encode(
|
305
|
-
"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
|
306
|
-
)
|
307
|
-
) +
|
308
|
-
"\xa2" + self.asn1encode(
|
309
|
-
"\x04" + self.asn1encode(
|
310
|
-
"NTLMSSP\x00" +
|
311
|
-
[2].pack('V') +
|
312
|
-
[
|
313
|
-
win_domain.length, # length
|
314
|
-
win_domain.length, # max length
|
315
|
-
(ptr += 48)
|
316
|
-
].pack('vvV') +
|
317
|
-
[ flags ].pack('V') +
|
318
|
-
chall +
|
319
|
-
"\x00\x00\x00\x00\x00\x00\x00\x00" +
|
320
|
-
[
|
321
|
-
addr_list.length, # length
|
322
|
-
addr_list.length, # max length
|
323
|
-
(ptr += win_domain.length)
|
324
|
-
].pack('vvV') +
|
325
|
-
win_domain +
|
326
|
-
addr_list
|
327
|
-
)
|
328
|
-
)
|
329
|
-
)
|
330
|
-
)
|
331
|
-
|
332
|
-
return blob
|
333
|
-
end
|
334
|
-
|
335
|
-
def self.make_ntlmv2_secblob_success
|
336
|
-
blob =
|
337
|
-
"\xa1" + self.asn1encode(
|
338
|
-
"\x30" + self.asn1encode(
|
339
|
-
"\xa0" + self.asn1encode(
|
340
|
-
"\x0a" + self.asn1encode(
|
341
|
-
"\x00"
|
342
|
-
)
|
343
|
-
)
|
344
|
-
)
|
345
|
-
)
|
346
|
-
return blob
|
347
|
-
end
|
348
|
-
|
349
|
-
#
|
350
|
-
# Process Type 3 NTLM Message (in Base64)
|
351
|
-
#
|
352
|
-
# from http://www.innovation.ch/personal/ronald/ntlm.html
|
353
|
-
#
|
354
|
-
# struct {
|
355
|
-
# byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
|
356
|
-
# byte type; // 0x03
|
357
|
-
# byte zero[3];
|
358
|
-
#
|
359
|
-
# short lm_resp_len; // LanManager response length (always 0x18)
|
360
|
-
# short lm_resp_len; // LanManager response length (always 0x18)
|
361
|
-
# short lm_resp_off; // LanManager response offset
|
362
|
-
# byte zero[2];
|
363
|
-
#
|
364
|
-
# short nt_resp_len; // NT response length (always 0x18)
|
365
|
-
# short nt_resp_len; // NT response length (always 0x18)
|
366
|
-
# short nt_resp_off; // NT response offset
|
367
|
-
# byte zero[2];
|
368
|
-
#
|
369
|
-
# short dom_len; // domain string length
|
370
|
-
# short dom_len; // domain string length
|
371
|
-
# short dom_off; // domain string offset (always 0x40)
|
372
|
-
# byte zero[2];
|
373
|
-
#
|
374
|
-
# short user_len; // username string length
|
375
|
-
# short user_len; // username string length
|
376
|
-
# short user_off; // username string offset
|
377
|
-
# byte zero[2];
|
378
|
-
#
|
379
|
-
# short host_len; // host string length
|
380
|
-
# short host_len; // host string length
|
381
|
-
# short host_off; // host string offset
|
382
|
-
# byte zero[6];
|
383
|
-
#
|
384
|
-
# short msg_len; // message length
|
385
|
-
# byte zero[2];
|
386
|
-
#
|
387
|
-
# short flags; // 0x8201
|
388
|
-
# byte zero[2];
|
389
|
-
#
|
390
|
-
# byte dom[*]; // domain string (unicode UTF-16LE)
|
391
|
-
# byte user[*]; // username string (unicode UTF-16LE)
|
392
|
-
# byte host[*]; // host string (unicode UTF-16LE)
|
393
|
-
# byte lm_resp[*]; // LanManager response
|
394
|
-
# byte nt_resp[*]; // NT response
|
395
|
-
# } type_3_message
|
396
|
-
#
|
397
|
-
def self.process_type3_message(message)
|
398
|
-
decode = Rex::Text.decode_base64(message.strip)
|
399
|
-
type = decode[8,1].unpack("C").first
|
400
|
-
if (type == 3)
|
401
|
-
lm_len = decode[12,2].unpack("v").first
|
402
|
-
lm_offset = decode[16,2].unpack("v").first
|
403
|
-
lm = decode[lm_offset, lm_len].unpack("H*").first
|
404
|
-
|
405
|
-
nt_len = decode[20,2].unpack("v").first
|
406
|
-
nt_offset = decode[24,2].unpack("v").first
|
407
|
-
nt = decode[nt_offset, nt_len].unpack("H*").first
|
408
|
-
|
409
|
-
dom_len = decode[28,2].unpack("v").first
|
410
|
-
dom_offset = decode[32,2].unpack("v").first
|
411
|
-
domain = decode[dom_offset, dom_len]
|
412
|
-
|
413
|
-
user_len = decode[36,2].unpack("v").first
|
414
|
-
user_offset = decode[40,2].unpack("v").first
|
415
|
-
user = decode[user_offset, user_len]
|
416
|
-
|
417
|
-
host_len = decode[44,2].unpack("v").first
|
418
|
-
host_offset = decode[48,2].unpack("v").first
|
419
|
-
host = decode[host_offset, host_len]
|
420
|
-
|
421
|
-
return domain, user, host, lm, nt
|
422
|
-
else
|
423
|
-
return "", "", "", "", ""
|
424
|
-
end
|
425
|
-
end
|
426
|
-
|
427
|
-
#
|
428
|
-
# Process Type 1 NTLM Messages, return a Base64 Type 2 Message
|
429
|
-
#
|
430
|
-
def self.process_type1_message(message, nonce = "\x11\x22\x33\x44\x55\x66\x77\x88", win_domain = 'DOMAIN',
|
431
|
-
win_name = 'SERVER', dns_name = 'server', dns_domain = 'example.com', downgrade = true)
|
432
|
-
|
433
|
-
dns_name = Rex::Text.to_unicode(dns_name + "." + dns_domain)
|
434
|
-
win_domain = Rex::Text.to_unicode(win_domain)
|
435
|
-
dns_domain = Rex::Text.to_unicode(dns_domain)
|
436
|
-
win_name = Rex::Text.to_unicode(win_name)
|
437
|
-
decode = Rex::Text.decode_base64(message.strip)
|
438
|
-
|
439
|
-
type = decode[8,1].unpack("C").first
|
440
|
-
|
441
|
-
if (type == 1)
|
442
|
-
# A type 1 message has been received, lets build a type 2 message response
|
443
|
-
|
444
|
-
reqflags = decode[12,4]
|
445
|
-
reqflags = reqflags.unpack("V").first
|
446
|
-
|
447
|
-
if (reqflags & CONST::REQUEST_TARGET) == CONST::REQUEST_TARGET
|
448
|
-
|
449
|
-
if (downgrade)
|
450
|
-
# At this time NTLMv2 and signing requirements are not supported
|
451
|
-
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
|
452
|
-
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
|
453
|
-
end
|
454
|
-
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
|
455
|
-
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
flags = reqflags + CONST::TARGET_TYPE_DOMAIN + CONST::TARGET_TYPE_SERVER
|
460
|
-
tid = true
|
461
|
-
|
462
|
-
tidoffset = 48 + win_domain.length
|
463
|
-
tidbuff =
|
464
|
-
[2].pack('v') + # tid type, win domain
|
465
|
-
[win_domain.length].pack('v') +
|
466
|
-
win_domain +
|
467
|
-
[1].pack('v') + # tid type, server name
|
468
|
-
[win_name.length].pack('v') +
|
469
|
-
win_name +
|
470
|
-
[4].pack('v') + # tid type, domain name
|
471
|
-
[dns_domain.length].pack('v') +
|
472
|
-
dns_domain +
|
473
|
-
[3].pack('v') + # tid type, dns_name
|
474
|
-
[dns_name.length].pack('v') +
|
475
|
-
dns_name
|
476
|
-
else
|
477
|
-
flags = CONST::NEGOTIATE_UNICODE + CONST::NEGOTIATE_NTLM
|
478
|
-
tid = false
|
479
|
-
end
|
480
|
-
|
481
|
-
type2msg = "NTLMSSP\0" + # protocol, 8 bytes
|
482
|
-
"\x02\x00\x00\x00" # type, 4 bytes
|
483
|
-
|
484
|
-
if (tid)
|
485
|
-
type2msg += # Target security info, 8 bytes. Filled if REQUEST_TARGET
|
486
|
-
[win_domain.length].pack('v') + # Length, 2 bytes
|
487
|
-
[win_domain.length].pack('v') # Allocated space, 2 bytes
|
488
|
-
end
|
489
|
-
|
490
|
-
type2msg +="\x30\x00\x00\x00" + # Offset, 4 bytes
|
491
|
-
[flags].pack('V') + # flags, 4 bytes
|
492
|
-
nonce + # the nonce, 8 bytes
|
493
|
-
"\x00" * 8 # Context (all 0s), 8 bytes
|
494
|
-
|
495
|
-
if (tid)
|
496
|
-
type2msg += # Target information security buffer. Filled if REQUEST_TARGET
|
497
|
-
[tidbuff.length].pack('v') + # Length, 2 bytes
|
498
|
-
[tidbuff.length].pack('v') + # Allocated space, 2 bytes
|
499
|
-
[tidoffset].pack('V') + # Offset, 4 bytes (usually \x48 + length of win_domain)
|
500
|
-
win_domain + # Target name data (domain in unicode if REQUEST_UNICODE)
|
501
|
-
# Target information data
|
502
|
-
tidbuff + # Type, 2 bytes
|
503
|
-
# Length, 2 bytes
|
504
|
-
# Data (in unicode if REQUEST_UNICODE)
|
505
|
-
"\x00\x00\x00\x00" # Terminator, 4 bytes, all \x00
|
506
|
-
end
|
507
|
-
|
508
|
-
type2msg = Rex::Text.encode_base64(type2msg).delete("\n") # base64 encode and remove the returns
|
509
|
-
else
|
510
|
-
# This is not a Type2 message
|
511
|
-
type2msg = ""
|
512
|
-
end
|
513
|
-
|
514
|
-
return type2msg
|
515
|
-
end
|
516
|
-
|
517
|
-
#
|
518
|
-
# Downgrading Type messages to LMv1/NTLMv1 and removing signing
|
519
|
-
#
|
520
|
-
def self.downgrade_type_message(message)
|
521
|
-
decode = Rex::Text.decode_base64(message.strip)
|
522
|
-
|
523
|
-
type = decode[8,1].unpack("C").first
|
524
|
-
|
525
|
-
if (type > 0 and type < 4)
|
526
|
-
reqflags = decode[12..15] if (type == 1 or type == 3)
|
527
|
-
reqflags = decode[20..23] if (type == 2)
|
528
|
-
reqflags = reqflags.unpack("V")
|
529
|
-
|
530
|
-
# Remove NEGOTIATE_NTLMV2_KEY and NEGOTIATE_ALWAYS_SIGN, this lowers the negotiation
|
531
|
-
# down to LMv1/NTLMv1.
|
532
|
-
if (reqflags & CONST::NEGOTIATE_NTLM2_KEY) == CONST::NEGOTIATE_NTLM2_KEY
|
533
|
-
reqflags = reqflags - CONST::NEGOTIATE_NTLM2_KEY
|
534
|
-
end
|
535
|
-
if (reqflags & CONST::NEGOTIATE_ALWAYS_SIGN) == CONST::NEGOTIATE_ALWAYS_SIGN
|
536
|
-
reqflags = reqflags - CONST::NEGOTIATE_ALWAYS_SIGN
|
537
|
-
end
|
538
|
-
|
539
|
-
# Return the flags back to the decode so we can base64 it again
|
540
|
-
flags = reqflags.to_s(16)
|
541
|
-
0.upto(8) do |idx|
|
542
|
-
if (idx > flags.length)
|
543
|
-
flags.insert(0, "0")
|
544
|
-
end
|
545
|
-
end
|
546
|
-
|
547
|
-
idx = 0
|
548
|
-
0.upto(3) do |cnt|
|
549
|
-
if (type == 2)
|
550
|
-
decode[23-cnt] = [flags[idx,1]].pack("C")
|
551
|
-
else
|
552
|
-
decode[15-cnt] = [flags[idx,1]].pack("C")
|
553
|
-
end
|
554
|
-
idx += 2
|
555
|
-
end
|
556
|
-
|
557
|
-
end
|
558
|
-
return Rex::Text.encode_base64(decode).delete("\n") # base64 encode and remove the returns
|
559
|
-
end
|
560
|
-
|
561
104
|
end
|
562
105
|
end
|
563
106
|
end
|