ruby_smb 3.3.21 → 3.3.23

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a002233be5acb354928b1a3144a55f1e7224ca543ddd02ca8aed49f10d147bf
4
- data.tar.gz: d410e410c764cec434426a75dab79b46f72700862750b470c068e3eee822d43c
3
+ metadata.gz: 9d886dad5cf6f498c65899db9f1e92e90ada198ec372944b1342aee7f080a75e
4
+ data.tar.gz: f0449b0250fcd31a7828011f0aff42695fe9c42c8bcb1317bacb7290726bacdb
5
5
  SHA512:
6
- metadata.gz: a7e1295bb0a883cb00546a32ead1d647254bce82d8cd1bbad3217c3d8d68a8fb404a488f2b97b0a494e899fe33d39cd7f552b5e0515f202d97bb53525d869ca7
7
- data.tar.gz: 698f56e64e2c419c4faddaaf18d0efa201e162a6cfabfe580a2472f98bb16d6f4729d2166aa546336357326847db6cdd520f37c26e5305a2019b5ec5397513eb
6
+ metadata.gz: f905b9bb1f1e300203da9ba5cecc6c171398e88df7dd810a16b6cbe96974412f0ea5c076dfd7af792601944a86d42c93587fdf379ce5e6879bf9761b8d88dacd
7
+ data.tar.gz: 2a2f83457861f8dd05f95bb3cc67364bde774ff9190986cf7b2704a1ddafc55e69645a1964e33c42a735db2f89fbfe241073bc4687ed034528830dc026e3e9dd
@@ -99,7 +99,8 @@ module RubySMB
99
99
  response = smb1_ntlmssp_final_packet(raw)
100
100
  response_code = response.status_code
101
101
 
102
- @user_id = user_id if response_code == WindowsError::NTStatus::STATUS_SUCCESS
102
+ # Use the UID from the final response (some servers, e.g. Samba, reassign it on success).
103
+ @user_id = response.smb_header.uid if response_code == WindowsError::NTStatus::STATUS_SUCCESS
103
104
 
104
105
  response_code
105
106
  end
@@ -85,7 +85,7 @@ module RubySMB
85
85
 
86
86
  @server_challenge = @provider.generate_server_challenge
87
87
  msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
