ruby_smb 3.3.9 → 3.3.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/metasploit-framework_acceptance.yml +47 -0
  4. data/lib/ruby_smb/client.rb +5 -5
  5. data/lib/ruby_smb/dcerpc/client.rb +2 -4
  6. data/lib/ruby_smb/dcerpc/icpr.rb +2 -2
  7. data/lib/ruby_smb/dcerpc/request.rb +4 -1
  8. data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_request.rb +23 -0
  9. data/lib/ruby_smb/dcerpc/samr/samr_get_members_in_group_response.rb +34 -0
  10. data/lib/ruby_smb/dcerpc/samr/samr_open_group_request.rb +26 -0
  11. data/lib/ruby_smb/dcerpc/samr/samr_open_group_response.rb +24 -0
  12. data/lib/ruby_smb/dcerpc/samr.rb +71 -1
  13. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb +0 -3
  14. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request.rb +25 -0
  15. data/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response.rb +25 -0
  16. data/lib/ruby_smb/dcerpc/wkssvc.rb +118 -3
  17. data/lib/ruby_smb/gss/provider/ntlm.rb +5 -9
  18. data/lib/ruby_smb/ntlm/client.rb +3 -74
  19. data/lib/ruby_smb/ntlm.rb +0 -37
  20. data/lib/ruby_smb/server/server_client/tree_connect.rb +2 -2
  21. data/lib/ruby_smb/version.rb +1 -1
  22. data/lib/ruby_smb.rb +0 -2
  23. data/ruby_smb.gemspec +9 -2
  24. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request_spec.rb +0 -8
  25. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response_spec.rb +1 -1
  26. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_identity_handle.rb +7 -0
  27. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_request_spec.rb +71 -0
  28. data/spec/lib/ruby_smb/dcerpc/wkssvc/netr_wksta_user_enum_response_spec.rb +65 -0
  29. data/spec/lib/ruby_smb/dcerpc/wkssvc_spec.rb +58 -1
  30. data/spec/lib/ruby_smb/gss/provider/ntlm/authenticator_spec.rb +119 -4
  31. data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +1 -1
  32. data.tar.gz.sig +0 -0
  33. metadata +19 -7
  34. metadata.gz.sig +0 -0
  35. data/lib/ruby_smb/ntlm/custom/string_encoder.rb +0 -22
  36. data/lib/ruby_smb/utils.rb +0 -15
@@ -1,78 +1,7 @@
1
1
  module RubySMB::NTLM
2
- module Message
3
- def deflag
4
- security_buffers.inject(head_size) do |cur, a|
5
- a[1].offset = cur
6
- cur += a[1].data_size
7
- has_flag?(:UNICODE) ? cur + cur % 2 : cur
8
- end
9
- end
10
-
11
- def serialize
12
- deflag
13
- @alist.map { |n, f| f.serialize }.join + security_buffers.map { |n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '') }.join
14
- end
15
- end
16
-
17
2
  class Client < Net::NTLM::Client
18
- class Session < Net::NTLM::Client::Session
19
- def authenticate!
20
- calculate_user_session_key!
21
- type3_opts = {
22
- :lm_response => is_anonymous? ? "\x00".b : lmv2_resp,
23
- :ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
24
- :domain => domain,
25
- :user => username,
26
- :workstation => workstation,
27
- :flag => (challenge_message.flag & client.flags)
28
- }
29
- t3 = Net::NTLM::Message::Type3.create type3_opts
30
- t3.extend(Message)
31
- if negotiate_key_exchange?
32
- t3.enable(:session_key)
33
- rc4 = OpenSSL::Cipher.new("rc4")
34
- rc4.encrypt
35
- rc4.key = user_session_key
36
- sk = rc4.update exported_session_key
37
- sk << rc4.final
38
- t3.session_key = sk
39
- end
40
- t3
41
- end
42
-
43
- def is_anonymous?
44
- username == '' && password == ''
45
- end
46
-
47
- private
48
-
49
- def use_oem_strings?
50
- # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
51
- !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
52
- end
53
-
54
- def ntlmv2_hash
55
- @ntlmv2_hash ||= RubySMB::NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
56
- end
57
-
58
- def calculate_user_session_key!
59
- if is_anonymous?
60
- # see MS-NLMP section 3.4
61
- @user_session_key = "\x00".b * 16
62
- else
63
- @user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
64
- end
65
- end
66
- end
67
-
68
- def init_context(resp = nil, channel_binding = nil)
69
- if resp.nil?
70
- @session = nil
71
- type1_message
72
- else
73
- @session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
74
- @session.authenticate!
75
- end
76
- end
3
+ # There was a bunch of code in here that was necessary in versions up to and including rubyntlm version 0.6.3.
4
+ # The class is kept because there are references to it that should be kept in place in case future alterations to
5
+ # rubyntlm are required.
77
6
  end
