ruby_smb 1.0.4 → 2.0.2

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 (130) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +6 -2
  6. data/README.md +35 -47
  7. data/examples/enum_registry_key.rb +28 -0
  8. data/examples/enum_registry_values.rb +30 -0
  9. data/examples/negotiate.rb +51 -8
  10. data/examples/pipes.rb +2 -1
  11. data/examples/read_file_encryption.rb +56 -0
  12. data/examples/read_registry_key_value.rb +32 -0
  13. data/lib/ruby_smb.rb +4 -1
  14. data/lib/ruby_smb/client.rb +207 -18
  15. data/lib/ruby_smb/client/authentication.rb +27 -8
  16. data/lib/ruby_smb/client/encryption.rb +62 -0
  17. data/lib/ruby_smb/client/negotiation.rb +153 -12
  18. data/lib/ruby_smb/client/signing.rb +19 -0
  19. data/lib/ruby_smb/client/tree_connect.rb +4 -4
  20. data/lib/ruby_smb/client/utils.rb +8 -7
  21. data/lib/ruby_smb/client/winreg.rb +46 -0
  22. data/lib/ruby_smb/crypto.rb +30 -0
  23. data/lib/ruby_smb/dcerpc.rb +38 -0
  24. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  25. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  26. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  27. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  28. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  29. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  30. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  31. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  32. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  33. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  34. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  35. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  36. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  37. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  38. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  39. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  40. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  41. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  42. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  43. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  44. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  45. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  46. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  47. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  48. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  49. data/lib/ruby_smb/dispatcher/socket.rb +4 -3
  50. data/lib/ruby_smb/error.rb +28 -1
  51. data/lib/ruby_smb/smb1/commands.rb +1 -1
  52. data/lib/ruby_smb/smb1/file.rb +6 -4
  53. data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
  54. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  55. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  56. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  57. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  58. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  59. data/lib/ruby_smb/smb1/pipe.rb +79 -3
  60. data/lib/ruby_smb/smb1/tree.rb +12 -3
  61. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  62. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  63. data/lib/ruby_smb/smb2/file.rb +25 -43
  64. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  65. data/lib/ruby_smb/smb2/packet.rb +2 -0
  66. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  67. data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
  68. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  69. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  70. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  71. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  72. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  73. data/lib/ruby_smb/smb2/pipe.rb +77 -3
  74. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  75. data/lib/ruby_smb/smb2/tree.rb +23 -17
  76. data/lib/ruby_smb/version.rb +1 -1
  77. data/ruby_smb.gemspec +5 -3
  78. data/spec/lib/ruby_smb/client_spec.rb +1441 -61
  79. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  80. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  81. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  82. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  83. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  84. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  85. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  86. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  87. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  88. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  89. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  90. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  91. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  92. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  93. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  94. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  95. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  96. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  97. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  98. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  99. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  100. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  101. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  102. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  103. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  104. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
  105. data/spec/lib/ruby_smb/error_spec.rb +59 -0
  106. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  107. data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
  108. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  109. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  110. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  111. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  113. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  114. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  115. data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
  116. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  117. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  118. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
  119. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  120. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  121. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  122. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  123. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  124. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
  125. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  126. data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
  127. metadata +187 -81
  128. metadata.gz.sig +0 -0
  129. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  130. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This example script is used for testing the Winreg registry key value read functionality.
4
+ # It will attempt to connect to a host and reads the value of a specified registry key.
5
+ # Example usage: ruby enum_registry_key.rb 192.168.172.138 msfadmin msfadmin HKLM\\My\\Key ValueName
6
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin credentialas and reads the ValueName data corresponding to the HKLM\\My\\Key registry key.
7
+
8
+ require 'bundler/setup'
9
+ require 'ruby_smb'
10
+
11
+ address = ARGV[0]
12
+ username = ARGV[1]
13
+ password = ARGV[2]
14
+ registry_key = ARGV[3]
15
+ value_name = ARGV[4]
16
+
17
+ sock = TCPSocket.new address, 445
18
+ dispatcher = RubySMB::Dispatcher::Socket.new(sock, read_timeout: 60)
19
+
20
+ client = RubySMB::Client.new(dispatcher, smb1: true, smb2: true, username: username, password: password)
21
+ protocol = client.negotiate
22
+ status = client.authenticate
23
+
24
+ puts "#{protocol}: #{status}"
25
+ puts "Key: #{registry_key}"
26
+ puts "Value: #{value_name}"
27
+
28
+ key_value = client.read_registry_key_value(address, registry_key, value_name)
29
+ puts key_value
30
+
31
+ client.disconnect!
32
+
@@ -1,6 +1,9 @@
1
1
  require 'bindata'