88
- target_info = Net::NTLM::TargetInfo.new('')
88
+ target_info = Net::NTLM::TargetInfo.new(nil)
89
89
  target_info.av_pairs.merge!({
90
90
  Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => @provider.netbios_domain.encode('UTF-16LE').b,
91
91
  Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => @provider.netbios_hostname.encode('UTF-16LE').b,
@@ -3,8 +3,10 @@ module RubySMB
3
3
  # Extract and store useful information about the peer/server from the
4
4
  # NTLM Type 2 (challenge) TargetInfo fields.
5
5
  #
6
- # @param target_info_str [String] the Target Info string
6
+ # @param target_info_str [String, nil] the Target Info string
7
7
  def store_target_info(target_info_str)
8
+ return if target_info_str.nil? || target_info_str.empty?
9
+
8
10
  target_info = Net::NTLM::TargetInfo.new(target_info_str)
9
11
  {
10
12
  Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => :@default_name,
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.3.21'.freeze
2
+ VERSION = '3.3.23'.freeze
3
3
  end
@@ -1614,16 +1614,27 @@ RSpec.describe RubySMB::Client do
1614
1614
  expect(smb1_client.session_key).to eq ntlm_client.session_key
1615
1615
  end
1616
1616
 
1617
+ it 'continues authentication when the server advertises empty target info' do
1618
+ challenge = Net::NTLM::Message.decode64(type2_string)
1619
+ challenge.set_flag(:TARGET_INFO)
1620
+ challenge.target_info = ''.b
1621
+ allow(smb1_client).to receive(:smb1_type2_message).and_return(challenge.encode64)
1622
+
1623
+ expect(smb1_client.smb1_authenticate).to eq WindowsError::NTStatus::STATUS_SUCCESS
1624
+ end
1625
+
1617
1626
  it 'stores the OS version number from the challenge message' do
1618
1627
  smb1_client.smb1_authenticate
1619
1628
  expect(smb1_client.os_version).to eq '6.1.7601'
1620
1629
  end
1621
1630
 
1622
- it 'stores the user ID if the status code is \'STATUS_SUCCESS\'' do
1631
+ it 'stores the user ID from the final response packet if the status code is \'STATUS_SUCCESS\'' do
1632
+ # Session UID must come from the final response, not the challenge (Samba reassigns it on success).
1623
1633
  response_packet.smb_header.uid = user_id
1634
+ final_response_packet.smb_header.uid = user_id + 1
1624
1635
  final_response_packet.smb_header.nt_status = WindowsError::NTStatus::STATUS_SUCCESS.value
1625
1636
  smb1_client.smb1_authenticate
1626
- expect(smb1_client.user_id).to eq user_id
1637
+ expect(smb1_client.user_id).to eq user_id + 1
1627
1638
  end
1628
1639
 
1629
1640
  it 'does not store the user ID if the status code is not \'STATUS_SUCCESS\'' do
@@ -1858,6 +1869,15 @@ RSpec.describe RubySMB::Client do
1858
1869
  expect(smb2_client.session_key).to eq ntlm_client.session_key
1859
1870
  end
1860
1871
 
1872
+ it 'continues authentication when the server advertises empty target info' do
1873
+ challenge = Net::NTLM::Message.decode64(type2_string)
1874
+ challenge.set_flag(:TARGET_INFO)
1875
+ challenge.target_info = ''.b
1876
+ allow(smb2_client).to receive(:smb2_type2_message).and_return(challenge.encode64)
1877
+
1878
+ expect(smb2_client.smb2_authenticate).to eq WindowsError::NTStatus::STATUS_SUCCESS
1879
+ end
1880
+
1861
1881
  it 'stores the OS version number from the challenge message' do
1862
1882
  smb2_client.smb2_authenticate
1863
1883
  expect(smb2_client.os_version).to eq '6.1.7601'
@@ -2066,6 +2086,25 @@ RSpec.describe RubySMB::Client do
2066
2086
  client.store_target_info(target_info_str)
2067
2087
  end
2068
2088
 
2089
+ it 'preserves existing peer information when given empty target info' do
2090
+ client.store_target_info(target_info_str)
2091
+ expect { client.store_target_info(''.b) }.to_not change {
2092
+ [client.default_name, client.default_domain, client.dns_host_name, client.dns_domain_name, client.dns_tree_name]
2093
+ }
2094
+ end
2095
+
2096
+ it 'accepts nil target info' do
2097
+ expect { client.store_target_info(nil) }.to_not raise_error
2098
+ end
2099
+
2100
+ it 'accepts empty target info encoded with an end-of-list marker' do
2101
+ expect { client.store_target_info("\x00\x00\x00\x00".b) }.to_not raise_error
2102
+ end
2103
+
2104
+ it 'rejects nonempty invalid target info' do
2105
+ expect { client.store_target_info("\xff\xff\x00\x00".b) }.to raise_error(Net::NTLM::InvalidTargetDataError)
2106
+ end
2107
+
2069
2108
  it 'sets the expected Client\'s attribute' do
2070
2109
  client.store_target_info(target_info_str)
2071
2110
  expect(client.default_name).to eq 'TESTNAME'
@@ -173,6 +173,14 @@ RSpec.describe RubySMB::Dcerpc::Client do
173
173
  expect(auth_client.process_ntlm_type2(type2_message)).to start_with("NTLMSSP\x00\x03\x00\x00\x00")
174
174
  end
175
175
 
176
+ it 'returns a type3 message when the server advertises empty target info' do
177
+ challenge = Net::NTLM::Message.parse(type2_message)
178
+ challenge.set_flag(:TARGET_INFO)
179
+ challenge.target_info = ''.b
180
+
181
+ expect(auth_client.process_ntlm_type2(challenge.serialize)).to start_with("NTLMSSP\x00\x03\x00\x00\x00")
182
+ end
183
+
176
184
  it 'stores the session key' do
177
185
  auth_client.process_ntlm_type2(type2_message)
178
186
  expect(auth_client.instance_variable_get(:@session_key).size).to eq(16)
@@ -65,6 +65,19 @@ RSpec.describe RubySMB::Gss::Provider::NTLM::Authenticator do
65
65
  it 'should process a NTLM type 1 message and return a type2 message' do
66
66
  expect(authenticator.process_ntlm_type1(type1_msg)).to be_a Net::NTLM::Message::Type2
67
67
  end
68
+
69
+ it 'includes the server names in the serialized target info' do
70
+ type2_msg = Net::NTLM::Message.parse(authenticator.process_ntlm_type1(type1_msg).serialize)
71
+ target_info = Net::NTLM::TargetInfo.new(type2_msg.target_info)
72
+
73
+ expect(target_info.av_pairs).to include(
74
+ Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME => provider.netbios_domain.encode('UTF-16LE').b,
75
+ Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME => provider.netbios_hostname.encode('UTF-16LE').b,
76
+ Net::NTLM::TargetInfo::MSV_AV_DNS_DOMAIN_NAME => provider.dns_domain.encode('UTF-16LE').b,
77
+ Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME => provider.dns_hostname.encode('UTF-16LE').b
78
+ )
79
+ expect(target_info.av_pairs[Net::NTLM::TargetInfo::MSV_AV_TIMESTAMP].bytesize).to eq 8
80
+ end
68
81
  end
69
82
 
70
83
  describe '#process_ntlm_type3' do
@@ -8,7 +8,9 @@ RSpec.describe RubySMB::NTLM::Client::Session do
8
8
  AGwAbwBjAGEAbAADADgAVwBJAE4ALQAzAE0AUwBQADgASwAyAEwAQwBHAEMA
9
9
  LgBtAHMAZgBsAGEAYgAuAGwAbwBjAGEAbAAHAAgAS6UAWjxl2AEAAAAA
10
10
  }) }
