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
@@ -0,0 +1,326 @@
|
|
1
|
+
#
|
2
|
+
# An NTLM Authentication Library for Ruby
|
3
|
+
#
|
4
|
+
# This code is a derivative of "dbf2.rb" written by yrock
|
5
|
+
# and Minero Aoki. You can find original code here:
|
6
|
+
# http://jp.rubyist.net/magazine/?0013-CodeReview
|
7
|
+
# -------------------------------------------------------------
|
8
|
+
# Copyright (c) 2005,2006 yrock
|
9
|
+
#
|
10
|
+
# This program is free software.
|
11
|
+
# You can distribute/modify this program under the terms of the
|
12
|
+
# Ruby License.
|
13
|
+
#
|
14
|
+
# 2011-02-23 refactored by Alexandre Maloteaux for Metasploit Project
|
15
|
+
# -------------------------------------------------------------
|
16
|
+
#
|
17
|
+
# 2006-02-11 refactored by Minero Aoki
|
18
|
+
# -------------------------------------------------------------
|
19
|
+
#
|
20
|
+
# All protocol information used to write this code stems from
|
21
|
+
# "The NTLM Authentication Protocol" by Eric Glass. The author
|
22
|
+
# would thank to him for this tremendous work and making it
|
23
|
+
# available on the net.
|
24
|
+
# http://davenport.sourceforge.net/ntlm.html
|
25
|
+
# -------------------------------------------------------------
|
26
|
+
# Copyright (c) 2003 Eric Glass
|
27
|
+
#
|
28
|
+
# Permission to use, copy, modify, and distribute this document
|
29
|
+
# for any purpose and without any fee is hereby granted,
|
30
|
+
# provided that the above copyright notice and this list of
|
31
|
+
# conditions appear in all copies.
|
32
|
+
# -------------------------------------------------------------
|
33
|
+
#
|
34
|
+
# The author also looked Mozilla-Firefox-1.0.7 source code,
|
35
|
+
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
|
36
|
+
# Jonathan Bastien-Filiatrault's libntlm-ruby.
|
37
|
+
# "http://x2a.org/websvn/filedetails.php?
|
38
|
+
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
|
39
|
+
# The latter has a minor bug in its separate_keys function.
|
40
|
+
# The third key has to begin from the 14th character of the
|
41
|
+
# input string instead of 13th:)
|
42
|
+
#--
|
43
|
+
# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $
|
44
|
+
#++
|
45
|
+
|
46
|
+
#this class defines the base type needed for other modules like message and crypt
|
47
|
+
|
48
|
+
require 'rex/proto/ntlm/constants'
|
49
|
+
|
50
|
+
module Rex
|
51
|
+
module Proto
|
52
|
+
module NTLM
|
53
|
+
class Base
|
54
|
+
|
55
|
+
CONST = Rex::Proto::NTLM::Constants
|
56
|
+
|
57
|
+
# base classes for primitives
|
58
|
+
class Field
|
59
|
+
attr_accessor :active, :value
|
60
|
+
|
61
|
+
def initialize(opts)
|
62
|
+
@value = opts[:value]
|
63
|
+
@active = opts[:active].nil? ? true : opts[:active]
|
64
|
+
end
|
65
|
+
|
66
|
+
def size
|
67
|
+
@active ? @size : 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class String < Field
|
72
|
+
def initialize(opts)
|
73
|
+
super(opts)
|
74
|
+
@size = opts[:size]
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse(str, offset=0)
|
78
|
+
if @active and str.size >= offset + @size
|
79
|
+
@value = str[offset, @size]
|
80
|
+
@size
|
81
|
+
else
|
82
|
+
0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def serialize
|
87
|
+
if @active
|
88
|
+
@value
|
89
|
+
else
|
90
|
+
""
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def value=(val)
|
95
|
+
@value = val
|
96
|
+
@size = @value.nil? ? 0 : @value.size
|
97
|
+
@active = (@size > 0)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Int16LE < Field
|
102
|
+
def initialize(opt)
|
103
|
+
super(opt)
|
104
|
+
@size = 2
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse(str, offset=0)
|
108
|
+
if @active and str.size >= offset + @size
|
109
|
+
@value = str[offset, @size].unpack("v")[0]
|
110
|
+
@size
|
111
|
+
else
|
112
|
+
0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def serialize
|
117
|
+
[@value].pack("v")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Int32LE < Field
|
122
|
+
def initialize(opt)
|
123
|
+
super(opt)
|
124
|
+
@size = 4
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse(str, offset=0)
|
128
|
+
if @active and str.size >= offset + @size
|
129
|
+
@value = str.slice(offset, @size).unpack("V")[0]
|
130
|
+
@size
|
131
|
+
else
|
132
|
+
0
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def serialize
|
137
|
+
[@value].pack("V") if @active
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class Int64LE < Field
|
142
|
+
def initialize(opt)
|
143
|
+
super(opt)
|
144
|
+
@size = 8
|
145
|
+
end
|
146
|
+
|
147
|
+
def parse(str, offset=0)
|
148
|
+
if @active and str.size >= offset + @size
|
149
|
+
d, u = str.slice(offset, @size).unpack("V2")
|
150
|
+
@value = (u * 0x100000000 + d)
|
151
|
+
@size
|
152
|
+
else
|
153
|
+
0
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def serialize
|
158
|
+
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# base class of data structure
|
163
|
+
class FieldSet
|
164
|
+
class << FieldSet
|
165
|
+
def define(&block)
|
166
|
+
c = Class.new(self)
|
167
|
+
def c.inherited(subclass)
|
168
|
+
proto = @proto
|
169
|
+
subclass.instance_eval {
|
170
|
+
@proto = proto
|
171
|
+
}
|
172
|
+
end
|
173
|
+
c.module_eval(&block)
|
174
|
+
c
|
175
|
+
end
|
176
|
+
|
177
|
+
def string(name, opts)
|
178
|
+
add_field(name, String, opts)
|
179
|
+
end
|
180
|
+
|
181
|
+
def int16LE(name, opts)
|
182
|
+
add_field(name, Int16LE, opts)
|
183
|
+
end
|
184
|
+
|
185
|
+
def int32LE(name, opts)
|
186
|
+
add_field(name, Int32LE, opts)
|
187
|
+
end
|
188
|
+
|
189
|
+
def int64LE(name, opts)
|
190
|
+
add_field(name, Int64LE, opts)
|
191
|
+
end
|
192
|
+
|
193
|
+
def security_buffer(name, opts)
|
194
|
+
add_field(name, SecurityBuffer, opts)
|
195
|
+
end
|
196
|
+
|
197
|
+
def prototypes
|
198
|
+
@proto
|
199
|
+
end
|
200
|
+
|
201
|
+
def names
|
202
|
+
@proto.map{|n, t, o| n}
|
203
|
+
end
|
204
|
+
|
205
|
+
def types
|
206
|
+
@proto.map{|n, t, o| t}
|
207
|
+
end
|
208
|
+
|
209
|
+
def opts
|
210
|
+
@proto.map{|n, t, o| o}
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def add_field(name, type, opts)
|
216
|
+
(@proto ||= []).push [name, type, opts]
|
217
|
+
define_accessor name
|
218
|
+
end
|
219
|
+
|
220
|
+
def define_accessor(name)
|
221
|
+
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
222
|
+
def #{name}
|
223
|
+
self['#{name}'].value
|
224
|
+
end
|
225
|
+
|
226
|
+
def #{name}=(val)
|
227
|
+
self['#{name}'].value = val
|
228
|
+
end
|
229
|
+
End
|
230
|
+
end
|
231
|
+
end #self
|
232
|
+
|
233
|
+
def initialize
|
234
|
+
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
|
235
|
+
end
|
236
|
+
|
237
|
+
def serialize
|
238
|
+
@alist.map{|n, f| f.serialize }.join
|
239
|
+
end
|
240
|
+
|
241
|
+
def parse(str, offset=0)
|
242
|
+
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
|
243
|
+
end
|
244
|
+
|
245
|
+
def size
|
246
|
+
@alist.inject(0){|sum, a| sum += a[1].size}
|
247
|
+
end
|
248
|
+
|
249
|
+
def [](name)
|
250
|
+
a = @alist.assoc(name.to_s.intern)
|
251
|
+
raise ArgumentError, "no such field: #{name}" unless a
|
252
|
+
a[1]
|
253
|
+
end
|
254
|
+
|
255
|
+
def []=(name, val)
|
256
|
+
a = @alist.assoc(name.to_s.intern)
|
257
|
+
raise ArgumentError, "no such field: #{name}" unless a
|
258
|
+
a[1] = val
|
259
|
+
end
|
260
|
+
|
261
|
+
def enable(name)
|
262
|
+
self[name].active = true
|
263
|
+
end
|
264
|
+
|
265
|
+
def disable(name)
|
266
|
+
self[name].active = false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
Blob = FieldSet.define {
|
271
|
+
int32LE :blob_signature, {:value => CONST::BLOB_SIGN}
|
272
|
+
int32LE :reserved, {:value => 0}
|
273
|
+
int64LE :timestamp, {:value => 0}
|
274
|
+
string :challenge, {:value => "", :size => 8}
|
275
|
+
int32LE :unknown1, {:value => 0}
|
276
|
+
string :target_info, {:value => "", :size => 0}
|
277
|
+
int32LE :unknown2, {:value => 0}
|
278
|
+
}
|
279
|
+
|
280
|
+
SecurityBuffer = FieldSet.define {
|
281
|
+
int16LE :length, {:value => 0}
|
282
|
+
int16LE :allocated, {:value => 0}
|
283
|
+
int32LE :offset, {:value => 0}
|
284
|
+
}
|
285
|
+
|
286
|
+
|
287
|
+
class SecurityBuffer
|
288
|
+
attr_accessor :active
|
289
|
+
def initialize(opts)
|
290
|
+
super()
|
291
|
+
@value = opts[:value]
|
292
|
+
@active = opts[:active].nil? ? true : opts[:active]
|
293
|
+
@size = 8
|
294
|
+
end
|
295
|
+
|
296
|
+
def parse(str, offset=0)
|
297
|
+
if @active and str.size >= offset + @size
|
298
|
+
super(str, offset)
|
299
|
+
@value = str[self.offset, self.length]
|
300
|
+
@size
|
301
|
+
else
|
302
|
+
0
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def serialize
|
307
|
+
super if @active
|
308
|
+
end
|
309
|
+
|
310
|
+
def value
|
311
|
+
@value
|
312
|
+
end
|
313
|
+
|
314
|
+
def value=(val)
|
315
|
+
@value = val
|
316
|
+
self.length = self.allocated = val.size
|
317
|
+
end
|
318
|
+
|
319
|
+
def data_size
|
320
|
+
@active ? @value.size : 0
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Rex
|
2
|
+
module Proto
|
3
|
+
module NTLM
|
4
|
+
class Constants
|
5
|
+
|
6
|
+
SSP_SIGN = "NTLMSSP\0"
|
7
|
+
BLOB_SIGN = 0x00000101
|
8
|
+
LM_MAGIC = "KGS!@\#$%"
|
9
|
+
TIME_OFFSET = 11644473600
|
10
|
+
MAX64 = 0xffffffffffffffff
|
11
|
+
|
12
|
+
FLAGS = {
|
13
|
+
:UNICODE => 0x00000001,
|
14
|
+
:OEM => 0x00000002,
|
15
|
+
:REQUEST_TARGET => 0x00000004,
|
16
|
+
#:UNKNOWN => 0x00000008,
|
17
|
+
:SIGN => 0x00000010,
|
18
|
+
:SEAL => 0x00000020,
|
19
|
+
#:UNKNOWN => 0x00000040,
|
20
|
+
:NETWARE => 0x00000100,
|
21
|
+
:NTLM => 0x00000200,
|
22
|
+
#:UNKNOWN => 0x00000400,
|
23
|
+
#:UNKNOWN => 0x00000800,
|
24
|
+
:DOMAIN_SUPPLIED => 0x00001000,
|
25
|
+
:WORKSTATION_SUPPLIED => 0x00002000,
|
26
|
+
:LOCAL_CALL => 0x00004000,
|
27
|
+
:ALWAYS_SIGN => 0x00008000,
|
28
|
+
:TARGET_TYPE_DOMAIN => 0x00010000,
|
29
|
+
:TARGET_INFO => 0x00800000,
|
30
|
+
:NTLM2_KEY => 0x00080000,
|
31
|
+
:KEY128 => 0x20000000,
|
32
|
+
:KEY56 => 0x80000000
|
33
|
+
}
|
34
|
+
|
35
|
+
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
|
36
|
+
|
37
|
+
DEFAULT_FLAGS = {
|
38
|
+
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
|
39
|
+
:TYPE2 => FLAGS[:UNICODE],
|
40
|
+
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
|
41
|
+
}
|
42
|
+
|
43
|
+
# NTLM Response Type
|
44
|
+
NTLM_V1_RESPONSE = 1
|
45
|
+
NTLM_V2_RESPONSE = 2
|
46
|
+
NTLM_2_SESSION_RESPONSE = 3
|
47
|
+
|
48
|
+
#the same flags but merged from lib/rex/proto/smb/constants and keeped for compatibility
|
49
|
+
# NTLMSSP Message Flags
|
50
|
+
NEGOTIATE_UNICODE = 0x00000001 # Only set if Type 1 contains it - this or oem, not both
|
51
|
+
NEGOTIATE_OEM = 0x00000002 # Only set if Type 1 contains it - this or unicode, not both
|
52
|
+
REQUEST_TARGET = 0x00000004 # If set in Type 1, must return domain or server
|
53
|
+
NEGOTIATE_SIGN = 0x00000010 # Session signature required
|
54
|
+
NEGOTIATE_SEAL = 0x00000020 # Session seal required
|
55
|
+
NEGOTIATE_LMKEY = 0x00000080 # LM Session Key should be used for signing and sealing
|
56
|
+
NEGOTIATE_NTLM = 0x00000200 # NTLM auth is supported
|
57
|
+
NEGOTIATE_ANONYMOUS = 0x00000800 # Anonymous context used
|
58
|
+
NEGOTIATE_DOMAIN = 0x00001000 # Sent in Type1, client gives domain info
|
59
|
+
NEGOTIATE_WORKSTATION = 0x00002000 # Sent in Type1, client gives workstation info
|
60
|
+
NEGOTIATE_LOCAL_CALL = 0x00004000 # Server and client are on same machine
|
61
|
+
NEGOTIATE_ALWAYS_SIGN = 0x00008000 # Add signatures to packets
|
62
|
+
TARGET_TYPE_DOMAIN = 0x00010000 # If REQUEST_TARGET, we're adding the domain name
|
63
|
+
TARGET_TYPE_SERVER = 0x00020000 # If REQUEST_TARGET, we're adding the server name
|
64
|
+
TARGET_TYPE_SHARE = 0x00040000 # Supposed to denote "a share" but for a webserver?
|
65
|
+
NEGOTIATE_NTLM2_KEY = 0x00080000 # NTLMv2 Signature and Key exchanges
|
66
|
+
NEGOTIATE_TARGET_INFO = 0x00800000 # Server set when sending Target Information Block
|
67
|
+
NEGOTIATE_128 = 0x20000000 # 128-bit encryption supported
|
68
|
+
NEGOTIATE_KEY_EXCH = 0x40000000 # Client will supply encrypted master key in Session Key field of Type3 msg
|
69
|
+
NEGOTIATE_56 = 0x80000000 # 56-bit encryption supported
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
#
|
2
|
+
# An NTLM Authentication Library for Ruby
|
3
|
+
#
|
4
|
+
# This code is a derivative of "dbf2.rb" written by yrock
|
5
|
+
# and Minero Aoki. You can find original code here:
|
6
|
+
# http://jp.rubyist.net/magazine/?0013-CodeReview
|
7
|
+
# -------------------------------------------------------------
|
8
|
+
# Copyright (c) 2005,2006 yrock
|
9
|
+
#
|
10
|
+
# This program is free software.
|
11
|
+
# You can distribute/modify this program under the terms of the
|
12
|
+
# Ruby License.
|
13
|
+
#
|
14
|
+
# 2011-03-08 improved through a code merge with Metasploit's SMB::Crypt
|
15
|
+
# -------------------------------------------------------------
|
16
|
+
#
|
17
|
+
# 2011-02-23 refactored and improved by Alexandre Maloteaux for Metasploit Project
|
18
|
+
# -------------------------------------------------------------
|
19
|
+
#
|
20
|
+
# 2006-02-11 refactored by Minero Aoki
|
21
|
+
# -------------------------------------------------------------
|
22
|
+
#
|
23
|
+
# All protocol information used to write this code stems from
|
24
|
+
# "The NTLM Authentication Protocol" by Eric Glass. The author
|
25
|
+
# would thank to him for this tremendous work and making it
|
26
|
+
# available on the net.
|
27
|
+
# http://davenport.sourceforge.net/ntlm.html
|
28
|
+
# -------------------------------------------------------------
|
29
|
+
# Copyright (c) 2003 Eric Glass
|
30
|
+
#
|
31
|
+
# Permission to use, copy, modify, and distribute this document
|
32
|
+
# for any purpose and without any fee is hereby granted,
|
33
|
+
# provided that the above copyright notice and this list of
|
34
|
+
# conditions appear in all copies.
|
35
|
+
# -------------------------------------------------------------
|
36
|
+
#
|
37
|
+
# The author also looked Mozilla-Firefox-1.0.7 source code,
|
38
|
+
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
|
39
|
+
# Jonathan Bastien-Filiatrault's libntlm-ruby.
|
40
|
+
# "http://x2a.org/websvn/filedetails.php?
|
41
|
+
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
|
42
|
+
# The latter has a minor bug in its separate_keys function.
|
43
|
+
# The third key has to begin from the 14th character of the
|
44
|
+
# input string instead of 13th:)
|
45
|
+
#--
|
46
|
+
# $Id: ntlm.rb 11678 2011-01-30 19:26:35Z hdm $
|
47
|
+
#++
|
48
|
+
|
49
|
+
|
50
|
+
require 'rex/proto/ntlm/constants'
|
51
|
+
require 'rex/proto/ntlm/base'
|
52
|
+
|
53
|
+
module Rex
|
54
|
+
module Proto
|
55
|
+
module NTLM
|
56
|
+
class Crypt
|
57
|
+
|
58
|
+
CONST = Rex::Proto::NTLM::Constants
|
59
|
+
BASE = Rex::Proto::NTLM::Base
|
60
|
+
|
61
|
+
@@loaded_openssl = false
|
62
|
+
|
63
|
+
begin
|
64
|
+
require 'openssl'
|
65
|
+
require 'openssl/digest'
|
66
|
+
@@loaded_openssl = true
|
67
|
+
rescue ::Exception
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.gen_keys(str)
|
71
|
+
str.scan(/.{7}/).map{ |key| des_56_to_64(key) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.des_56_to_64(ckey56s)
|
75
|
+
ckey64 = []
|
76
|
+
ckey56 = ckey56s.unpack('C*')
|
77
|
+
ckey64[0] = ckey56[0]
|
78
|
+
ckey64[1] = ((ckey56[0] << 7) & 0xFF) | (ckey56[1] >> 1)
|
79
|
+
ckey64[2] = ((ckey56[1] << 6) & 0xFF) | (ckey56[2] >> 2)
|
80
|
+
ckey64[3] = ((ckey56[2] << 5) & 0xFF) | (ckey56[3] >> 3)
|
81
|
+
ckey64[4] = ((ckey56[3] << 4) & 0xFF) | (ckey56[4] >> 4)
|
82
|
+
ckey64[5] = ((ckey56[4] << 3) & 0xFF) | (ckey56[5] >> 5)
|
83
|
+
ckey64[6] = ((ckey56[5] << 2) & 0xFF) | (ckey56[6] >> 6)
|
84
|
+
ckey64[7] = (ckey56[6] << 1) & 0xFF
|
85
|
+
ckey64.pack('C*')
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.apply_des(plain, keys)
|
89
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
90
|
+
dec = OpenSSL::Cipher::DES.new
|
91
|
+
keys.map do |k|
|
92
|
+
dec.key = k
|
93
|
+
dec.encrypt.update(plain)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.lm_hash(password, half = false)
|
98
|
+
size = half ? 7 : 14
|
99
|
+
keys = gen_keys(password.upcase.ljust(size, "\0"))
|
100
|
+
apply_des(CONST::LM_MAGIC, keys).join
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.ntlm_hash(password, opt = {})
|
104
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
105
|
+
pwd = password.dup
|
106
|
+
unless opt[:unicode]
|
107
|
+
pwd = Rex::Text.to_unicode(pwd)
|
108
|
+
end
|
109
|
+
OpenSSL::Digest::MD4.digest(pwd)
|
110
|
+
end
|
111
|
+
|
112
|
+
# This hash is used for lmv2/ntlmv2 response calculation
|
113
|
+
def self.ntlmv2_hash(user, password, domain, opt={})
|
114
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
115
|
+
|
116
|
+
if opt[:pass_is_hash]
|
117
|
+
ntlmhash = password
|
118
|
+
else
|
119
|
+
ntlmhash = ntlm_hash(password, opt)
|
120
|
+
end
|
121
|
+
|
122
|
+
# With Win 7 and maybe other OSs we sometimes get the domain not uppercased
|
123
|
+
userdomain = user.upcase + domain
|
124
|
+
unless opt[:unicode]
|
125
|
+
userdomain = Rex::Text.to_unicode(userdomain)
|
126
|
+
end
|
127
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Create the LANMAN response
|
131
|
+
def self.lm_response(arg, half = false)
|
132
|
+
begin
|
133
|
+
hash = arg[:lm_hash]
|
134
|
+
chal = arg[:challenge]
|
135
|
+
rescue
|
136
|
+
raise ArgumentError
|
137
|
+
end
|
138
|
+
chal = BASE::pack_int64le(chal) if chal.is_a?(Integer)
|
139
|
+
if half then size = 7 else size = 21 end
|
140
|
+
keys = gen_keys hash.ljust(size, "\0")
|
141
|
+
apply_des(chal, keys).join
|
142
|
+
end
|
143
|
+
|
144
|
+
# Synonym of lm_response for old compatibility with lib/rex/proto/smb/crypt
|
145
|
+
def self.lanman_des(password, challenge)
|
146
|
+
lm_response({
|
147
|
+
:lm_hash => self.lm_hash(password),
|
148
|
+
:challenge => challenge
|
149
|
+
})
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.ntlm_response(arg)
|
153
|
+
hash = arg[:ntlm_hash]
|
154
|
+
chal = arg[:challenge]
|
155
|
+
chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
|
156
|
+
keys = gen_keys(hash.ljust(21, "\0"))
|
157
|
+
apply_des(chal, keys).join
|
158
|
+
end
|
159
|
+
|
160
|
+
#synonym of ntlm_response for old compatibility with lib/rex/proto/smb/crypt
|
161
|
+
def self.ntlm_md4(password, challenge)
|
162
|
+
ntlm_response({
|
163
|
+
:ntlm_hash => self.ntlm_hash(password),
|
164
|
+
:challenge => challenge
|
165
|
+
})
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.ntlmv2_response(arg, opt = {})
|
169
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
170
|
+
|
171
|
+
key, chal = arg[:ntlmv2_hash], arg[:challenge]
|
172
|
+
if not (key and chal)
|
173
|
+
raise ArgumentError , 'ntlmv2_hash and challenge are mandatory'
|
174
|
+
end
|
175
|
+
|
176
|
+
chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
|
177
|
+
bb = nil
|
178
|
+
|
179
|
+
if opt[:nt_client_challenge]
|
180
|
+
if opt[:nt_client_challenge].to_s.length <= 24
|
181
|
+
raise ArgumentError,"nt_client_challenge is not in a correct format "
|
182
|
+
end
|
183
|
+
bb = opt[:nt_client_challenge]
|
184
|
+
else
|
185
|
+
if not arg[:target_info]
|
186
|
+
raise ArgumentError, "target_info is mandatory in this case"
|
187
|
+
end
|
188
|
+
|
189
|
+
ti = arg[:target_info]
|
190
|
+
cc = opt[:client_challenge] || rand(CONST::MAX64)
|
191
|
+
cc = BASE::pack_int64le(cc) if cc.is_a?(::Integer)
|
192
|
+
|
193
|
+
ts = opt[:timestamp] || Time.now.to_i
|
194
|
+
|
195
|
+
# Convert the unix timestamp to windows format
|
196
|
+
# epoch -> milsec from Jan 1, 1601
|
197
|
+
ts = 10000000 * (ts + CONST::TIME_OFFSET)
|
198
|
+
|
199
|
+
blob = BASE::Blob.new
|
200
|
+
blob.timestamp = ts
|
201
|
+
blob.challenge = cc
|
202
|
+
blob.target_info = ti
|
203
|
+
|
204
|
+
bb = blob.serialize
|
205
|
+
end
|
206
|
+
|
207
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.lmv2_response(arg, opt = {})
|
212
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
213
|
+
key = arg[:ntlmv2_hash]
|
214
|
+
chal = arg[:challenge]
|
215
|
+
|
216
|
+
chal = BASE::pack_int64le(chal) if chal.is_a?(::Integer)
|
217
|
+
cc = opt[:client_challenge] || rand(CONST::MAX64)
|
218
|
+
cc = BASE::pack_int64le(cc) if cc.is_a?(::Integer)
|
219
|
+
|
220
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.ntlm2_session(arg, opt = {})
|
224
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
225
|
+
passwd_hash,chal = arg[:ntlm_hash],arg[:challenge]
|
226
|
+
if not (passwd_hash and chal)
|
227
|
+
raise RuntimeError, "ntlm_hash and challenge are required"
|
228
|
+
end
|
229
|
+
|
230
|
+
cc = opt[:client_challenge] || rand(CONST::MAX64)
|
231
|
+
cc = BASE::pack_int64le(cc) if cc.is_a?(Integer)
|
232
|
+
|
233
|
+
keys = gen_keys(passwd_hash.ljust(21, "\0"))
|
234
|
+
session_hash = OpenSSL::Digest::MD5.digest(chal + cc)[0,8]
|
235
|
+
response = apply_des(session_hash, keys).join
|
236
|
+
[cc.ljust(24, "\0"), response]
|
237
|
+
end
|
238
|
+
|
239
|
+
#
|
240
|
+
# Signing method added for metasploit project
|
241
|
+
#
|
242
|
+
|
243
|
+
# Used when only the LMv1 response is provided (i.e., with Win9x clients)
|
244
|
+
def self.lmv1_user_session_key(pass, opt = {})
|
245
|
+
if opt[:pass_is_hash]
|
246
|
+
usk = pass[0,8]
|
247
|
+
else
|
248
|
+
usk = self.lm_hash(pass.upcase[0,7],true)
|
249
|
+
end
|
250
|
+
usk.ljust(16,"\x00")
|
251
|
+
end
|
252
|
+
|
253
|
+
# This variant is used when the client sends the NTLMv1 response
|
254
|
+
def self.ntlmv1_user_session_key(pass, opt = {})
|
255
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
256
|
+
|
257
|
+
if opt[:pass_is_hash]
|
258
|
+
usk = pass
|
259
|
+
else
|
260
|
+
usk = self.ntlm_hash(pass)
|
261
|
+
end
|
262
|
+
OpenSSL::Digest::MD4.digest(usk)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Used when NTLMv1 authentication is employed with NTLM2 session security
|
266
|
+
def self.ntlm2_session_user_session_key(pass, srv_chall, cli_chall, opt = {})
|
267
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
268
|
+
|
269
|
+
ntlm_key = self.ntlmv1_user_session_key(pass, opt )
|
270
|
+
session_chal = srv_chall + cli_chall
|
271
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlm_key, session_chal)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Used when the LMv2 response is sent
|
275
|
+
def self.lmv2_user_session_key(user, pass, domain, srv_chall, cli_chall, opt = {})
|
276
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
277
|
+
|
278
|
+
ntlmv2_key = self.ntlmv2_hash(user, pass, domain, opt)
|
279
|
+
hash1 = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_key, srv_chall + cli_chall)
|
280
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_key, hash1)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Used when the NTLMv2 response is sent
|
284
|
+
class << self; alias_method :ntlmv2_user_session_key, :lmv2_user_session_key; end
|
285
|
+
|
286
|
+
# Used when LanMan Key flag is set
|
287
|
+
def self.lanman_session_key(pass, srvchall, opt = {})
|
288
|
+
if opt[:pass_is_hash]
|
289
|
+
halfhash = pass[0,8]
|
290
|
+
else
|
291
|
+
halfhash = lm_hash(pass.upcase[0,7],true)
|
292
|
+
end
|
293
|
+
plain = self.lm_response({
|
294
|
+
:lm_hash => halfhash[0,7],
|
295
|
+
:challenge => srvchall
|
296
|
+
}, true )
|
297
|
+
key = halfhash + ["bdbdbdbdbdbd"].pack("H*")
|
298
|
+
keys = self.gen_keys(key)
|
299
|
+
apply_des(plain, keys).join
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.encrypt_sessionkey(session_key, user_session_key)
|
303
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
304
|
+
cipher = OpenSSL::Cipher::Cipher.new('rc4')
|
305
|
+
cipher.encrypt
|
306
|
+
cipher.key = user_session_key
|
307
|
+
cipher.update(session_key)
|
308
|
+
end
|
309
|
+
|
310
|
+
def self.decrypt_sessionkey(encrypted_session_key, user_session_key)
|
311
|
+
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
312
|
+
cipher = OpenSSL::Cipher::Cipher.new('rc4')
|
313
|
+
cipher.decrypt
|
314
|
+
cipher.key = user_session_key
|
315
|
+
cipher.update(encrypted_session_key)
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.make_weak_sessionkey(session_key,key_size,lanman_key = false)
|
319
|
+
case key_size
|
320
|
+
when 40
|
321
|
+
if lanman_key
|
322
|
+
return session_key[0,5] + "\xe5\x38\xb0"
|
323
|
+
else
|
324
|
+
return session_key[0,5]
|
325
|
+
end
|
326
|
+
when 56
|
327
|
+
if lanman_key
|
328
|
+
return session_key[0,7] + "\xa0"
|
329
|
+
else
|
330
|
+
return session_key[0,7]
|
331
|
+
end
|
332
|
+
else #128
|
333
|
+
return session_key[0,16]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|