ruby_smb 3.2.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ce8d4ea99335efaf6bbc1ef003231ffa8a809382760e54dc26be04b76e53529
4
- data.tar.gz: f284dabf7fe032ae02933a517abb2371f7c7c37eb641876f1cbf2b8cc0e4c2ac
3
+ metadata.gz: 7b1e12b49f282f15459e21c5f16c6a6fc0f1074b107b97d85395d75a8531df1c
4
+ data.tar.gz: bccbfe2ef2823c390974786165e822eaa2fb7d1e1a96e8171daabbe871c6b324
5
5
  SHA512:
6
- metadata.gz: 5cf115ffd4c634f593bceef2bb503d2c0a2b19c5cfa73391887d35ce2691472fabda03e47462b716f8c870d7dedbc5dc815f644887b259d1268658752396ea17
7
- data.tar.gz: 46ba6053ca246692ef29c545c8a9682b6675c4bf50d10d3611107bf537470226986aeece854516d59d99afa1642ee2d72fed99f39c1731112292fcbb6d8138d2
6
+ metadata.gz: fc4ba500076138ce97e1dc369733675600c4892669d9b5db815b4d09c1dc1b47efd0068da6cb7530fafdcdfc326d00b42b91937e44ed5fe2fdb9b4e5b37ffd9a
7
+ data.tar.gz: b851e6cc8f2846b7b6f7c36b01b14adb089c5d75257315940a282793bb62cce5bdf1f944c7ca3c95f75464914e26753f4ce1577ac446deea2c42c5a84986f82e
checksums.yaml.gz.sig CHANGED
Binary file
@@ -33,17 +33,15 @@ jobs:
33
33
  fail-fast: true
34
34
  matrix:
35
35
  ruby:
36
- - 2.6
37
36
  - 2.7
38
37
  - 3.0
39
38
  - 3.1
40
39
  os:
41
- - ubuntu-18.04
42
- - ubuntu-22.04
40
+ - ubuntu-20.04
41
+ - ubuntu-latest
43
42
  exclude:
44
- - { os: ubuntu-22.04, ruby: 2.6 }
45
- - { os: ubuntu-22.04, ruby: 2.7 }
46
- - { os: ubuntu-22.04, ruby: 3.0 }
43
+ - { os: ubuntu-latest, ruby: 2.7 }
44
+ - { os: ubuntu-latest, ruby: 3.0 }
47
45
  test_cmd:
48
46
  - bundle exec rspec
49
47
 
@@ -4,6 +4,7 @@ module RubySMB
4
4
  class Client
5
5
  require 'ruby_smb/ntlm'
6
6
  require 'ruby_smb/signing'
7
+ require 'ruby_smb/utils'
7
8
  require 'ruby_smb/client/negotiation'
8
9
  require 'ruby_smb/client/authentication'
9
10
  require 'ruby_smb/client/tree_connect'
@@ -13,6 +14,7 @@ module RubySMB
13
14
  require 'ruby_smb/client/encryption'
14
15
 
15
16
  include RubySMB::Signing
17
+ include RubySMB::NTLM
16
18
  include RubySMB::Client::Negotiation
17
19
  include RubySMB::Client::Authentication
18
20
  include RubySMB::Client::TreeConnect
@@ -322,7 +324,7 @@ module RubySMB
322
324
  @pid = rand(0xFFFF)
323
325
  @domain = domain
324
326
  @local_workstation = local_workstation
325
- @password = password.encode('utf-8') || ''.encode('utf-8')
327
+ @password = RubySMB::Utils.safe_encode((password||''), 'utf-8')
326
328
  @sequence_counter = 0
327
329
  @session_id = 0x00
328
330
  @session_key = ''
@@ -332,7 +334,7 @@ module RubySMB
332
334
  @smb1 = smb1
333
335
  @smb2 = smb2
334
336
  @smb3 = smb3
335
- @username = username.encode('utf-8') || ''.encode('utf-8')
337
+ @username = RubySMB::Utils.safe_encode((username||''), 'utf-8')
336
338
  @max_buffer_size = MAX_BUFFER_SIZE
337
339
  # These sizes will be modified during negotiation
338
340
  @server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
@@ -415,8 +417,8 @@ module RubySMB
415
417
  local_workstation: self.local_workstation, ntlm_flags: NTLM::DEFAULT_CLIENT_FLAGS)
416
418
  @domain = domain
417
419
  @local_workstation = local_workstation
418
- @password = pass.encode('utf-8') || ''.encode('utf-8')
419
- @username = user.encode('utf-8') || ''.encode('utf-8')
420
+ @password = RubySMB::Utils.safe_encode((pass||''), 'utf-8')
421
+ @username = RubySMB::Utils.safe_encode((user||''), 'utf-8')
420
422
 