78
7
  end
data/lib/ruby_smb/ntlm.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'ruby_smb/ntlm/custom/string_encoder'
2
-
3
1
  module RubySMB
4
2
  module NTLM
5
3
  # [[MS-NLMP] 2.2.2.5](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832)
@@ -58,41 +56,6 @@ module RubySMB
58
56
  "Version #{major}.#{minor} (Build #{build}); NTLM Current Revision #{ntlm_revision}"
59
57
  end
60
58
  end
61
-
62
- class << self
63
-
64
- # Generate a NTLMv2 Hash
65
- # @param [String] user The username
66
- # @param [String] password The password
67
- # @param [String] target The domain or workstation to authenticate to
68
- # @option opt :unicode (false) Unicode encode the domain
69
- def ntlmv2_hash(user, password, target, opt={})
70
- if Net::NTLM.is_ntlm_hash? password
71
- decoded_password = Net::NTLM::EncodeUtil.decode_utf16le(password)
72
- ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
73
- else
74
- ntlmhash = Net::NTLM.ntlm_hash(password, opt)
75
- end
76
-
77
- if opt[:unicode]
78
- # Uppercase operation on username containing non-ASCII characters
79
- # after being unicode encoded with `EncodeUtil.encode_utf16le`
80
- # doesn't play well. Upcase should be done before encoding.
81
- user_upcase = Net::NTLM::EncodeUtil.decode_utf16le(user).upcase
82
- user_upcase = Net::NTLM::EncodeUtil.encode_utf16le(user_upcase)
83
- else
84
- user_upcase = user.upcase
85
- end
86
- userdomain = user_upcase + target
87
-
88
- unless opt[:unicode]
89
- userdomain = Net::NTLM::EncodeUtil.encode_utf16le(userdomain)
90
- end
91
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
92
- end
93
-
94
- end
95
-
96
59
  end
97
60
  end
98
61
 
@@ -7,7 +7,7 @@ module RubySMB
7
7
  # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b062f3e3-1b65-4a9a-854a-0ee432499d8f
8
8
  response = RubySMB::SMB1::Packet::TreeConnectResponse.new
9
9
 
10
- share_name = RubySMB::Utils.safe_encode(request.data_block.path, 'UTF-8').split('\\', 4).last
10
+ share_name = request.data_block.path.split('\\', 4).last
11
11
  share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
12
12
  if share_provider.nil?
13
13
  logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
@@ -51,7 +51,7 @@ module RubySMB
51
51
  return response
52
52
  end
53
53
 
54
- share_name = RubySMB::Utils.safe_encode(request.path, 'UTF-8').split('\\', 4).last
54
+ share_name = request.path.encode.split('\\', 4).last
55
55
  share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
56
56
 
57
57
  if share_provider.nil?
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.3.9'.freeze
2
+ VERSION = '3.3.11'.freeze
3
3
  end
data/lib/ruby_smb.rb CHANGED
@@ -6,13 +6,11 @@ require 'openssl/ccm'
6
6
  require 'openssl/cmac'
7
7
  require 'windows_error'
8
8
  require 'windows_error/nt_status'
9
- require 'ruby_smb/ntlm/custom/string_encoder'
10
9
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
11
10
  #
12
11
  # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
13
12
  # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
14
13
  module RubySMB
15
- require 'ruby_smb/utils'
16
14
  require 'ruby_smb/error'
17
15
  require 'ruby_smb/create_actions'
18
16
  require 'ruby_smb/dispositions'
