ruby_smb 3.3.9 → 3.3.11

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.
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