421
423
  @ntlm_client = RubySMB::NTLM::Client.new(
422
424
  @username,
@@ -5,14 +5,16 @@ module RubySMB
5
5
  class Client
6
6
  require 'bindata'
7
7
  require 'windows_error'
8
- require 'net/ntlm'
8
+ require 'ruby_smb/ntlm'
9
9
  require 'ruby_smb/dcerpc'
10
10
  require 'ruby_smb/gss'
11
11
  require 'ruby_smb/peer_info'
12
+ require 'ruby_smb/utils'
12
13
 
13
14
  include Dcerpc
14
15
  include Epm
15
16
  include PeerInfo
17
+ include Utils
16
18
 
17
19
  # The default maximum size of a RPC message that the Client accepts (in bytes)
18
20
  MAX_BUFFER_SIZE = 64512
@@ -118,15 +120,15 @@ module RubySMB
118
120
  @read_timeout = read_timeout
119
121
  @domain = domain
120
122
  @local_workstation = local_workstation
121
- @username = username.encode('utf-8')
122
- @password = password.encode('utf-8')
123
+ @username = RubySMB::Utils.safe_encode(username, 'utf-8')
124
+ @password = RubySMB::Utils.safe_encode(password, 'utf-8')
123
125
  @max_buffer_size = MAX_BUFFER_SIZE
124
126
  @call_id = 1
125
127
  @ctx_id = 0
126
128
  @auth_ctx_id_base = rand(0xFFFFFFFF)
127
129
 
128
130
  unless username.empty? && password.empty?
129
- @ntlm_client = Net::NTLM::Client.new(
131
+ @ntlm_client = RubySMB::NTLM::Client.new(
130
132
  @username,
131
133
  @password,
132
134
  workstation: @local_workstation,
@@ -35,7 +35,7 @@ module RubySMB
35
35
  def cert_server_request(attributes:, authority:, csr:)
36
36
  cert_server_request_request = CertServerRequestRequest.new(
37
37
  pwsz_authority: authority,
38
- pctb_attribs: { pb: (attributes.map { |k,v| "#{k}:#{v}" }.join("\n").encode('UTF-16le').force_encoding('ASCII-8bit') + "\x00\x00".b) },
38
+ pctb_attribs: { pb: (RubySMB::Utils.safe_encode(attributes.map { |k,v| "#{k}:#{v}" }.join("\n"), 'UTF-16le').force_encoding('ASCII-8bit') + "\x00\x00".b) },
39
39
  pctb_request: { pb: csr.to_der }
40
40
  )
41
41
 
@@ -247,12 +247,22 @@ module RubySMB::Dcerpc::Ndr
247
247
 
248
248
  def do_read(io)
249
249
  if is_a?(ConfPlugin) && should_process_max_count?
250
- set_max_count(io.readbytes(4).unpack('L<').first)
250
+ max_count = io.readbytes(4).unpack1('L<')
251
+ BinData.trace_message do |tracer|
252
+ tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
253
+ end
254
+ set_max_count(max_count)
251
255
  end
252
256
 
253
257
  if is_a?(VarPlugin)
254
258
  @offset = io.readbytes(4).unpack('L<').first
259
+ BinData.trace_message do |tracer|
260
+ tracer.trace_obj("#{debug_name}.offset", @offset.to_s)
261
+ end
255
262
  @actual_count = @read_until_index = io.readbytes(4).unpack('L<').first
263
+ BinData.trace_message do |tracer|
264
+ tracer.trace_obj("#{debug_name}.actual_count", @actual_count.to_s)
265
+ end
256
266
  end
257
267
 
258
268
  if has_elements_to_read?
@@ -523,7 +533,11 @@ module RubySMB::Dcerpc::Ndr
523
533
 
524
534
  def do_read(io)
525
535
  if should_process_max_count?
526
- set_max_count(io.readbytes(4).unpack('L<').first)
536
+ max_count = io.readbytes(4).unpack1('L<')
537
+ BinData.trace_message do |tracer|
538
+ tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
539
+ end
540
+ set_max_count(max_count)
527
541
  end
528
542
  super
529
543
  end
@@ -582,7 +596,13 @@ module RubySMB::Dcerpc::Ndr
582
596
 
583
597
  def do_read(io)
584
598
  @offset = io.readbytes(4).unpack('L<').first
599
+ BinData.trace_message do |tracer|
600
+ tracer.trace_obj("#{debug_name}.offset", @offset.to_s)
601
+ end
585
602
  @actual_count = io.readbytes(4).unpack('L<').first
603
+ BinData.trace_message do |tracer|
604
+ tracer.trace_obj("#{debug_name}.actual_count", @actual_count.to_s)
605
+ end
586
606
  super if @actual_count > 0
587
607
  end
588
608
 
@@ -702,7 +722,11 @@ module RubySMB::Dcerpc::Ndr
702
722
 
703
723
  def do_read(io)
704
724
  if should_process_max_count?
705
- set_max_count(io.readbytes(4).unpack('L<').first)
725
+ max_count = io.readbytes(4).unpack1('L<')
726
+ BinData.trace_message do |tracer|
727
+ tracer.trace_obj("#{debug_name}.max_count", max_count.to_s)
728
+ end
729
+ set_max_count(max_count)
706
730
 
707
731
  # Align the structure according to the alignment rules for the structure
708
732
  if respond_to?(:referent_bytes_align)
@@ -1034,6 +1058,9 @@ module RubySMB::Dcerpc::Ndr
1034
1058
  end
1035
1059
  else
1036
1060
  @ref_id = io.readbytes(4).unpack('L<').first
1061
+ BinData.trace_message do |tracer|
1062
+ tracer.trace_obj("#{debug_name}.ref_id", @ref_id.to_s)
1063
+ end
1037
1064
  parent_obj = nil
1038
1065
  if parent&.is_a?(ConstructedTypePlugin)
1039
1066
  parent_obj = parent.get_top_level_constructed_type
@@ -320,7 +320,7 @@ module RubySMB
320
320
 
321
321
  def self.encrypt_password(password, key)
322
322
  # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/5fe3c4c4-e71b-440d-b2fd-8448bfaf6e04
323
- password = password.encode('UTF-16LE').force_encoding('ASCII-8bit')
323
+ password = RubySMB::Utils.safe_encode(password, 'UTF-16LE').force_encoding('ASCII-8bit')
324
324
  buffer = password.rjust(512, "\x00") + [ password.length ].pack('V')
325
325
  salt = SecureRandom.random_bytes(16)
326
326
  key = OpenSSL::Digest::MD5.new(salt + key).digest
@@ -18,6 +18,7 @@ module RubySMB
18
18
  end
19
19
 
20
20
  class Authenticator < Authenticator::Base
21
+
21
22
  def reset!
22
23
  super
23
24
  @server_challenge = nil
@@ -141,7 +142,10 @@ module RubySMB
141
142
  case type3_msg.ntlm_version
142
143
  when :ntlmv1
143
144
  my_ntlm_response = Net::NTLM::ntlm_response(
144
- ntlm_hash: Net::NTLM::ntlm_hash(account.password.encode('UTF-16LE'), unicode: true),
145
+ ntlm_hash: Net::NTLM::ntlm_hash(
146
+ RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
147
+ unicode: true
148
+ ),
145
149
  challenge: @server_challenge
146
150
  )
147
151
  matches = my_ntlm_response == type3_msg.ntlm_response
@@ -151,9 +155,9 @@ module RubySMB
151
155
  their_blob = type3_msg.ntlm_response[digest.digest_length..-1]
152
156
 
153
157
  ntlmv2_hash = Net::NTLM.ntlmv2_hash(
154
- account.username.encode('UTF-16LE'),
155
- account.password.encode('UTF-16LE'),
156
- type3_msg.domain.encode('UTF-16LE'), # don't use the account domain because of the special '.' value
158
+ RubySMB::Utils.safe_encode(account.username, 'UTF-16LE'),
159
+ RubySMB::Utils.safe_encode(account.password, 'UTF-16LE'),
160
+ RubySMB::Utils.safe_encode(type3_msg.domain, 'UTF-16LE'), # don't use the account domain because of the special '.' value
157
161
  {client_challenge: their_blob[16...24], unicode: true}
158
162
  )
159
163
 
@@ -305,7 +309,10 @@ module RubySMB
305
309
  username = username.downcase
306
310
  domain = @default_domain if domain.nil? || domain == '.'.encode(domain.encoding)
307
311
  domain = domain.downcase
308
- @accounts.find { |account| account.username.encode(username.encoding).downcase == username && account.domain.encode(domain.encoding).downcase == domain }
312
+ @accounts.find do |account|
313
+ RubySMB::Utils.safe_encode(account.username, username.encoding).downcase == username &&
314
+ RubySMB::Utils.safe_encode(account.domain, domain.encoding).downcase == domain
315
+ end
309
316
  end
310
317
 
311
318
  #
@@ -51,6 +51,10 @@ module RubySMB::NTLM
51
51
  !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
52
52
  end
53
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
+
54
58
  def calculate_user_session_key!
55
59
  if is_anonymous?
56
60
  # see MS-NLMP section 3.4
@@ -0,0 +1,19 @@
1
+ require 'net/ntlm'
2
+
3
+ module Custom
4
+ module NTLM
5
+
6
+ def self.prepended(base)
7
+ base.singleton_class.send(:prepend, ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def encode_utf16le(str)
12
+ str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+
19
+ Net::NTLM::EncodeUtil.send(:prepend, Custom::NTLM)
data/lib/ruby_smb/ntlm.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'ruby_smb/ntlm/custom/ntlm'
2
+
1
3
  module RubySMB
2
4
  module NTLM
3
5
  # [[MS-NLMP] 2.2.2.5](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832)
@@ -56,6 +58,41 @@ module RubySMB
56
58
  "Version #{major}.#{minor} (Build #{build}); NTLM Current Revision #{ntlm_revision}"
57
59
  end
58
60
  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
+
59
96
  end
60
97
  end
61
98
 
@@ -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 = request.data_block.path.encode('UTF-8').split('\\', 4).last
10
+ share_name = RubySMB::Utils.safe_encode(request.data_block.path, 'UTF-8').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}")
@@ -49,7 +49,7 @@ module RubySMB
49
49
  return response
50
50
  end
51
51
 
52
- share_name = request.path.encode('UTF-8').split('\\', 4).last
52
+ share_name = RubySMB::Utils.safe_encode(request.path, 'UTF-8').split('\\', 4).last
53
53
  share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
54
54
 
55
55
  if share_provider.nil?
@@ -147,6 +147,10 @@ module RubySMB
147
147
  break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
148
148
  raw_data = read(bytes: @tree.client.max_buffer_size)
149
149
  dcerpc_response = dcerpc_response_from_raw_response(raw_data)
150
+ if options[:auth_level] &&
151
+ [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
152
+ handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
153
+ end
150
154
  stub_data << dcerpc_response.stub.to_s
151
155
  end
152
156
  stub_data
@@ -145,6 +145,10 @@ module RubySMB
145
145
  break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
146
146
  raw_data = read(bytes: @tree.client.max_buffer_size)
147
147
  dcerpc_response = dcerpc_response_from_raw_response(raw_data)
148
+ if options[:auth_level] &&
149
+ [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(options[:auth_level])
150
+ handle_integrity_privacy(dcerpc_response, auth_level: options[:auth_level], auth_type: options[:auth_type])
151
+ end
148
152
  stub_data << dcerpc_response.stub.to_s
149
153
  end
150
154
  stub_data
@@ -0,0 +1,15 @@
1
+ module RubySMB
2
+ module Utils
3
+
4
+ def self.safe_encode(str, encoding)
5
+ str.encode(encoding)
6
+ rescue EncodingError
7
+ if str.encoding == ::Encoding::ASCII_8BIT
8
+ str.dup.force_encoding(encoding)
9
+ else
10
+ raise
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.2.0'.freeze
2
+ VERSION = '3.2.1'.freeze
3
3
  end
data/lib/ruby_smb.rb CHANGED
@@ -6,11 +6,13 @@ 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/ntlm'
9
10
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
10
11
  #
11
12
  # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
12
13
  # [[MS-SMB2] Server Message Block (SMB) Protocol Versions 2 and 3](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
13
14
  module RubySMB
15
+ require 'ruby_smb/utils'
14
16
  require 'ruby_smb/error'
15
17
  require 'ruby_smb/create_actions'
16
18
  require 'ruby_smb/dispositions'
data.tar.gz.sig CHANGED
Binary file
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.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -97,7 +97,7 @@ cert_chain:
97
97
  EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
98
98
  9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
99
99
  -----END CERTIFICATE-----
100
- date: 2022-08-30 00:00:00.000000000 Z
100
+ date: 2022-11-17 00:00:00.000000000 Z
101
101
  dependencies:
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: redcarpet
@@ -493,6 +493,7 @@ files:
493
493
  - lib/ruby_smb/nbss/session_request.rb
494
494
  - lib/ruby_smb/ntlm.rb
495
495
  - lib/ruby_smb/ntlm/client.rb
496
+ - lib/ruby_smb/ntlm/custom/ntlm.rb
496
497
  - lib/ruby_smb/peer_info.rb
497
498
  - lib/ruby_smb/server.rb
498
499
  - lib/ruby_smb/server/cli.rb
@@ -673,6 +674,7 @@ files:
673
674
  - lib/ruby_smb/smb2/smb2_header.rb
674
675
  - lib/ruby_smb/smb2/tree.rb
675
676
  - lib/ruby_smb/smb_error.rb
677
+ - lib/ruby_smb/utils.rb
676
678
  - lib/ruby_smb/version.rb
677
679
  - ruby_smb.gemspec
678
680
  - spec/lib/ruby_smb/client_spec.rb
metadata.gz.sig CHANGED
Binary file