rubyntlm 0.6.1 → 0.6.2
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -3
- data/.rspec +2 -2
- data/.travis.yml +13 -12
- data/CHANGELOG.md +118 -6
- data/Gemfile +3 -3
- data/LICENSE +19 -19
- data/Rakefile +25 -22
- data/lib/net/ntlm.rb +266 -266
- data/lib/net/ntlm/blob.rb +28 -28
- data/lib/net/ntlm/channel_binding.rb +65 -65
- data/lib/net/ntlm/client.rb +65 -65
- data/lib/net/ntlm/client/session.rb +237 -237
- data/lib/net/ntlm/encode_util.rb +48 -48
- data/lib/net/ntlm/exceptions.rb +14 -14
- data/lib/net/ntlm/field.rb +34 -34
- data/lib/net/ntlm/field_set.rb +129 -129
- data/lib/net/ntlm/int16_le.rb +25 -25
- data/lib/net/ntlm/int32_le.rb +24 -24
- data/lib/net/ntlm/int64_le.rb +25 -25
- data/lib/net/ntlm/message.rb +129 -129
- data/lib/net/ntlm/message/type0.rb +16 -16
- data/lib/net/ntlm/message/type1.rb +18 -18
- data/lib/net/ntlm/message/type2.rb +102 -102
- data/lib/net/ntlm/message/type3.rb +131 -131
- data/lib/net/ntlm/security_buffer.rb +47 -47
- data/lib/net/ntlm/string.rb +34 -34
- data/lib/net/ntlm/target_info.rb +89 -89
- data/lib/net/ntlm/version.rb +11 -11
- data/rubyntlm.gemspec +29 -28
- data/spec/lib/net/ntlm/blob_spec.rb +16 -16
- data/spec/lib/net/ntlm/channel_binding_spec.rb +17 -17
- data/spec/lib/net/ntlm/client/session_spec.rb +68 -68
- data/spec/lib/net/ntlm/client_spec.rb +64 -64
- data/spec/lib/net/ntlm/encode_util_spec.rb +16 -16
- data/spec/lib/net/ntlm/field_set_spec.rb +33 -33
- data/spec/lib/net/ntlm/field_spec.rb +34 -34
- data/spec/lib/net/ntlm/int16_le_spec.rb +17 -17
- data/spec/lib/net/ntlm/int32_le_spec.rb +18 -18
- data/spec/lib/net/ntlm/int64_le_spec.rb +18 -18
- data/spec/lib/net/ntlm/message/type0_spec.rb +20 -20
- data/spec/lib/net/ntlm/message/type1_spec.rb +131 -131
- data/spec/lib/net/ntlm/message/type2_spec.rb +132 -132
- data/spec/lib/net/ntlm/message/type3_spec.rb +225 -225
- data/spec/lib/net/ntlm/message_spec.rb +16 -16
- data/spec/lib/net/ntlm/security_buffer_spec.rb +64 -64
- data/spec/lib/net/ntlm/string_spec.rb +72 -72
- data/spec/lib/net/ntlm/target_info_spec.rb +76 -76
- data/spec/lib/net/ntlm/version_spec.rb +27 -27
- data/spec/lib/net/ntlm_spec.rb +127 -127
- data/spec/spec_helper.rb +22 -22
- data/spec/support/certificates/sha_256_hash.pem +19 -19
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +25 -25
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +239 -239
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +43 -43
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +35 -35
- metadata +17 -3
@@ -1,102 +1,102 @@
|
|
1
|
-
module Net
|
2
|
-
module NTLM
|
3
|
-
class Message
|
4
|
-
|
5
|
-
# @private false
|
6
|
-
class Type2 < Message
|
7
|
-
|
8
|
-
string :sign, { :size => 8, :value => SSP_SIGN }
|
9
|
-
int32LE :type, { :value => 2 }
|
10
|
-
security_buffer :target_name, { :size => 0, :value => "" }
|
11
|
-
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
|
12
|
-
int64LE :challenge, { :value => 0}
|
13
|
-
int64LE :context, { :value => 0, :active => false }
|
14
|
-
security_buffer :target_info, { :value => "", :active => false }
|
15
|
-
string :os_version, { :size => 8, :value => "", :active => false }
|
16
|
-
|
17
|
-
# Generates a Type 3 response based on the Type 2 Information
|
18
|
-
# @return [Type3]
|
19
|
-
# @option arg [String] :username The username to authenticate with
|
20
|
-
# @option arg [String] :password The user's password
|
21
|
-
# @option arg [String] :domain ('') The domain to authenticate to
|
22
|
-
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
23
|
-
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
|
24
|
-
# @note An empty :domain option authenticates to the local machine.
|
25
|
-
# @note The :use_default_target has precedence over the :domain option
|
26
|
-
def response(arg, opt = {})
|
27
|
-
usr = arg[:user]
|
28
|
-
pwd = arg[:password]
|
29
|
-
domain = arg[:domain] ? arg[:domain].upcase : ""
|
30
|
-
if usr.nil? or pwd.nil?
|
31
|
-
raise ArgumentError, "user and password have to be supplied"
|
32
|
-
end
|
33
|
-
|
34
|
-
if opt[:workstation]
|
35
|
-
ws = opt[:workstation]
|
36
|
-
else
|
37
|
-
ws = Socket.gethostname
|
38
|
-
end
|
39
|
-
|
40
|
-
if opt[:client_challenge]
|
41
|
-
cc = opt[:client_challenge]
|
42
|
-
else
|
43
|
-
cc = rand(MAX64)
|
44
|
-
end
|
45
|
-
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
46
|
-
opt[:client_challenge] = cc
|
47
|
-
|
48
|
-
if has_flag?(:OEM) and opt[:unicode]
|
49
|
-
usr = NTLM::EncodeUtil.decode_utf16le(usr)
|
50
|
-
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
|
51
|
-
ws = NTLM::EncodeUtil.decode_utf16le(ws)
|
52
|
-
domain = NTLM::EncodeUtil.decode_utf16le(domain)
|
53
|
-
opt[:unicode] = false
|
54
|
-
end
|
55
|
-
|
56
|
-
if has_flag?(:UNICODE) and !opt[:unicode]
|
57
|
-
usr = NTLM::EncodeUtil.encode_utf16le(usr)
|
58
|
-
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
|
59
|
-
ws = NTLM::EncodeUtil.encode_utf16le(ws)
|
60
|
-
domain = NTLM::EncodeUtil.encode_utf16le(domain)
|
61
|
-
opt[:unicode] = true
|
62
|
-
end
|
63
|
-
|
64
|
-
if opt[:use_default_target]
|
65
|
-
domain = self.target_name
|
66
|
-
end
|
67
|
-
|
68
|
-
ti = self.target_info
|
69
|
-
|
70
|
-
chal = self[:challenge].serialize
|
71
|
-
|
72
|
-
if opt[:ntlmv2]
|
73
|
-
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
|
74
|
-
lm_res = NTLM::lmv2_response(ar, opt)
|
75
|
-
ntlm_res = NTLM::ntlmv2_response(ar, opt)
|
76
|
-
elsif has_flag?(:NTLM2_KEY)
|
77
|
-
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
78
|
-
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
|
79
|
-
else
|
80
|
-
ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
|
81
|
-
lm_res = NTLM::lm_response(ar)
|
82
|
-
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
83
|
-
ntlm_res = NTLM::ntlm_response(ar)
|
84
|
-
end
|
85
|
-
|
86
|
-
Type3.create({
|
87
|
-
:lm_response => lm_res,
|
88
|
-
:ntlm_response => ntlm_res,
|
89
|
-
:domain => domain,
|
90
|
-
:user => usr,
|
91
|
-
:workstation => ws,
|
92
|
-
:flag => self.flag
|
93
|
-
})
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
|
1
|
+
module Net
|
2
|
+
module NTLM
|
3
|
+
class Message
|
4
|
+
|
5
|
+
# @private false
|
6
|
+
class Type2 < Message
|
7
|
+
|
8
|
+
string :sign, { :size => 8, :value => SSP_SIGN }
|
9
|
+
int32LE :type, { :value => 2 }
|
10
|
+
security_buffer :target_name, { :size => 0, :value => "" }
|
11
|
+
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
|
12
|
+
int64LE :challenge, { :value => 0}
|
13
|
+
int64LE :context, { :value => 0, :active => false }
|
14
|
+
security_buffer :target_info, { :value => "", :active => false }
|
15
|
+
string :os_version, { :size => 8, :value => "", :active => false }
|
16
|
+
|
17
|
+
# Generates a Type 3 response based on the Type 2 Information
|
18
|
+
# @return [Type3]
|
19
|
+
# @option arg [String] :username The username to authenticate with
|
20
|
+
# @option arg [String] :password The user's password
|
21
|
+
# @option arg [String] :domain ('') The domain to authenticate to
|
22
|
+
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
23
|
+
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
|
24
|
+
# @note An empty :domain option authenticates to the local machine.
|
25
|
+
# @note The :use_default_target has precedence over the :domain option
|
26
|
+
def response(arg, opt = {})
|
27
|
+
usr = arg[:user]
|
28
|
+
pwd = arg[:password]
|
29
|
+
domain = arg[:domain] ? arg[:domain].upcase : ""
|
30
|
+
if usr.nil? or pwd.nil?
|
31
|
+
raise ArgumentError, "user and password have to be supplied"
|
32
|
+
end
|
33
|
+
|
34
|
+
if opt[:workstation]
|
35
|
+
ws = opt[:workstation]
|
36
|
+
else
|
37
|
+
ws = Socket.gethostname
|
38
|
+
end
|
39
|
+
|
40
|
+
if opt[:client_challenge]
|
41
|
+
cc = opt[:client_challenge]
|
42
|
+
else
|
43
|
+
cc = rand(MAX64)
|
44
|
+
end
|
45
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
46
|
+
opt[:client_challenge] = cc
|
47
|
+
|
48
|
+
if has_flag?(:OEM) and opt[:unicode]
|
49
|
+
usr = NTLM::EncodeUtil.decode_utf16le(usr)
|
50
|
+
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
|
51
|
+
ws = NTLM::EncodeUtil.decode_utf16le(ws)
|
52
|
+
domain = NTLM::EncodeUtil.decode_utf16le(domain)
|
53
|
+
opt[:unicode] = false
|
54
|
+
end
|
55
|
+
|
56
|
+
if has_flag?(:UNICODE) and !opt[:unicode]
|
57
|
+
usr = NTLM::EncodeUtil.encode_utf16le(usr)
|
58
|
+
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
|
59
|
+
ws = NTLM::EncodeUtil.encode_utf16le(ws)
|
60
|
+
domain = NTLM::EncodeUtil.encode_utf16le(domain)
|
61
|
+
opt[:unicode] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
if opt[:use_default_target]
|
65
|
+
domain = self.target_name
|
66
|
+
end
|
67
|
+
|
68
|
+
ti = self.target_info
|
69
|
+
|
70
|
+
chal = self[:challenge].serialize
|
71
|
+
|
72
|
+
if opt[:ntlmv2]
|
73
|
+
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
|
74
|
+
lm_res = NTLM::lmv2_response(ar, opt)
|
75
|
+
ntlm_res = NTLM::ntlmv2_response(ar, opt)
|
76
|
+
elsif has_flag?(:NTLM2_KEY)
|
77
|
+
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
78
|
+
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
|
79
|
+
else
|
80
|
+
ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
|
81
|
+
lm_res = NTLM::lm_response(ar)
|
82
|
+
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
83
|
+
ntlm_res = NTLM::ntlm_response(ar)
|
84
|
+
end
|
85
|
+
|
86
|
+
Type3.create({
|
87
|
+
:lm_response => lm_res,
|
88
|
+
:ntlm_response => ntlm_res,
|
89
|
+
:domain => domain,
|
90
|
+
:user => usr,
|
91
|
+
:workstation => ws,
|
92
|
+
:flag => self.flag
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
@@ -1,131 +1,131 @@
|
|
1
|
-
module Net
|
2
|
-
module NTLM
|
3
|
-
class Message
|
4
|
-
|
5
|
-
# @private false
|
6
|
-
class Type3 < Message
|
7
|
-
|
8
|
-
string :sign, {:size => 8, :value => SSP_SIGN}
|
9
|
-
int32LE :type, {:value => 3}
|
10
|
-
security_buffer :lm_response, {:value => ""}
|
11
|
-
security_buffer :ntlm_response, {:value => ""}
|
12
|
-
security_buffer :domain, {:value => ""}
|
13
|
-
security_buffer :user, {:value => ""}
|
14
|
-
security_buffer :workstation, {:value => ""}
|
15
|
-
security_buffer :session_key, {:value => "", :active => false }
|
16
|
-
int32LE :flag, {:value => 0, :active => false }
|
17
|
-
string :os_version, {:size => 8, :active => false }
|
18
|
-
|
19
|
-
class << Type3
|
20
|
-
# Builds a Type 3 packet
|
21
|
-
# @note All options must be properly encoded with either unicode or oem encoding
|
22
|
-
# @return [Type3]
|
23
|
-
# @option arg [String] :lm_response The LM hash
|
24
|
-
# @option arg [String] :ntlm_response The NTLM hash
|
25
|
-
# @option arg [String] :domain The domain to authenticate to
|
26
|
-
# @option arg [String] :workstation The name of the calling workstation
|
27
|
-
# @option arg [String] :session_key The session key
|
28
|
-
# @option arg [Integer] :flag Flags for the packet
|
29
|
-
def create(arg, opt ={})
|
30
|
-
t = new
|
31
|
-
t.lm_response = arg[:lm_response]
|
32
|
-
t.ntlm_response = arg[:ntlm_response]
|
33
|
-
t.domain = arg[:domain]
|
34
|
-
t.user = arg[:user]
|
35
|
-
|
36
|
-
if arg[:workstation]
|
37
|
-
t.workstation = arg[:workstation]
|
38
|
-
end
|
39
|
-
|
40
|
-
if arg[:session_key]
|
41
|
-
t.enable(:session_key)
|
42
|
-
t.session_key = arg[:session_key]
|
43
|
-
end
|
44
|
-
|
45
|
-
if arg[:flag]
|
46
|
-
t.enable(:session_key)
|
47
|
-
t.enable(:flag)
|
48
|
-
t.flag = arg[:flag]
|
49
|
-
end
|
50
|
-
t
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# @param server_challenge (see #password?)
|
55
|
-
def blank_password?(server_challenge)
|
56
|
-
password?('', server_challenge)
|
57
|
-
end
|
58
|
-
|
59
|
-
# @param password [String]
|
60
|
-
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
|
61
|
-
# {Type2} message for which this object is a response.
|
62
|
-
# @return [true] if +password+ was the password used to generate this
|
63
|
-
# {Type3} message
|
64
|
-
# @return [false] otherwise
|
65
|
-
def password?(password, server_challenge)
|
66
|
-
case ntlm_version
|
67
|
-
when :ntlm2_session
|
68
|
-
ntlm2_session_password?(password, server_challenge)
|
69
|
-
when :ntlmv2
|
70
|
-
ntlmv2_password?(password, server_challenge)
|
71
|
-
else
|
72
|
-
raise
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# @return [Symbol]
|
77
|
-
def ntlm_version
|
78
|
-
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
|
79
|
-
:ntlm2_session
|
80
|
-
elsif ntlm_response.size == 24
|
81
|
-
:ntlmv1
|
82
|
-
elsif ntlm_response.size > 24
|
83
|
-
:ntlmv2
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def ntlm2_session_password?(password, server_challenge)
|
90
|
-
hash = ntlm_response
|
91
|
-
_lm, empty_hash = NTLM.ntlm2_session(
|
92
|
-
{
|
93
|
-
:ntlm_hash => NTLM.ntlm_hash(password),
|
94
|
-
:challenge => server_challenge,
|
95
|
-
},
|
96
|
-
{
|
97
|
-
:client_challenge => lm_response[0,8]
|
98
|
-
}
|
99
|
-
)
|
100
|
-
hash == empty_hash
|
101
|
-
end
|
102
|
-
|
103
|
-
def ntlmv2_password?(password, server_challenge)
|
104
|
-
|
105
|
-
# The first 16 bytes of the ntlm_response are the HMAC of the blob
|
106
|
-
# that follows it.
|
107
|
-
blob = Blob.new
|
108
|
-
blob.parse(ntlm_response[16..-1])
|
109
|
-
|
110
|
-
empty_hash = NTLM.ntlmv2_response(
|
111
|
-
{
|
112
|
-
# user and domain came from the serialized data here, so
|
113
|
-
# they're already unicode
|
114
|
-
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
|
115
|
-
:challenge => server_challenge,
|
116
|
-
:target_info => blob.target_info
|
117
|
-
},
|
118
|
-
{
|
119
|
-
:client_challenge => blob.challenge,
|
120
|
-
# The blob's timestamp is already in milliseconds since 1601,
|
121
|
-
# so convert it back to epoch time first
|
122
|
-
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
|
123
|
-
}
|
124
|
-
)
|
125
|
-
|
126
|
-
empty_hash == ntlm_response
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
1
|
+
module Net
|
2
|
+
module NTLM
|
3
|
+
class Message
|
4
|
+
|
5
|
+
# @private false
|
6
|
+
class Type3 < Message
|
7
|
+
|
8
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
9
|
+
int32LE :type, {:value => 3}
|
10
|
+
security_buffer :lm_response, {:value => ""}
|
11
|
+
security_buffer :ntlm_response, {:value => ""}
|
12
|
+
security_buffer :domain, {:value => ""}
|
13
|
+
security_buffer :user, {:value => ""}
|
14
|
+
security_buffer :workstation, {:value => ""}
|
15
|
+
security_buffer :session_key, {:value => "", :active => false }
|
16
|
+
int32LE :flag, {:value => 0, :active => false }
|
17
|
+
string :os_version, {:size => 8, :active => false }
|
18
|
+
|
19
|
+
class << Type3
|
20
|
+
# Builds a Type 3 packet
|
21
|
+
# @note All options must be properly encoded with either unicode or oem encoding
|
22
|
+
# @return [Type3]
|
23
|
+
# @option arg [String] :lm_response The LM hash
|
24
|
+
# @option arg [String] :ntlm_response The NTLM hash
|
25
|
+
# @option arg [String] :domain The domain to authenticate to
|
26
|
+
# @option arg [String] :workstation The name of the calling workstation
|
27
|
+
# @option arg [String] :session_key The session key
|
28
|
+
# @option arg [Integer] :flag Flags for the packet
|
29
|
+
def create(arg, opt ={})
|
30
|
+
t = new
|
31
|
+
t.lm_response = arg[:lm_response]
|
32
|
+
t.ntlm_response = arg[:ntlm_response]
|
33
|
+
t.domain = arg[:domain]
|
34
|
+
t.user = arg[:user]
|
35
|
+
|
36
|
+
if arg[:workstation]
|
37
|
+
t.workstation = arg[:workstation]
|
38
|
+
end
|
39
|
+
|
40
|
+
if arg[:session_key]
|
41
|
+
t.enable(:session_key)
|
42
|
+
t.session_key = arg[:session_key]
|
43
|
+
end
|
44
|
+
|
45
|
+
if arg[:flag]
|
46
|
+
t.enable(:session_key)
|
47
|
+
t.enable(:flag)
|
48
|
+
t.flag = arg[:flag]
|
49
|
+
end
|
50
|
+
t
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param server_challenge (see #password?)
|
55
|
+
def blank_password?(server_challenge)
|
56
|
+
password?('', server_challenge)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param password [String]
|
60
|
+
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
|
61
|
+
# {Type2} message for which this object is a response.
|
62
|
+
# @return [true] if +password+ was the password used to generate this
|
63
|
+
# {Type3} message
|
64
|
+
# @return [false] otherwise
|
65
|
+
def password?(password, server_challenge)
|
66
|
+
case ntlm_version
|
67
|
+
when :ntlm2_session
|
68
|
+
ntlm2_session_password?(password, server_challenge)
|
69
|
+
when :ntlmv2
|
70
|
+
ntlmv2_password?(password, server_challenge)
|
71
|
+
else
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Symbol]
|
77
|
+
def ntlm_version
|
78
|
+
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
|
79
|
+
:ntlm2_session
|
80
|
+
elsif ntlm_response.size == 24
|
81
|
+
:ntlmv1
|
82
|
+
elsif ntlm_response.size > 24
|
83
|
+
:ntlmv2
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def ntlm2_session_password?(password, server_challenge)
|
90
|
+
hash = ntlm_response
|
91
|
+
_lm, empty_hash = NTLM.ntlm2_session(
|
92
|
+
{
|
93
|
+
:ntlm_hash => NTLM.ntlm_hash(password),
|
94
|
+
:challenge => server_challenge,
|
95
|
+
},
|
96
|
+
{
|
97
|
+
:client_challenge => lm_response[0,8]
|
98
|
+
}
|
99
|
+
)
|
100
|
+
hash == empty_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def ntlmv2_password?(password, server_challenge)
|
104
|
+
|
105
|
+
# The first 16 bytes of the ntlm_response are the HMAC of the blob
|
106
|
+
# that follows it.
|
107
|
+
blob = Blob.new
|
108
|
+
blob.parse(ntlm_response[16..-1])
|
109
|
+
|
110
|
+
empty_hash = NTLM.ntlmv2_response(
|
111
|
+
{
|
112
|
+
# user and domain came from the serialized data here, so
|
113
|
+
# they're already unicode
|
114
|
+
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
|
115
|
+
:challenge => server_challenge,
|
116
|
+
:target_info => blob.target_info
|
117
|
+
},
|
118
|
+
{
|
119
|
+
:client_challenge => blob.challenge,
|
120
|
+
# The blob's timestamp is already in milliseconds since 1601,
|
121
|
+
# so convert it back to epoch time first
|
122
|
+
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
|
123
|
+
}
|
124
|
+
)
|
125
|
+
|
126
|
+
empty_hash == ntlm_response
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|