2
2
  require 'net/ntlm'
3
3
  require 'net/ntlm/client'
4
+ require 'openssl'
5
+ require 'openssl/ccm'
6
+ require 'openssl/cmac'
4
7
  require 'windows_error'
5
8
  require 'windows_error/nt_status'
6
9
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
@@ -15,7 +18,6 @@ module RubySMB
15
18
  require 'ruby_smb/field'
16
19
  require 'ruby_smb/nbss'
17
20
  require 'ruby_smb/fscc'
18
- require 'ruby_smb/dcerpc'
19
21
  require 'ruby_smb/generic_packet'
20
22
  require 'ruby_smb/dispatcher'
21
23
  require 'ruby_smb/version'
@@ -23,4 +25,5 @@ module RubySMB
23
25
  require 'ruby_smb/smb2'
24
26
  require 'ruby_smb/smb1'
25
27
  require 'ruby_smb/client'
28
+ require 'ruby_smb/crypto'
26
29
  end
@@ -8,6 +8,8 @@ module RubySMB
8
8
  require 'ruby_smb/client/tree_connect'
9
9
  require 'ruby_smb/client/echo'
10
10
  require 'ruby_smb/client/utils'
11
+ require 'ruby_smb/client/winreg'
12
+ require 'ruby_smb/client/encryption'
11
13
 
12
14
  include RubySMB::Client::Negotiation
13
15
  include RubySMB::Client::Authentication
@@ -15,13 +17,21 @@ module RubySMB
15
17
  include RubySMB::Client::TreeConnect
16
18
  include RubySMB::Client::Echo
17
19
  include RubySMB::Client::Utils
20
+ include RubySMB::Client::Winreg
21
+ include RubySMB::Client::Encryption
18
22
 
19
23
  # The Default SMB1 Dialect string used in an SMB1 Negotiate Request
20
24
  SMB1_DIALECT_SMB1_DEFAULT = 'NT LM 0.12'.freeze
21
25
  # The Default SMB2 Dialect string used in an SMB1 Negotiate Request
22
26
  SMB1_DIALECT_SMB2_DEFAULT = 'SMB 2.002'.freeze
23
- # Dialect value for SMB2 Default (Version 2.02)
24
- SMB2_DIALECT_DEFAULT = 0x0202
27
+ # The SMB2 wildcard revision number Dialect string used in an SMB1 Negotiate Request
28
+ # It indicates that the server implements SMB 2.1 or future dialect revisions
29
+ # Note that this must be used for SMB3
30
+ SMB1_DIALECT_SMB2_WILDCARD = 'SMB 2.???'.freeze
31
+ # Dialect values for SMB2
32
+ SMB2_DIALECT_DEFAULT = ['0x0202', '0x0210']
33
+ # Dialect values for SMB3
34
+ SMB3_DIALECT_DEFAULT = ['0x0300', '0x0302', '0x0311']
25
35
  # The default maximum size of a SMB message that the Client accepts (in bytes)
26
36
  MAX_BUFFER_SIZE = 64512
27
37
  # The default maximum size of a SMB message that the Server accepts (in bytes)
@@ -133,6 +143,11 @@ module RubySMB
133
143
  # @return [Boolean]
134
144
  attr_accessor :smb2
135
145
 
146
+ # Whether or not the Client should support SMB3
147
+ # @!attribute [rw] smb3
148
+ # @return [Boolean]
149
+ attr_accessor :smb3
150
+
136
151
  # Tracks the current SMB2 Message ID that keeps communication in sync
137
152
  # @!attribute [rw] smb2_message_id
138
153
  # @return [Integer]
@@ -176,12 +191,80 @@ module RubySMB
176
191
  # @return [Integer]
177
192
  attr_accessor :server_max_transact_size
178
193
 