data/ruby_smb.gemspec CHANGED
@@ -6,7 +6,14 @@ require 'ruby_smb/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'ruby_smb'
8
8
  spec.version = RubySMB::VERSION
9
- spec.authors = ['Metasploit Hackers', 'David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
9
+ spec.authors = [
10
+ 'Metasploit Hackers',
11
+ 'David Maloney',
12
+ 'James Lee',
13
+ 'Dev Mohanty',
14
+ 'Christophe De La Fuente',
15
+ 'Spencer McIntyre'
16
+ ]
10
17
  spec.email = ['msfdev@metasploit.com']
11
18
  spec.summary = 'A pure Ruby implementation of the SMB Protocol Family'
12
19
  spec.description = ''
@@ -33,7 +40,7 @@ Gem::Specification.new do |spec|
33
40
  spec.add_development_dependency 'rake'
34
41
  spec.add_development_dependency 'yard'
35
42
 
36
- spec.add_runtime_dependency 'rubyntlm'
43
+ spec.add_runtime_dependency 'rubyntlm', '>= 0.6.5'
37
44
  spec.add_runtime_dependency 'windows_error', '>= 0.1.4'
38
45
  spec.add_runtime_dependency 'bindata', '2.4.15'
39
46
  spec.add_runtime_dependency 'openssl-ccm'
@@ -1,11 +1,3 @@
1
- RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do
2
- subject(:packet) { described_class.new }
3
-
4
- it 'is a Ndr::NdrWideStringPtr' do
5
- expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr)
6
- end
7
- end
8
-
9
1
  RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaGetInfoRequest do
10
2
  subject(:packet) { described_class.new }
11
3
 
@@ -305,7 +305,7 @@ RSpec.describe RubySMB::Dcerpc::Wkssvc::LpwkstaInfo do
305
305
  it 'is little endian' do
306
306
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
307
307
  end
308
- it 'it is a Ndr::NdrStruct' do
308
+ it 'is a Ndr::NdrStruct' do
309
309
  expect(described_class).to be < RubySMB::Dcerpc::Ndr::NdrStruct
310
310
  end
311
311
  describe '#level' do