11
- subject(:client) { RubySMB::NTLM::Client.new('rubysmb', 'rubysmb', flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
11
+ let(:username) { 'rubysmb' }
12
+ let(:password) { 'rubysmb' }
13
+ let(:client) { RubySMB::NTLM::Client.new(username, password, flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
12
14
  subject(:session) { described_class.new(client, message) }
13
15
 
14
16
  describe '#authenticate!' do
@@ -17,72 +19,49 @@ RSpec.describe RubySMB::NTLM::Client::Session do
17
19
  session.authenticate!
18
20
  end
19
21
 
20
- it 'checks if it is anonymous' do
21
- expect(session).to receive(:is_anonymous?).at_least(1).times.and_call_original
22
- session.authenticate!
23
- end
24
-
25
22
  it 'returns a Type3 message' do
26
23
  expect(session.authenticate!).to be_a Net::NTLM::Message::Type3
27
24
  expect(session.authenticate!).to be_a Net::NTLM::Message
28
25
  end
29
26
 
30
27
  context 'when it is anonymous' do
31
- before(:each) { allow(session).to receive(:is_anonymous?).and_return(true) }
32
- after(:each) { session.authenticate! }
28
+ let(:username) { '' }
29
+ let(:password) { '' }
33
30
 
34
31
  it 'uses the correct lm response' do
35
- expect(session).to_not receive(:lmv2_resp)
36
- expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
37
- expect(params).to include :lm_response
38
- expect(params[:lm_response]).to eq "\x00".b
39
- method.call(params)
40
- end
32
+ expect(session.authenticate!.lm_response).to eq "\x00".b
41
33
  end
42
34
 
43
35
  it 'uses the correct ntlm response' do
44
- expect(session).to_not receive(:ntlmv2_resp)
45
- expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
46
- expect(params).to include :ntlm_response
47
- expect(params[:ntlm_response]).to eq ''
48
- method.call(params)
49
- end
36
+ expect(session.authenticate!.ntlm_response).to eq ''
50
37
  end
51
38
  end
52
39
 
53
40
  context 'when it is not anonymous' do
54
- before(:each) { allow(session).to receive(:is_anonymous?).and_return(false) }
55
- after(:each) { session.authenticate! }
56
-
57
41
  it 'uses the correct lm response' do
58
- expect(session).to receive(:lmv2_resp).and_call_original
59
- expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
60
- expect(params).to include :lm_response
61
- expect(params[:lm_response].length).to be > 16
62
- method.call(params)
63
- end
42
+ expect(session.authenticate!.lm_response.length).to be > 16
64
43
  end
65
44
 
66
45
  it 'uses the correct ntlm response' do
67
- expect(session).to receive(:ntlmv2_resp).and_call_original
68
- expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
69
- expect(params).to include :ntlm_response
70
- expect(params[:ntlm_response].length).to be > 16
71
- method.call(params)
72
- end
46
+ expect(session.authenticate!.ntlm_response.length).to be > 16
73
47
  end
74
48
  end
75
49
  end
76
50
 
77
51
  describe '#calculate_user_session_key!' do
78
- it 'returns an all zero key when it is anonymous' do
79
- expect(session).to receive(:is_anonymous?).and_return(true)
80
- expect(session.send(:calculate_user_session_key!)).to eq "\x00".b * 16
52
+ context 'when it is anonymous' do
53
+ let(:username) { '' }
54
+ let(:password) { '' }
55
+
56
+ it 'returns an all zero key' do
57
+ expect(session.send(:calculate_user_session_key!)).to eq "\x00".b * 16
58
+ end
81
59
  end
82
60
 
83
61
  it 'returns a session key' do
84
- expect(session).to receive(:is_anonymous?).and_return(false)
85
- expect(session.send(:calculate_user_session_key!)).to_not eq "\x00".b * 16
62
+ session_key = session.send(:calculate_user_session_key!)
63
+ expect(session_key.bytesize).to eq 16
64
+ expect(session_key).to_not eq "\x00".b * 16
86
65
  end
87
66
  end
88
67
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_smb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.21
4
+ version: 3.3.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2026-06-08 00:00:00.000000000 Z
16
+ date: 2026-09-28 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: redcarpet