194
+ # The algorithm to compute the preauthentication integrity hash (SMB 3.1.1).
195
+ # @!attribute [rw] preauth_integrity_hash_algorithm
196
+ # @return [String]
197
+ attr_accessor :preauth_integrity_hash_algorithm
198
+
199
+ # The preauthentication integrity hash value (SMB 3.1.1).
200
+ # @!attribute [rw] preauth_integrity_hash_value
201
+ # @return [String]
202
+ attr_accessor :preauth_integrity_hash_value
203
+
204
+ # The algorithm for encryption (SMB 3.x).
205
+ # @!attribute [rw] encryption_algorithm
206
+ # @return [String]
207
+ attr_accessor :encryption_algorithm
208
+
209
+ # The client encryption key (SMB 3.x).
210
+ # @!attribute [rw] client_encryption_key
211
+ # @return [String]
212
+ attr_accessor :client_encryption_key
213
+
214
+ # The server encryption key (SMB 3.x).
215
+ # @!attribute [rw] server_encryption_key
216
+ # @return [String]
217
+ attr_accessor :server_encryption_key
218
+
219
+ # Whether or not the whole session needs to be encrypted (SMB 3.x)
220
+ # @!attribute [rw] session_encrypt_data
221
+ # @return [Boolean]
222
+ attr_accessor :session_encrypt_data
223
+
224
+ # The encryption algorithms supported by the server (SMB 3.x).
225
+ # @!attribute [rw] server_encryption_algorithms
226
+ # @return [Array<Integer>] list of supported encryption algorithms
227
+ # (constants defined in RubySMB::SMB2::EncryptionCapabilities)
228
+ attr_accessor :server_encryption_algorithms
229
+
230
+ # The compression algorithms supported by the server (SMB 3.x).
231
+ # @!attribute [rw] server_compression_algorithms
232
+ # @return [Array<Integer>] list of supported compression algorithms
233
+ # (constants defined in RubySMB::SMB2::CompressionCapabilities)
234
+ attr_accessor :server_compression_algorithms
235
+
236
+ # The GUID of the server (SMB 2.x and 3.x).
237
+ # @!attribute [rw] server_guid
238
+ # @return [String]
239
+ attr_accessor :server_guid
240
+
241
+ # The server's start time if it is reported as part of the negotiation
242
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
243
+ # it (reports a value of 0).
244
+ # @!attribute [rw] server_start_time
245
+ # @return [Time] the time that the server reports that it was started at
246
+ attr_accessor :server_start_time
247
+
248
+ # The server's current time if it is reported as part of the negotiation
249
+ # process (SMB 2.x and 3.x). This value is nil if the server does not report
250
+ # it (reports a value of 0).
251
+ # @!attribute [rw] server_system_time
252
+ # @return [Time] the time that the server reports as current
253
+ attr_accessor :server_system_time
254
+
255
+ # The SMB version that has been successfully negotiated. This value is only
256
+ # set after the NEGOTIATE handshake has been performed.
257
+ # @!attribute [rw] negotiated_smb_version
258
+ # @return [Integer] the negotiated SMB version
259
+ attr_accessor :negotiated_smb_version
260
+
179
261
  # @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
180
262
  # @param smb1 [Boolean] whether or not to enable SMB1 support
181
263
  # @param smb2 [Boolean] whether or not to enable SMB2 support