@@ -0,0 +1,7 @@
1
+ RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it 'is a Ndr::NdrWideStringPtr' do
5
+ expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringPtr)
6
+ end
7
+ end
@@ -0,0 +1,71 @@
1
+ RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaUserEnumRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ def random_str(nb = 8)
5
+ nb.times.map { rand('a'.ord..'z'.ord).chr }.join
6
+ end
7
+
8
+ it { is_expected.to respond_to :server_name }
9
+ it { is_expected.to respond_to :user_info }
10
+ it { is_expected.to respond_to :preferred_max_length }
11
+ it { is_expected.to respond_to :result_handle }
12
+ it { is_expected.to respond_to :opnum }
13
+
14
+ it 'is little endian' do
15
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
16
+ end
17
+ it 'is a BinData::Record' do
18
+ expect(packet).to be_a(BinData::Record)
19
+ end
20
+ describe '#server_name' do
21
+ it 'is a WkssvcIdentifyHandle structure' do
22
+ expect(packet.server_name).to be_a RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle
23
+ end
24
+ end
25
+ describe '#user_info' do
26
+ it 'is a WkstaUserEnumStructure structure' do
27
+ expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::WkstaUserEnumStructure
28
+ end
29
+ end
30
+ describe '#preferred_max_length' do
31
+ it 'is a NdrUint32 structure' do
32
+ expect(packet.preferred_max_length).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
33
+ end
34
+
35
+ it 'has a default value of 0xFFFFFFFF' do
36
+ expect(packet.preferred_max_length).to eq(0xFFFFFFFF)
37
+ end
38
+ end
39
+ describe '#result_handle' do
40
+ it 'is a NdrUint32Ptr structure' do
41
+ expect(packet.result_handle).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr
42
+ end
43
+
44
+ it 'has a default value of 0' do
45
+ expect(packet.result_handle).to eq(0)
46
+ end
47
+ end
48
+ describe '#initialize_instance' do
49
+ it 'sets #opnum to NETR_WKSTA_USER_ENUM constant' do
50
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Wkssvc::NETR_WKSTA_USER_ENUM)
51
+ end
52
+ end
53
+ it 'reads itself' do
54
+ packet = described_class.new(
55
+ server_name: 'TestServer',
56
+ user_info: {
57
+ level: RubySMB::Dcerpc::Wkssvc::WKSTA_USER_INFO_0,
58
+ info: {
59
+ wkui0_entries_read: 1,
60
+ wkui0_buffer: [{
61
+ wkui0_username: random_str
62
+ }],
63
+ },
64
+ },
65
+ preferred_max_length: 0xFFFFFFFF,
66
+ result_handle: 0
67
+ )
68
+ binary = packet.to_binary_s
69
+ expect(described_class.read(binary)).to eq(packet)
70
+ end
71
+ end
@@ -0,0 +1,65 @@
1
+ RSpec.describe RubySMB::Dcerpc::Wkssvc::NetrWkstaUserEnumResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ def random_str(nb = 8)
5
+ nb.times.map { rand('a'.ord..'z'.ord).chr }.join
6
+ end
7
+
8
+ it { is_expected.to respond_to :user_info }
9
+ it { is_expected.to respond_to :total_entries }
10
+ it { is_expected.to respond_to :result_handle }
11
+ it { is_expected.to respond_to :error_status }
12
+
13
+ it 'is little endian' do
14
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
15
+ end
16
+ it 'is a BinData::Record' do
17
+ expect(packet).to be_a(BinData::Record)
18
+ end
19
+ describe '#user_info' do
20
+ it 'is a WkstaUserEnumStructure structure' do
21
+ expect(packet.user_info).to be_a RubySMB::Dcerpc::Wkssvc::WkstaUserEnumStructure
22
+ end
23
+ end
24
+ describe '#total_entries' do
25
+ it 'is a NdrUint32Ptr structure' do
26
+ expect(packet.total_entries).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr
27
+ end
28
+ end
29
+ describe '#result_handle' do
30
+ it 'is a NdrUint32Ptr structure' do
31
+ expect(packet.result_handle).to be_a RubySMB::Dcerpc::Ndr::NdrUint32Ptr
32
+ end
33
+ end
34
+ describe '#error_status' do
35
+ it 'is a NdrUint32 structure' do
36
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
37
+ end
38
+ end
39
+ describe '#initialize_instance' do
40
+ it 'sets #opnum to NETR_WKSTA_USER_ENUM constant' do
41
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Wkssvc::NETR_WKSTA_USER_ENUM)
42
+ end
43
+ end
44
+ it 'reads itself' do
45
+ packet = described_class.new(
46
+ user_info: {
47
+ level: RubySMB::Dcerpc::Wkssvc::WKSTA_USER_INFO_1,
48
+ info: {
49
+ wkui1_entries_read: 1,
50
+ wkui1_buffer: [{
51
+ wkui1_username: random_str,
52
+ wkui1_logon_domain: random_str,
53
+ wkui1_oth_domains: random_str,
54
+ wkui1_logon_server: random_str
55
+ }],
56
+ },
57
+ },
58
+ total_entries: 1,
59
+ result_handle: 0,
60
+ error_status: 0
61
+ )
62
+ binary = packet.to_binary_s
63
+ expect(described_class.read(binary)).to eq(packet)
64
+ end
65
+ end
@@ -1,3 +1,11 @@
1
+ RSpec.describe RubySMB::Dcerpc::Wkssvc::WkssvcIdentifyHandle do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it 'is a Ndr::NdrWideStringzPtr' do
5
+ expect(packet).to be_a(RubySMB::Dcerpc::Ndr::NdrWideStringzPtr)
6
+ end
7
+ end
8
+
1
9
  RSpec.describe RubySMB::Dcerpc::Wkssvc do
2
10
  let(:wkssvc) do
