rubyntlm 0.6.6 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +48 -48
- data/lib/net/ntlm/blob.rb +11 -10
- data/lib/net/ntlm/channel_binding.rb +13 -9
- data/lib/net/ntlm/client/session.rb +34 -141
- data/lib/net/ntlm/client/session_crypto.rb +126 -0
- data/lib/net/ntlm/client.rb +8 -9
- data/lib/net/ntlm/encode_util.rb +39 -38
- data/lib/net/ntlm/exceptions.rb +3 -0
- data/lib/net/ntlm/field.rb +29 -31
- data/lib/net/ntlm/field_set.rb +39 -18
- data/lib/net/ntlm/int16_le.rb +8 -8
- data/lib/net/ntlm/int32_le.rb +8 -7
- data/lib/net/ntlm/int64_le.rb +8 -7
- data/lib/net/ntlm/md4.rb +102 -64
- data/lib/net/ntlm/message/type0.rb +4 -7
- data/lib/net/ntlm/message/type1.rb +8 -9
- data/lib/net/ntlm/message/type2.rb +87 -67
- data/lib/net/ntlm/message/type3.rb +43 -41
- data/lib/net/ntlm/message.rb +116 -111
- data/lib/net/ntlm/rc4.rb +51 -44
- data/lib/net/ntlm/response.rb +112 -0
- data/lib/net/ntlm/security_buffer.rb +13 -15
- data/lib/net/ntlm/string.rb +8 -7
- data/lib/net/ntlm/target_info.rb +52 -44
- data/lib/net/ntlm/version.rb +3 -1
- data/lib/net/ntlm.rb +37 -125
- data/lib/rubyntlm.rb +3 -1
- metadata +5 -44
- data/.devcontainer/devcontainer.json +0 -22
- data/.github/dependabot.yml +0 -10
- data/.github/workflows/build.yml +0 -55
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/Gemfile +0 -20
- data/Rakefile +0 -37
- data/examples/http.rb +0 -86
- data/examples/imap.rb +0 -73
- data/examples/smtp.rb +0 -94
- data/rubyntlm.gemspec +0 -28
- data/spec/lib/net/ntlm/blob_spec.rb +0 -14
- data/spec/lib/net/ntlm/channel_binding_spec.rb +0 -15
- data/spec/lib/net/ntlm/client/session_spec.rb +0 -85
- data/spec/lib/net/ntlm/client_spec.rb +0 -62
- data/spec/lib/net/ntlm/encode_util_spec.rb +0 -14
- data/spec/lib/net/ntlm/field_set_spec.rb +0 -31
- data/spec/lib/net/ntlm/field_spec.rb +0 -32
- data/spec/lib/net/ntlm/int16_le_spec.rb +0 -16
- data/spec/lib/net/ntlm/int32_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/int64_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/md4_spec.rb +0 -16
- data/spec/lib/net/ntlm/message/type0_spec.rb +0 -19
- data/spec/lib/net/ntlm/message/type1_spec.rb +0 -129
- data/spec/lib/net/ntlm/message/type2_spec.rb +0 -131
- data/spec/lib/net/ntlm/message/type3_spec.rb +0 -336
- data/spec/lib/net/ntlm/message_spec.rb +0 -15
- data/spec/lib/net/ntlm/security_buffer_spec.rb +0 -62
- data/spec/lib/net/ntlm/string_spec.rb +0 -70
- data/spec/lib/net/ntlm/target_info_spec.rb +0 -74
- data/spec/lib/net/ntlm/version_spec.rb +0 -26
- data/spec/lib/net/ntlm_spec.rb +0 -133
- data/spec/spec_helper.rb +0 -51
- data/spec/support/certificates/sha_256_hash.pem +0 -19
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +0 -25
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +0 -239
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +0 -43
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +0 -35
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
|
|
3
|
-
RSpec.describe Net::NTLM::Message::Type2 do
|
|
4
|
-
|
|
5
|
-
fields = [
|
|
6
|
-
{ :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true },
|
|
7
|
-
{ :name => :type, :class => Net::NTLM::Int32LE, :value => 2, :active => true },
|
|
8
|
-
{ :name => :challenge, :class => Net::NTLM::Int64LE, :value => 0, :active => true },
|
|
9
|
-
{ :name => :context, :class => Net::NTLM::Int64LE, :value => 0, :active => false },
|
|
10
|
-
{ :name => :flag, :class => Net::NTLM::Int32LE, :value => Net::NTLM::DEFAULT_FLAGS[:TYPE2], :active => true },
|
|
11
|
-
{ :name => :target_name, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
12
|
-
{ :name => :target_info, :class => Net::NTLM::SecurityBuffer, :value => '', :active => false },
|
|
13
|
-
{ :name => :os_version, :class => Net::NTLM::String, :value => '', :active => false },
|
|
14
|
-
]
|
|
15
|
-
flags = [
|
|
16
|
-
:UNICODE
|
|
17
|
-
]
|
|
18
|
-
it_behaves_like 'a fieldset', fields
|
|
19
|
-
it_behaves_like 'a message', flags
|
|
20
|
-
|
|
21
|
-
let(:type2_packet) {"TlRMTVNTUAACAAAAHAAcADgAAAAFgooCJ+UA1//+ZM4AAAAAAAAAAJAAkABUAAAABgGxHQAAAA9WAEEARwBSAEEATgBUAC0AMgAwADAAOABSADIAAgAcAFYAQQBHAFIAQQBOAFQALQAyADAAMAA4AFIAMgABABwAVgBBAEcAUgBBAE4AVAAtADIAMAAwADgAUgAyAAQAHAB2AGEAZwByAGEAbgB0AC0AMgAwADAAOABSADIAAwAcAHYAYQBnAHIAYQBuAHQALQAyADAAMAA4AFIAMgAHAAgAZBMdFHQnzgEAAAAA"}
|
|
22
|
-
let(:type3_packet) {"TlRMTVNTUAADAAAAGAAYAEQAAADAAMAAXAAAAAAAAAAcAQAADgAOABwBAAAUABQAKgEAAAAAAAA+AQAABYKKAgAAAADVS27TfQGmWxSSbXmolTUQyxJmD8ISQuBKKHFKC8GksUZISYc8Ps9RAQEAAAAAAAAANasTdCfOAcsSZg/CEkLgAAAAAAIAHABWAEEARwBSAEEATgBUAC0AMgAwADAAOABSADIAAQAcAFYAQQBHAFIAQQBOAFQALQAyADAAMAA4AFIAMgAEABwAdgBhAGcAcgBhAG4AdAAtADIAMAAwADgAUgAyAAMAHAB2AGEAZwByAGEAbgB0AC0AMgAwADAAOABSADIABwAIAGQTHRR0J84BAAAAAAAAAAB2AGEAZwByAGEAbgB0AGsAbwBiAGUALgBsAG8AYwBhAGwA"}
|
|
23
|
-
|
|
24
|
-
it 'should deserialize' do
|
|
25
|
-
t2 = Net::NTLM::Message.decode64(type2_packet)
|
|
26
|
-
expect(t2.class).to eq(Net::NTLM::Message::Type2)
|
|
27
|
-
expect(t2.challenge).to eq(14872292244261496103)
|
|
28
|
-
expect(t2.context).to eq(0)
|
|
29
|
-
expect(t2.flag).to eq(42631685)
|
|
30
|
-
expect(t2.os_version).to eq(['0601b11d0000000f'].pack('H*'))
|
|
31
|
-
expect(t2.sign).to eq("NTLMSSP\0")
|
|
32
|
-
|
|
33
|
-
t2_target_info = Net::NTLM::EncodeUtil.decode_utf16le(t2.target_info)
|
|
34
|
-
if RUBY_VERSION == "1.8.7"
|
|
35
|
-
expect(t2_target_info).to eq("\x02\x1CVAGRANT-2008R2\x01\x1CVAGRANT-2008R2\x04\x1Cvagrant-2008R2\x03\x1Cvagrant-2008R2\a\b\e$(D+&\e(B\0\0")
|
|
36
|
-
else
|
|
37
|
-
expect(t2_target_info).to eq("\u0002\u001CVAGRANT-2008R2\u0001\u001CVAGRANT-2008R2\u0004\u001Cvagrant-2008R2\u0003\u001Cvagrant-2008R2\a\b፤ᐝ❴ǎ\0\0")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
expect(Net::NTLM::EncodeUtil.decode_utf16le(t2.target_name)).to eq("VAGRANT-2008R2")
|
|
41
|
-
expect(t2.type).to eq(2)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'should serialize' do
|
|
45
|
-
source = Net::NTLM::Message.decode64(type2_packet)
|
|
46
|
-
|
|
47
|
-
t2 = Net::NTLM::Message::Type2.new
|
|
48
|
-
t2.challenge = source.challenge
|
|
49
|
-
t2.context = source.context
|
|
50
|
-
t2.flag = source.flag
|
|
51
|
-
t2.os_version = source.os_version
|
|
52
|
-
t2.sign = source.sign
|
|
53
|
-
t2.target_info = source.target_info
|
|
54
|
-
t2.target_name = source.target_name
|
|
55
|
-
t2.type = source.type
|
|
56
|
-
t2.enable(:context)
|
|
57
|
-
t2.enable(:target_info)
|
|
58
|
-
t2.enable(:os_version)
|
|
59
|
-
|
|
60
|
-
expect(t2.encode64).to eq(type2_packet)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it 'should generate a type 3 response' do
|
|
64
|
-
t2 = Net::NTLM::Message.decode64(type2_packet)
|
|
65
|
-
|
|
66
|
-
type3_known = Net::NTLM::Message.decode64(type3_packet)
|
|
67
|
-
type3_known.flag = 0x028a8205
|
|
68
|
-
type3_known.enable(:session_key)
|
|
69
|
-
type3_known.enable(:flag)
|
|
70
|
-
|
|
71
|
-
t3 = t2.response({:user => 'vagrant', :password => 'vagrant', :domain => ''}, {:ntlmv2 => true, :workstation => 'kobe.local'})
|
|
72
|
-
expect(t3.domain).to eq(type3_known.domain)
|
|
73
|
-
expect(t3.flag).to eq(type3_known.flag)
|
|
74
|
-
expect(t3.sign).to eq("NTLMSSP\0")
|
|
75
|
-
expect(t3.workstation).to eq("k\0o\0b\0e\0.\0l\0o\0c\0a\0l\0")
|
|
76
|
-
expect(t3.user).to eq("v\0a\0g\0r\0a\0n\0t\0")
|
|
77
|
-
expect(t3.session_key).to eq('')
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'should upcase domain when provided' do
|
|
81
|
-
t2 = Net::NTLM::Message.decode64(type2_packet)
|
|
82
|
-
t3 = t2.response({:user => 'vagrant', :password => 'vagrant', :domain => 'domain'}, {:ntlmv2 => true, :workstation => 'kobe.local'})
|
|
83
|
-
expect(t3.domain).to eq("D\0O\0M\0A\0I\0N\0")
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
describe '.parse' do
|
|
87
|
-
subject(:message) { described_class.parse(data) }
|
|
88
|
-
# http://davenport.sourceforge.net/ntlm.html#appendixC7
|
|
89
|
-
context 'NTLM2 Session Response Authentication; NTLM2 Signing and Sealing Using the 128-bit NTLM2 Session Response User Session Key With Key Exchange Negotiated' do
|
|
90
|
-
let(:data) do
|
|
91
|
-
[
|
|
92
|
-
'4e544c4d53535000020000000c000c0030000000358289e0677f1c557a5ee96c' \
|
|
93
|
-
'0000000000000000460046003c00000054004500530054004e00540002000c00' \
|
|
94
|
-
'54004500530054004e00540001000c004d0045004d0042004500520003001e00' \
|
|
95
|
-
'6d0065006d006200650072002e0074006500730074002e0063006f006d000000' \
|
|
96
|
-
'0000'
|
|
97
|
-
].pack('H*')
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it 'should set the magic' do
|
|
101
|
-
expect(message.sign).to eql(Net::NTLM::SSP_SIGN)
|
|
102
|
-
end
|
|
103
|
-
it 'should set the type' do
|
|
104
|
-
expect(message.type).to eq(2)
|
|
105
|
-
end
|
|
106
|
-
it 'should set the target name' do
|
|
107
|
-
# TESTNT
|
|
108
|
-
expect(message.target_name).to eq(["54004500530054004e005400"].pack('H*'))
|
|
109
|
-
end
|
|
110
|
-
it 'should set the flags' do
|
|
111
|
-
expect(message.flag).to eq(0xe0898235)
|
|
112
|
-
end
|
|
113
|
-
it 'should set the challenge' do
|
|
114
|
-
expect(message.challenge).to eq(0x6ce95e7a551c7f67)
|
|
115
|
-
end
|
|
116
|
-
it 'should set an empty context' do
|
|
117
|
-
expect(message.context).to be_zero
|
|
118
|
-
end
|
|
119
|
-
it 'should set target info' do
|
|
120
|
-
ti = [
|
|
121
|
-
'02000c0054004500530054004e00540001000c004d0045004d00420045005200' \
|
|
122
|
-
'03001e006d0065006d006200650072002e0074006500730074002e0063006f00' \
|
|
123
|
-
'6d0000000000'
|
|
124
|
-
].pack('H*')
|
|
125
|
-
expect(message.target_info).to eq(ti)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
end
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
RSpec.describe Net::NTLM::Message::Type3 do
|
|
2
|
-
|
|
3
|
-
fields = [
|
|
4
|
-
{ :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true },
|
|
5
|
-
{ :name => :type, :class => Net::NTLM::Int32LE, :value => 3, :active => true },
|
|
6
|
-
{ :name => :lm_response, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
7
|
-
{ :name => :ntlm_response, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
8
|
-
{ :name => :domain, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
9
|
-
{ :name => :user, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
10
|
-
{ :name => :workstation, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true },
|
|
11
|
-
{ :name => :session_key, :class => Net::NTLM::SecurityBuffer, :value => '', :active => false },
|
|
12
|
-
{ :name => :flag, :class => Net::NTLM::Int32LE, :value => 0, :active => false },
|
|
13
|
-
]
|
|
14
|
-
flags = []
|
|
15
|
-
it_behaves_like 'a fieldset', fields
|
|
16
|
-
it_behaves_like 'a message', flags
|
|
17
|
-
|
|
18
|
-
describe '.parse' do
|
|
19
|
-
subject(:message) { described_class.parse(data) }
|
|
20
|
-
|
|
21
|
-
context 'with NTLMv2 data' do
|
|
22
|
-
let(:data) do
|
|
23
|
-
# Captured NTLMSSP blob from smbclient with username 'administrator'
|
|
24
|
-
# and a blank password, i.e.:
|
|
25
|
-
# smbclient -U 'administrator%' -L //192.168.100.140/
|
|
26
|
-
[
|
|
27
|
-
'4e544c4d53535000030000001800180040000000c400c4005800000012001200' \
|
|
28
|
-
'1c0100001a001a002e0100001a001a0048010000100010006201000015820860' \
|
|
29
|
-
'ced203d860b80c7350050754b238202a8c1c63134f0ae0f086a3fb147e8b2f9f' \
|
|
30
|
-
'de3ef1b1b43c83dc010100000000000080512dba020ed0011c5bc2c8339fd29a' \
|
|
31
|
-
'0000000002001e00570049004e002d00420035004a004e003300520048004700' \
|
|
32
|
-
'46003300310001001e00570049004e002d00420035004a004e00330052004800' \
|
|
33
|
-
'470046003300310004001e00570049004e002d00420035004a004e0033005200' \
|
|
34
|
-
'4800470046003300310003001e00570049004e002d00420035004a004e003300' \
|
|
35
|
-
'52004800470046003300310007000800a209e5ba020ed0010000000057004f00' \
|
|
36
|
-
'52004b00470052004f0055005000610064006d0069006e006900730074007200' \
|
|
37
|
-
'610074006f0072004100550053002d004c004500450054002d00310030003300' \
|
|
38
|
-
'31007036615cd6d9b19a685ded4312311cd7'
|
|
39
|
-
].pack('H*')
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
let(:server_challenge) { ['f588469dc96fe809'].pack('H*') }
|
|
43
|
-
|
|
44
|
-
it 'should set the magic' do
|
|
45
|
-
expect(message.sign).to eql(Net::NTLM::SSP_SIGN)
|
|
46
|
-
end
|
|
47
|
-
it 'should set the type' do
|
|
48
|
-
expect(message.type).to eq(3)
|
|
49
|
-
end
|
|
50
|
-
it 'should set the LM response' do
|
|
51
|
-
lm_response = ['ced203d860b80c7350050754b238202a8c1c63134f0ae0f0'].pack('H*')
|
|
52
|
-
expect(message.lm_response).to eq(lm_response)
|
|
53
|
-
end
|
|
54
|
-
it 'should set the NTLM response' do
|
|
55
|
-
ntlm_response = [
|
|
56
|
-
'86a3fb147e8b2f9fde3ef1b1b43c83dc010100000000000080512dba020ed001' \
|
|
57
|
-
'1c5bc2c8339fd29a0000000002001e00570049004e002d00420035004a004e00' \
|
|
58
|
-
'330052004800470046003300310001001e00570049004e002d00420035004a00' \
|
|
59
|
-
'4e00330052004800470046003300310004001e00570049004e002d0042003500' \
|
|
60
|
-
'4a004e00330052004800470046003300310003001e00570049004e002d004200' \
|
|
61
|
-
'35004a004e00330052004800470046003300310007000800a209e5ba020ed001' \
|
|
62
|
-
'00000000'
|
|
63
|
-
].pack('H*')
|
|
64
|
-
expect(message.ntlm_response).to eq(ntlm_response)
|
|
65
|
-
end
|
|
66
|
-
it 'should set the user' do
|
|
67
|
-
# administrator
|
|
68
|
-
user = ['610064006d0069006e006900730074007200610074006f007200'].pack('H*')
|
|
69
|
-
expect(message.user).to eq(user)
|
|
70
|
-
end
|
|
71
|
-
it 'should set the domain' do
|
|
72
|
-
# WORKGROUP
|
|
73
|
-
domain = ['57004f0052004b00470052004f0055005000'].pack('H*')
|
|
74
|
-
expect(message.domain).to eq(domain)
|
|
75
|
-
end
|
|
76
|
-
it 'should set the workstation' do
|
|
77
|
-
# AUS-LEET-1031
|
|
78
|
-
workstation = ['4100550053002d004c004500450054002d003100300033003100'].pack('H*')
|
|
79
|
-
expect(message.workstation).to eq(workstation)
|
|
80
|
-
end
|
|
81
|
-
it 'should set the session key' do
|
|
82
|
-
session_key = ['7036615cd6d9b19a685ded4312311cd7'].pack('H*')
|
|
83
|
-
expect(message.session_key).to eq(session_key)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it 'should set the flags' do
|
|
87
|
-
expect(message.flag).to eq(0x60088215)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'should NOT set the OS version structure' do
|
|
91
|
-
expect(message.os_version).to be_nil
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
describe '#blank_password?' do
|
|
95
|
-
it 'should be true' do
|
|
96
|
-
expect(message.blank_password?(server_challenge)).to be true
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it 'rejects a non-empty password for an empty-password response' do
|
|
101
|
-
expect(message.password?('test1234', server_challenge)).to be false
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
describe '#ntlm_version' do
|
|
105
|
-
let(:ver) { message.ntlm_version }
|
|
106
|
-
it 'should be :ntlmv2' do
|
|
107
|
-
expect(ver).to eq(:ntlmv2)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# http://davenport.sourceforge.net/ntlm.html#appendixC7
|
|
114
|
-
context 'NTLM2 Session Response Authentication; NTLM2 Signing and Sealing Using the 128-bit NTLM2 Session Response User Session Key With Key Exchange Negotiated' do
|
|
115
|
-
|
|
116
|
-
let(:data) do
|
|
117
|
-
[
|
|
118
|
-
'4e544c4d5353500003000000180018006000000018001800780000000c000c00' \
|
|
119
|
-
'40000000080008004c0000000c000c00540000001000100090000000358288e0' \
|
|
120
|
-
'54004500530054004e00540074006500730074004d0045004d00420045005200' \
|
|
121
|
-
'404d1b6f6915258000000000000000000000000000000000ea8cc49f24da157f' \
|
|
122
|
-
'13436637f77693d8b992d619e584c7ee727a5240822ec7af4e9100c43e6fee7f'
|
|
123
|
-
].pack('H*')
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
it 'should set the LM response' do
|
|
127
|
-
lm_response = ['404d1b6f6915258000000000000000000000000000000000'].pack('H*')
|
|
128
|
-
expect(message.lm_response).to eq(lm_response)
|
|
129
|
-
end
|
|
130
|
-
it 'should set the NTLM response' do
|
|
131
|
-
ntlm_response = [ 'ea8cc49f24da157f13436637f77693d8b992d619e584c7ee' ].pack('H*')
|
|
132
|
-
expect(message.ntlm_response).to eq(ntlm_response)
|
|
133
|
-
end
|
|
134
|
-
it 'should set the domain' do
|
|
135
|
-
# TESTNT
|
|
136
|
-
domain = ['54004500530054004e005400'].pack('H*')
|
|
137
|
-
expect(message.domain).to eq(domain)
|
|
138
|
-
end
|
|
139
|
-
it 'should set the user' do
|
|
140
|
-
# test
|
|
141
|
-
user = ['7400650073007400'].pack('H*')
|
|
142
|
-
expect(message.user).to eq(user)
|
|
143
|
-
end
|
|
144
|
-
it 'should set the workstation' do
|
|
145
|
-
# MEMBER
|
|
146
|
-
workstation = ['4d0045004d00420045005200'].pack('H*')
|
|
147
|
-
expect(message.workstation).to eq(workstation)
|
|
148
|
-
end
|
|
149
|
-
it 'should set the session key' do
|
|
150
|
-
session_key = ['727a5240822ec7af4e9100c43e6fee7f'].pack('H*')
|
|
151
|
-
expect(message.session_key).to eq(session_key)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
let(:server_challenge) { ['677f1c557a5ee96c'].pack('H*') }
|
|
155
|
-
describe '#password?' do
|
|
156
|
-
it 'should be true for "test1234"' do
|
|
157
|
-
expect(message.password?('test1234', server_challenge)).to be true
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
describe '#blank_password?' do
|
|
161
|
-
it 'should be false' do
|
|
162
|
-
expect(message.blank_password?(server_challenge)).to be false
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
describe '#ntlm_version' do
|
|
167
|
-
let(:ver) { message.ntlm_version }
|
|
168
|
-
it 'should be :ntlm2_session' do
|
|
169
|
-
expect(ver).to eq(:ntlm2_session)
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# http://davenport.sourceforge.net/ntlm.html#appendixC9
|
|
176
|
-
context 'NTLMv2 Authentication; NTLM1 Signing and Sealing Using the 40-bit NTLMv2 User Session Key' do
|
|
177
|
-
let(:server_challenge) { ['0033b02d17275b77'].pack('H*') }
|
|
178
|
-
|
|
179
|
-
describe '#password?' do
|
|
180
|
-
it 'accepts the known fixture password' do
|
|
181
|
-
expect(message.password?('test1234', server_challenge)).to be true
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
it 'rejects an incorrect password' do
|
|
185
|
-
expect(message.password?('wrong', server_challenge)).to be false
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
it 'rejects an empty password' do
|
|
189
|
-
expect(message.blank_password?(server_challenge)).to be false
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
let(:data) do
|
|
194
|
-
[
|
|
195
|
-
'4e544c4d5353500003000000180018006000000076007600780000000c000c00' \
|
|
196
|
-
'40000000080008004c0000000c000c005400000000000000ee00000035828000' \
|
|
197
|
-
'54004500530054004e00540074006500730074004d0045004d00420045005200' \
|
|
198
|
-
'5d55a02b60a40526ac9a1e4d15fa45a0f2e6329726c598e8f77c67dad00b9321' \
|
|
199
|
-
'6242b197fe6addfa0101000000000000502db638677bc301f2e6329726c598e8' \
|
|
200
|
-
'0000000002000c0054004500530054004e00540001000c004d0045004d004200' \
|
|
201
|
-
'4500520003001e006d0065006d006200650072002e0074006500730074002e00' \
|
|
202
|
-
'63006f006d000000000000000000'
|
|
203
|
-
].pack 'H*'
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it 'should set the NTLM response' do
|
|
207
|
-
ntlm_response = [
|
|
208
|
-
'f77c67dad00b93216242b197fe6addfa0101000000000000502db638677bc301' \
|
|
209
|
-
'f2e6329726c598e80000000002000c0054004500530054004e00540001000c00' \
|
|
210
|
-
'4d0045004d0042004500520003001e006d0065006d006200650072002e007400' \
|
|
211
|
-
'6500730074002e0063006f006d000000000000000000'
|
|
212
|
-
].pack 'H*'
|
|
213
|
-
expect(message.ntlm_response).to eq(ntlm_response)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
it 'should set the domain' do
|
|
217
|
-
# TESTNT
|
|
218
|
-
domain = ['54004500530054004e005400'].pack('H*')
|
|
219
|
-
expect(message.domain).to eq(domain)
|
|
220
|
-
end
|
|
221
|
-
it 'should set the user' do
|
|
222
|
-
# test
|
|
223
|
-
user = ['7400650073007400'].pack('H*')
|
|
224
|
-
expect(message.user).to eq(user)
|
|
225
|
-
end
|
|
226
|
-
it 'should set the workstation' do
|
|
227
|
-
# MEMBER
|
|
228
|
-
workstation = ['4d0045004d00420045005200'].pack('H*')
|
|
229
|
-
expect(message.workstation).to eq(workstation)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
describe '#ntlm_version' do
|
|
233
|
-
let(:ver) { message.ntlm_version }
|
|
234
|
-
it 'should be :ntlmv2' do
|
|
235
|
-
expect(ver).to eq(:ntlmv2)
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
describe '#password? with serialized NTLMv2 responses' do
|
|
244
|
-
let(:type2) do
|
|
245
|
-
Net::NTLM::Message::Type2.new.tap do |message|
|
|
246
|
-
message.challenge = 0x0123456789abcdef
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
let(:server_challenge) { Net::NTLM.pack_int64le(type2.challenge) }
|
|
250
|
-
subject(:message) do
|
|
251
|
-
described_class.parse(type2.response(
|
|
252
|
-
{ :user => 'victim', :domain => 'CORP', :password => password },
|
|
253
|
-
{ :ntlmv2 => true, :client_challenge => 0x1122334455667788, :timestamp => 0 }
|
|
254
|
-
).serialize)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
['secret', 'odd', "p\u00e4ss\u5bc6\u7801\u{1f512}", ''].each do |password|
|
|
258
|
-
context "with password #{password.inspect}" do
|
|
259
|
-
let(:password) { password }
|
|
260
|
-
|
|
261
|
-
it 'accepts the password used to generate the response' do
|
|
262
|
-
expect(message.password?(password, server_challenge)).to be true
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
it 'rejects a different non-empty password' do
|
|
266
|
-
expect(message.password?('incorrect', server_challenge)).to be false
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
it 'recognizes only an empty password as blank' do
|
|
270
|
-
expect(message.blank_password?(server_challenge)).to eq(password.empty?)
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
it 'rejects a different server challenge' do
|
|
274
|
-
expect(message.password?(password, "\x00" * 8)).to be false
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
it 'accepts an integer server challenge' do
|
|
278
|
-
expect(message.password?(password, type2.challenge)).to be true
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
it 'rejects a modified proof' do
|
|
282
|
-
message.ntlm_response.setbyte(0, message.ntlm_response.getbyte(0) ^ 1)
|
|
283
|
-
expect(message.password?(password, server_challenge)).to be false
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
it 'rejects a modified blob' do
|
|
287
|
-
message.ntlm_response.setbyte(24, message.ntlm_response.getbyte(24) ^ 1)
|
|
288
|
-
expect(message.password?(password, server_challenge)).to be false
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
describe '#serialize' do
|
|
295
|
-
context 'when the username contains non-ASCI characters' do
|
|
296
|
-
let(:t3) {
|
|
297
|
-
t2 = Net::NTLM::Message::Type2.new
|
|
298
|
-
t2.response(
|
|
299
|
-
{
|
|
300
|
-
:user => 'Hélène',
|
|
301
|
-
:password => '123456',
|
|
302
|
-
:domain => ''
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
:ntlmv2 => true,
|
|
306
|
-
:workstation => 'testlab.local'
|
|
307
|
-
}
|
|
308
|
-
)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
it 'serializes without error' do
|
|
312
|
-
expect { t3.serialize }.not_to raise_error
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
subject(:message) { described_class.create(opts) }
|
|
317
|
-
context 'with the UNICODE flag set' do
|
|
318
|
-
let(:opts) { {lm_response: "\x00".b, ntlm_response: '', domain: '', workstation: '', user: '', flag: Net::NTLM::DEFAULT_FLAGS[:TYPE3] | Net::NTLM::FLAGS[:UNICODE] } }
|
|
319
|
-
|
|
320
|
-
it 'should pad the domain field to a multiple of 2' do
|
|
321
|
-
message.serialize
|
|
322
|
-
expect(message[:domain][:offset].value % 2).to eq 0
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
it 'should pad the user field to a multiple of 2' do
|
|
326
|
-
message.serialize
|
|
327
|
-
expect(message[:user][:offset].value % 2).to eq 0
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
it 'should pad the workstation field to a multiple of 2' do
|
|
331
|
-
message.serialize
|
|
332
|
-
expect(message[:workstation][:offset].value % 2).to eq 0
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
end
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
RSpec.describe Net::NTLM::SecurityBuffer do
|
|
2
|
-
|
|
3
|
-
fields = [
|
|
4
|
-
{ :name => :length, :class => Net::NTLM::Int16LE, :value => 0, :active => true },
|
|
5
|
-
{ :name => :allocated, :class => Net::NTLM::Int16LE, :value => 0, :active => true },
|
|
6
|
-
{ :name => :offset, :class => Net::NTLM::Int32LE, :value => 0, :active => true },
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
it_behaves_like 'a fieldset', fields
|
|
10
|
-
it_behaves_like 'a field', 'WORKSTATION', true
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
subject(:domain_security_buffer) do
|
|
14
|
-
Net::NTLM::SecurityBuffer.new({
|
|
15
|
-
:value => 'WORKSTATION',
|
|
16
|
-
:active => true
|
|
17
|
-
})
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
context 'when setting the value directly' do
|
|
21
|
-
before(:each) do
|
|
22
|
-
domain_security_buffer.value = 'DOMAIN1'
|
|
23
|
-
end
|
|
24
|
-
it 'should change the value' do
|
|
25
|
-
expect(domain_security_buffer.value).to eq('DOMAIN1')
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it 'should adjust the length field to the size of the new value' do
|
|
29
|
-
expect(domain_security_buffer.length).to eq(7)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'should adjust the allocated field to the size of the new value' do
|
|
33
|
-
expect(domain_security_buffer.allocated).to eq(7)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context '#data_size' do
|
|
38
|
-
it 'should return the size of the value if active' do
|
|
39
|
-
expect(domain_security_buffer.data_size).to eq(11)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'should return 0 if inactive' do
|
|
43
|
-
domain_security_buffer.active = false
|
|
44
|
-
expect(domain_security_buffer.data_size).to eq(0)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
context '#parse' do
|
|
49
|
-
it 'should read in a properly formatted string' do
|
|
50
|
-
# Length of the string is 8
|
|
51
|
-
length = "\x08\x00"
|
|
52
|
-
# Space allocated is 8
|
|
53
|
-
allocated = "\x08\x00"
|
|
54
|
-
# The offset that the actual value begins at is also 8
|
|
55
|
-
offset = "\x08\x00\x00\x00"
|
|
56
|
-
string_to_parse = "#{length}#{allocated}#{offset}FooBarBaz"
|
|
57
|
-
expect(domain_security_buffer.parse(string_to_parse)).to eq(8)
|
|
58
|
-
expect(domain_security_buffer.value).to eq('FooBarBa')
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
end
|
|
62
|
-
end
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
RSpec.describe Net::NTLM::String do
|
|
2
|
-
|
|
3
|
-
it_behaves_like 'a field', 'Foo', false
|
|
4
|
-
|
|
5
|
-
let(:active) {
|
|
6
|
-
Net::NTLM::String.new({
|
|
7
|
-
:value => 'Test',
|
|
8
|
-
:active => true,
|
|
9
|
-
:size => 4
|
|
10
|
-
})
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let(:inactive) {
|
|
14
|
-
Net::NTLM::String.new({
|
|
15
|
-
:value => 'Test',
|
|
16
|
-
:active => false,
|
|
17
|
-
:size => 4
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
context '#serialize' do
|
|
22
|
-
it 'should return the value when active' do
|
|
23
|
-
expect(active.serialize).to eq('Test')
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it 'should return an empty string when inactive' do
|
|
27
|
-
expect(inactive.serialize).to eq('')
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it 'should coerce non-string values into strings' do
|
|
31
|
-
active.value = 15
|
|
32
|
-
expect(active.serialize).to eq('15')
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'should return empty string on a nil' do
|
|
36
|
-
active.value = nil
|
|
37
|
-
expect(active.serialize).to eq('')
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
context '#value=' do
|
|
42
|
-
it 'should set active to false if it empty' do
|
|
43
|
-
active.value = ''
|
|
44
|
-
expect(active.active).to eq(false)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it 'should adjust the size based on the value set' do
|
|
48
|
-
expect(active.size).to eq(4)
|
|
49
|
-
active.value = 'Foobar'
|
|
50
|
-
expect(active.size).to eq(6)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
context '#parse' do
|
|
55
|
-
it 'should read in a string of the proper size' do
|
|
56
|
-
expect(active.parse('tseT')).to eq(4)
|
|
57
|
-
expect(active.value).to eq('tseT')
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it 'should not read in a string that is too small' do
|
|
61
|
-
expect(active.parse('B')).to eq(0)
|
|
62
|
-
expect(active.value).to eq('Test')
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it 'should be able to read from an offset and only for the given size' do
|
|
66
|
-
expect(active.parse('FooBarBaz',3)).to eq(4)
|
|
67
|
-
expect(active.value).to eq('BarB')
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
RSpec.describe Net::NTLM::TargetInfo do
|
|
2
|
-
let(:key1) { Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME }
|
|
3
|
-
let(:value1) { 'some data' }
|
|
4
|
-
let(:key2) { Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME }
|
|
5
|
-
let(:value2) { 'some other data' }
|
|
6
|
-
let(:data) do
|
|
7
|
-
dt = key1.dup
|
|
8
|
-
dt << [value1.length].pack('S')
|
|
9
|
-
dt << value1
|
|
10
|
-
dt << key2.dup
|
|
11
|
-
dt << [value2.length].pack('S')
|
|
12
|
-
dt << value2
|
|
13
|
-
dt << Net::NTLM::TargetInfo::MSV_AV_EOL
|
|
14
|
-
dt << [0].pack('S')
|
|
15
|
-
dt.force_encoding(Encoding::ASCII_8BIT)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
subject { Net::NTLM::TargetInfo.new(data) }
|
|
19
|
-
|
|
20
|
-
describe 'invalid data' do
|
|
21
|
-
|
|
22
|
-
context 'invalid pair id' do
|
|
23
|
-
let(:data) { "\xFF\x00" }
|
|
24
|
-
|
|
25
|
-
it 'returns an error' do
|
|
26
|
-
expect{subject}.to raise_error Net::NTLM::InvalidTargetDataError
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
describe '#av_pairs' do
|
|
32
|
-
|
|
33
|
-
it 'returns the pair values with the given keys' do
|
|
34
|
-
expect(subject.av_pairs[key1]).to eq value1
|
|
35
|
-
expect(subject.av_pairs[key2]).to eq value2
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
context "target data is nil" do
|
|
39
|
-
subject { Net::NTLM::TargetInfo.new(nil) }
|
|
40
|
-
|
|
41
|
-
it 'returns the pair values with the given keys' do
|
|
42
|
-
expect(subject.av_pairs).to be_empty
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
describe '#to_s' do
|
|
48
|
-
let(:data) do
|
|
49
|
-
dt = key1.dup
|
|
50
|
-
dt << [value1.length].pack('S')
|
|
51
|
-
dt << value1
|
|
52
|
-
dt << key2.dup
|
|
53
|
-
dt << [value2.length].pack('S')
|
|
54
|
-
dt << value2
|
|
55
|
-
dt.force_encoding(Encoding::ASCII_8BIT)
|
|
56
|
-
end
|
|
57
|
-
let(:new_key) { Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS }
|
|
58
|
-
let(:new_value) { 'bindings' }
|
|
59
|
-
let(:new_data) do
|
|
60
|
-
dt = data
|
|
61
|
-
dt << new_key
|
|
62
|
-
dt << [new_value.length].pack('S')
|
|
63
|
-
dt << new_value
|
|
64
|
-
dt << Net::NTLM::TargetInfo::MSV_AV_EOL
|
|
65
|
-
dt << [0].pack('S')
|
|
66
|
-
dt.force_encoding(Encoding::ASCII_8BIT)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'returns bytes with any new data added' do
|
|
70
|
-
subject.av_pairs[new_key] = new_value
|
|
71
|
-
expect(subject.to_s).to eq new_data
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|