182
- def initialize(dispatcher, smb1: true, smb2: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION')
264
+ # @param smb3 [Boolean] whether or not to enable SMB3 support
265
+ def initialize(dispatcher, smb1: true, smb2: true, smb3: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION', always_encrypt: true)
183
266
  raise ArgumentError, 'No Dispatcher provided' unless dispatcher.is_a? RubySMB::Dispatcher::Base
184
- if smb1 == false && smb2 == false
267
+ if smb1 == false && smb2 == false && smb3 == false
185
268
  raise ArgumentError, 'You must enable at least one Protocol'
186
269
  end
187
270
  @dispatcher = dispatcher
@@ -194,6 +277,7 @@ module RubySMB
194
277
  @signing_required = false
195
278
  @smb1 = smb1
196
279
  @smb2 = smb2
280
+ @smb3 = smb3
197
281
  @username = username.encode('utf-8') || ''.encode('utf-8')
198
282
  @max_buffer_size = MAX_BUFFER_SIZE
199
283
  # These sizes will be modifed during negotiation
@@ -202,10 +286,14 @@ module RubySMB
202
286
  @server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
203
287
  @server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
204
288
 
289
+ # SMB 3.x options
290
+ @session_encrypt_data = always_encrypt
291
+
205
292
  negotiate_version_flag = 0x02000000
206
293
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
207
294
  Net::NTLM::FLAGS[:TARGET_INFO] |
208
- negotiate_version_flag
295
+ negotiate_version_flag ^
296
+ Net::NTLM::FLAGS[:OEM]
209
297
 
210
298
  @ntlm_client = Net::NTLM::Client.new(
211
299
  @username,
@@ -241,7 +329,7 @@ module RubySMB
241
329
  # @param data [String] the data the server should echo back (ignored in SMB2)
242
330
  # @return [WindowsError::ErrorCode] the NTStatus of the last response received
243
331
  def echo(count: 1, data: '')
244
- response = if smb2
332
+ response = if smb2 || smb3
245
333
  smb2_echo
246
334
  else
247
335
  smb1_echo(count: count, data: data)
@@ -256,10 +344,8 @@ module RubySMB
256
344
  # @param packet [RubySMB::GenericPacket] the packet to set the message id for
257
345
  # @return [RubySMB::GenericPacket] the modified packet
258
346
  def increment_smb_message_id(packet)
259
- if packet.smb2_header.message_id.zero? && smb2_message_id != 0
260
- packet.smb2_header.message_id = smb2_message_id
261
- self.smb2_message_id += 1
262
- end
347
+ packet.smb2_header.message_id = smb2_message_id
348
+ self.smb2_message_id += 1
263
349
  packet
264
350
  end
265
351
 
@@ -281,7 +367,8 @@ module RubySMB
281
367
  negotiate_version_flag = 0x02000000
282
368
  flags = Net::NTLM::Client::DEFAULT_FLAGS |
283
369
  Net::NTLM::FLAGS[:TARGET_INFO] |
284
- negotiate_version_flag
370
+ negotiate_version_flag ^
371
+ Net::NTLM::FLAGS[:OEM]
285
372
 
286
373
  @ntlm_client = Net::NTLM::Client.new(
287
374
  @username,
@@ -299,7 +386,7 @@ module RubySMB
299
386
  # @return [WindowsError::ErrorCode] the NTStatus of the response
300
387
  # @raise [RubySMB::Error::InvalidPacket] if the response packet is not a LogoffResponse packet
301
388
  def logoff!
302
- if smb2
389
+ if smb2 || smb3
303
390
  request = RubySMB::SMB2::Packet::LogoffRequest.new
304
391
  raw_response = send_recv(request)
305
392
  response = RubySMB::SMB2::Packet::LogoffResponse.read(raw_response)
@@ -332,9 +419,13 @@ module RubySMB
332
419
  # It will also sign the packet if neccessary.
333
420
  #
334
421
  # @param packet [RubySMB::GenericPacket] the request to be sent
422
+ # @param encrypt [Boolean] true if encryption has to be enabled for this transaction
423
+ # (note that if @session_encrypt_data is set, encryption will be enabled
424
+ # regardless of this parameter value)
335
425
  # @return [String] the raw response data received
336
- def send_recv(packet)
337
- case packet.packet_smb_version
426
+ def send_recv(packet, encrypt: false)
427
+ version = packet.packet_smb_version
428
+ case version
338
429
  when 'SMB1'
339
430
  packet.smb_header.uid = user_id if user_id
340
431
  packet = smb1_sign(packet)
@@ -342,25 +433,110 @@ module RubySMB
342
433
  packet = increment_smb_message_id(packet)
343
434
  packet.smb2_header.session_id = session_id
344
435
  unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)
345
- packet = smb2_sign(packet)
436
+ if self.smb2
437
+ packet = smb2_sign(packet)
438
+ elsif self.smb3
439
+ packet = smb3_sign(packet)
440
+ end
346
441
  end
347
442
  else
348
443
  packet = packet
349
444
  end
350
- dispatcher.send_packet(packet)
351
- raw_response = dispatcher.recv_packet
445
+
446
+ if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
447
+ send_encrypt(packet)
448
+ raw_response = recv_encrypt
449
+ loop do
450
+ break unless is_status_pending?(raw_response)
451
+ sleep 1
452
+ raw_response = recv_encrypt
453
+ end
454
+ else
455
+ dispatcher.send_packet(packet)
456
+ raw_response = dispatcher.recv_packet
457
+ loop do
458
+ break unless is_status_pending?(raw_response)
459
+ sleep 1
460
+ raw_response = dispatcher.recv_packet
461
+ end unless version == 'SMB1'
462
+ end
352
463
 
353
464
  self.sequence_counter += 1 if signing_required && !session_key.empty?
354
465
  raw_response
355
466
  end
356
467
 
468
+ # Check if the response is an asynchronous operation with STATUS_PENDING
469
+ # status code.
470
+ #
471
+ # @param raw_response [String] the raw response packet
472
+ # @return [Boolean] true if it is a status pending operation, false otherwise
473
+ def is_status_pending?(raw_response)
474
+ smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
475
+ value = smb2_header.nt_status.value
476
+ status_code = WindowsError::NTStatus.find_by_retval(value).first
477
+ status_code == WindowsError::NTStatus::STATUS_PENDING &&
478
+ smb2_header.flags.async_command == 1
479
+ end
480
+
481
+ # Check if the request packet can be encrypted. Per the SMB2 spec,
482
+ # SessionSetupRequest and NegotiateRequest must not be encrypted.
483
+ #
484
+ # @param packet [RubySMB::GenericPacket] the request packet
485
+ # @return [Boolean] true if the packet can be encrypted
486
+ def can_be_encrypted?(packet)
487
+ return false if packet.packet_smb_version == 'SMB1'
488
+ [RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
489
+ packet.is_a?(klass)
490
+ end
491
+ end
492
+
493
+ # Check if the current dialect supports encryption.
494
+ #
495
+ # @return [Boolean] true if encryption is supported
496
+ def encryption_supported?
497
+ ['0x0300', '0x0302', '0x0311'].include?(@dialect)
498
+ end
499
+
500
+ # Encrypt and send a packet
501
+ def send_encrypt(packet)
502
+ begin
503
+ transform_request = smb3_encrypt(packet.to_binary_s)
504
+ rescue RubySMB::Error::RubySMBError => e
505
+ raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
506
+ end
507
+ dispatcher.send_packet(transform_request)
508
+ end
509
+
510
+ # Receives the raw response through the Dispatcher and decrypt the packet.
511
+ #
512
+ # @return [String] the raw unencrypted packet
513
+ def recv_encrypt
514
+ begin
515
+ raw_response = dispatcher.recv_packet
516
+ rescue RubySMB::Error::CommunicationError => e
517
+ raise RubySMB::Error::EncryptionError, "Communication error with the "\
518
+ "remote host: #{e.message}. The server supports encryption but was "\
519
+ "not able to handle the encrypted request."
520
+ end
521
+ begin
522
+ transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
523
+ rescue IOError
524
+ raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
525
+ end
526
+ begin
527
+ smb3_decrypt(transform_response)
528
+ rescue RubySMB::Error::RubySMBError => e
529
+ raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
530
+ end
531
+ end
532
+
357
533
  # Connects to the supplied share
358
534
  #
359
535
  # @param share [String] the path to the share in `\\server\share_name` format
360
536
  # @return [RubySMB::SMB1::Tree] if talking over SMB1
361
537
  # @return [RubySMB::SMB2::Tree] if talking over SMB2
362
538
  def tree_connect(share)
363
- connected_tree = if smb2
539
+ connected_tree = if smb2 || smb3
364
540
  smb2_tree_connect(share)
365
541
  else
366
542
  smb1_tree_connect(share)
@@ -390,6 +566,8 @@ module RubySMB
390
566
  self.session_key = ''
391
567
  self.sequence_counter = 0
392
568
  self.smb2_message_id = 0
569
+ self.client_encryption_key = nil
570
+ self.server_encryption_key = nil
393
571
  end
394
572
 
395
573
  # Requests a NetBIOS Session Service using the provided name.
@@ -432,5 +610,16 @@ module RubySMB
432
610
  session_request
433
611
  end
434
612
 
613
+ def update_preauth_hash(data)
614
+ unless @preauth_integrity_hash_algorithm
615
+ raise RubySMB::Error::EncryptionError.new(
616
+ 'Cannot compute the Preauth Integrity Hash value: Preauth Integrity Hash Algorithm is nil'
617
+ )
618
+ end
619
+ @preauth_integrity_hash_value = OpenSSL::Digest.digest(
620
+ @preauth_integrity_hash_algorithm,
621
+ @preauth_integrity_hash_value + data.to_binary_s
622
+ )
623
+ end
435
624
  end
436
625
  end
@@ -175,7 +175,7 @@ module RubySMB
175
175
 
176
176
  status_code = packet.status_code
177
177
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
178
- raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
178
+ raise RubySMB::Error::UnexpectedStatusCode, status_code
179
179
  end
180
180
 
181
181
  packet
@@ -201,6 +201,9 @@ module RubySMB
201
201
  def smb2_authenticate
202
202
  response = smb2_ntlmssp_negotiate
203
203
  challenge_packet = smb2_ntlmssp_challenge_packet(response)
204
+ if @dialect == '0x0311'
205
+ update_preauth_hash(challenge_packet)
206
+ end
204
207
  @session_id = challenge_packet.smb2_header.session_id
205
208
  type2_b64_message = smb2_type2_message(challenge_packet)
206
209
  type3_message = @ntlm_client.init_context(type2_b64_message)
@@ -213,6 +216,16 @@ module RubySMB
213
216
  raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
214
217
  response = smb2_ntlmssp_final_packet(raw)
215
218
 
219
+ if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
220
+ @session_encrypt_data = true
221
+ end
222
+ ######
223
+ # DEBUG
224
+ #puts "Session ID = #{@session_id.to_binary_s.each_byte.map {|e| '%02x' % e}.join}"
225
+ #puts "Session key = #{@session_key.each_byte.map {|e| '%02x' % e}.join}"
226
+ #puts "PreAuthHash = #{@preauth_integrity_hash_value.each_byte.map {|e| '%02x' % e}.join}" if @preauth_integrity_hash_value
227
+ ######
228
+
216
229
  response.status_code
217
230
  end
218
231
 
@@ -245,7 +258,7 @@ module RubySMB
245
258
 
246
259
  status_code = packet.status_code
247
260
  unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
248
- raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
261
+ raise RubySMB::Error::UnexpectedStatusCode, status_code
249
262
  end
250
263
  packet
251
264
  end
@@ -256,7 +269,11 @@ module RubySMB
256
269
  # @return [String] the binary string response from the server
257
270
  def smb2_ntlmssp_negotiate
258
271
  packet = smb2_ntlmssp_negotiate_packet
259
- send_recv(packet)
272
+ response = send_recv(packet)
273
+ if @dialect == '0x0311'
274
+ update_preauth_hash(packet)
275
+ end
276
+ response
260
277
  end
261
278
 
262
279
  # Creates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
@@ -268,10 +285,7 @@ module RubySMB
268
285
  type1_message = ntlm_client.init_context
269
286
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
270
287
  packet.set_type1_blob(type1_message.serialize)
271
- # This Message ID should always be 1, but thanks to Multi-Protocol Negotiation
272
- # the Message ID can be out of sync at this point so we re-synch it here.
273
- packet.smb2_header.message_id = 1
274
- self.smb2_message_id = 2
288
+ packet.security_mode.signing_enabled = 1
275
289
  packet
276
290
  end
277
291
 
@@ -294,7 +308,11 @@ module RubySMB
294
308
  # @return [String] the raw binary response from the server
295
309
  def smb2_ntlmssp_authenticate(type3_message, user_id)
296
310
  packet = smb2_ntlmssp_auth_packet(type3_message, user_id)
297
- send_recv(packet)
311
+ response = send_recv(packet)
312
+ if @dialect == '0x0311'
313
+ update_preauth_hash(packet)
314
+ end
315
+ response
298
316
  end
299
317
 
300
318
  # Generates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
@@ -307,6 +325,7 @@ module RubySMB
307
325
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
308
326
  packet.smb2_header.session_id = session_id
309
327
  packet.set_type3_blob(type3_message.serialize)
328
+ packet.security_mode.signing_enabled = 1
310
329
  packet
311
330
  end
312
331