3
11
  RubySMB::SMB1::Pipe.new(
@@ -23,7 +31,7 @@ RSpec.describe RubySMB::Dcerpc::Wkssvc do
23
31
  it 'sets the request with the expected values' do
24
32
  wkssvc.netr_wksta_get_info
25
33
  expect(described_class::NetrWkstaGetInfoRequest).to have_received(:new).with(
26
- server_name: "\x00",
34
+ server_name: '',
27
35
  level: described_class::WKSTA_INFO_100
28
36
  )
29
37
  end
@@ -67,4 +75,53 @@ RSpec.describe RubySMB::Dcerpc::Wkssvc do
67
75
  end
68
76
  end
69
77
  end
78
+
79
+ describe '#netr_wksta_user_enum' do
80
+ let(:wkst_netr_wksta_user_enum_request) { double('NetrWkstaUserEnumRequest') }
81
+ let(:response) { double('Response') }
82
+ let(:wkst_netr_wksta_user_enum_response) { double('NetrWkstaUserEnumResponse') }
83
+ let(:info) { double('info') }
84
+ before :example do
85
+ allow(described_class::NetrWkstaUserEnumRequest).to receive(:new).and_return(wkst_netr_wksta_user_enum_request)
86
+ allow(wkssvc).to receive(:dcerpc_request).and_return(response)
87
+ allow(described_class::NetrWkstaUserEnumResponse).to receive(:read).and_return(wkst_netr_wksta_user_enum_response)
88
+ allow(wkst_netr_wksta_user_enum_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
89
+ allow(wkst_netr_wksta_user_enum_response).to receive_message_chain(:user_info, :info => info)
90
+ end
91
+
92
+ it 'sets the request with the expected values' do
93
+ wkssvc.netr_wksta_user_enum
94
+ expect(described_class::NetrWkstaUserEnumRequest).to have_received(:new).with(
95
+ server_name: '',
96
+ user_info: {
97
+ level: described_class::WKSTA_USER_INFO_0,
98
+ tag: described_class::WKSTA_USER_INFO_0,
99
+ info: {
100
+ wkui0_entries_read: 0,
101
+ },
102
+ },
103
+ preferred_max_length: 0xFFFFFFFF,
104
+ result_handle: 0
105
+ )
106
+ end
107
+ it 'send the expected request structure' do
108
+ wkssvc.netr_wksta_user_enum
109
+ expect(wkssvc).to have_received(:dcerpc_request).with(wkst_netr_wksta_user_enum_request)
110
+ end
111
+ context 'when an IOError occurs while parsing the response' do
112
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
113
+ allow(described_class::NetrWkstaUserEnumResponse).to receive(:read).and_raise(IOError)
114
+ expect { wkssvc.netr_wksta_user_enum }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
115
+ end
116
+ end
117
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
118
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
119
+ allow(wkst_netr_wksta_user_enum_response).to receive(:error_status).and_return(WindowsError::Win32::ERROR_INVALID_DATA)
120
+ expect { wkssvc.netr_wksta_user_enum }.to raise_error(RubySMB::Dcerpc::Error::WkssvcError)
121
+ end
122
+ end
123
+ it 'returns the expected handler' do
124
+ expect(wkssvc.netr_wksta_user_enum).to eq(info)
125
+ end
126
+ end
70
127
  end
@@ -9,8 +9,11 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
9
9
  msg.domain = domain
10
10
  end
11
11
  end
12
+ let(:type2_msg) do
13
+ Net::NTLM::Message::Type2.new
14
+ end
12
15
  let(:type3_msg) do
13
- Net::NTLM::Message::Type2.new.response(user: username, password: '', domain: domain)
16
+ type2_msg.response({user: username, password: password, domain: domain}, {ntlmv2: true})
14
17
  end
15
18
 
16
19
  before(:each) do
@@ -65,9 +68,121 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
65
68
  end
66
69
 
67
70
  describe '#process_ntlm_type3' do
68
- it 'should process a NTLM type 3 message and return an error code' do
69
- expect(authenticator.process_ntlm_type3(type3_msg)).to be_a WindowsError::ErrorCode
70
- expect(authenticator.process_ntlm_type3(type3_msg)).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
71
+ context 'when the message is anonymous' do
72
+ let(:type3_msg) do
73
+ type2_msg.response({user: '', password: ''}, {ntlmv2: true})
74
+ end
75
+
76
+ context 'when anonymous access is disabled' do
77
+ before(:each) do
78
+ expect(provider).to_not receive(:allow_guests)
79
+ expect(provider).to receive(:allow_anonymous).and_return(false)
80
+ end
81
+
82
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
83
+ status = authenticator.process_ntlm_type3(type3_msg)
84
+ expect(status).to be_a WindowsError::ErrorCode
85
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
86
+ end
87
+
88
+ after(:each) do
89
+ expect(authenticator.session_key).to be_nil
90
+ end
91
+ end
92
+
93
+ context 'when anonymous access is enabled' do
94
+ before(:each) do
95
+ expect(provider).to_not receive(:allow_guests)
96
+ expect(provider).to receive(:allow_anonymous).and_return(true)
97
+ end
98
+
99
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
100
+ status = authenticator.process_ntlm_type3(type3_msg)
101
+ expect(status).to be_a WindowsError::ErrorCode
102
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
103
+ end
104
+
105
+ after(:each) do
106
+ expect(authenticator.session_key).to eq "\x00".b * 16
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'when the message is a guest' do
112
+ let(:type3_msg) do
113
+ type2_msg.response({user: 'Spencer', password: password}, {ntlmv2: true})
114
+ end
115
+
116
+ context 'when guest access is disabled' do
117
+ before(:each) do
118
+ expect(provider).to_not receive(:allow_anonymous)
119
+ expect(provider).to receive(:allow_guests).and_return(false)
120
+ end
121
+
122
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
123
+ status = authenticator.process_ntlm_type3(type3_msg)
124
+ expect(status).to be_a WindowsError::ErrorCode
125
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
126
+ end
127
+
128
+ after(:each) do
129
+ expect(authenticator.session_key).to be_nil
130
+ end
131
+ end
132
+
133
+ context 'when guest access is enabled' do
134
+ before(:each) do
135
+ expect(provider).to_not receive(:allow_anonymous)
136
+ expect(provider).to receive(:allow_guests).and_return(true)
137
+ end
138
+
139
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
140
+ status = authenticator.process_ntlm_type3(type3_msg)
141
+ expect(status).to be_a WindowsError::ErrorCode
142
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
143
+ end
144
+
145
+ after(:each) do
146
+ expect(authenticator.session_key).to eq "\x00".b * 16
147
+ end
148
+ end
149
+ end
150
+
151
+ context 'when the message is a known user' do
152
+ before(:each) do
153
+ authenticator.instance_variable_set(:@server_challenge, type2_msg[:challenge].serialize)
154
+ end
155
+
156
+ context 'when the password is correct' do
157
+ it 'should process a NTLM type 3 message and return STATUS_SUCCESS' do
158
+ type3_msg.user.force_encoding('UTF-16LE')
159
+ type3_msg.domain.force_encoding('UTF-16LE')
160
+ status = authenticator.process_ntlm_type3(type3_msg)
161
+ expect(status).to be_a WindowsError::ErrorCode
162
+ expect(status).to eq WindowsError::NTStatus::STATUS_SUCCESS
163
+ end
164
+
165
+ after(:each) do
166
+ expect(authenticator.session_key).to be_a String
167
+ expect(authenticator.session_key.length).to eq 16
168
+ end
169
+ end
170
+
171
+ context 'when the password is wrong' do
172
+ let(:type3_msg) do
173
+ type2_msg.response({user: username, password: 'Wrong' + password, domain: domain}, {ntlmv2: true})
174
+ end
175
+
176
+ it 'should process a NTLM type 3 message and return STATUS_LOGON_FAILURE' do
177
+ status = authenticator.process_ntlm_type3(type3_msg)
178
+ expect(status).to be_a WindowsError::ErrorCode
179
+ expect(status).to eq WindowsError::NTStatus::STATUS_LOGON_FAILURE
180
+ end
181
+
182
+ after(:each) do
183
+ expect(authenticator.session_key).to be nil
184
+ end
185
+ end
71
186
  end
72
187
  end
73
188
 
@@ -24,7 +24,7 @@ RSpec.describe RubySMB::NTLM::Client::Session do
24
24
 
25
25
  it 'returns a Type3 message' do
26
26
  expect(session.authenticate!).to be_a Net::NTLM::Message::Type3
27
- expect(session.authenticate!).to be_a RubySMB::NTLM::Message
27
+ expect(session.authenticate!).to be_a Net::NTLM::Message
28
28
  end
29
29
 
30
30
  context 'when it is anonymous' do
data.tar.gz.sig CHANGED
Binary file