ruby_smb 2.0.0 → 2.0.5
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 +4 -4
- checksums.yaml.gz.sig +3 -3
- data.tar.gz.sig +5 -1
- data/examples/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/enum_registry_key.rb +5 -4
- data/examples/enum_registry_values.rb +5 -4
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +11 -12
- data/examples/query_service_status.rb +64 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_registry_key_value.rb +6 -5
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb/client.rb +117 -53
- data/lib/ruby_smb/client/authentication.rb +7 -12
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/negotiation.rb +31 -12
- data/lib/ruby_smb/client/tree_connect.rb +2 -4
- data/lib/ruby_smb/client/utils.rb +16 -10
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/dcerpc.rb +4 -0
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +306 -44
- data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +28 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
- data/lib/ruby_smb/dcerpc/request.rb +19 -0
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
- data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
- data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
- data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +3 -2
- data/lib/ruby_smb/error.rb +21 -5
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/generic_packet.rb +11 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/file.rb +9 -24
- data/lib/ruby_smb/smb1/pipe.rb +8 -6
- data/lib/ruby_smb/smb1/tree.rb +22 -9
- data/lib/ruby_smb/smb2/file.rb +46 -46
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
- data/lib/ruby_smb/smb2/pipe.rb +9 -6
- data/lib/ruby_smb/smb2/tree.rb +30 -20
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +248 -109
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
- data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
- data/spec/lib/ruby_smb/error_spec.rb +34 -5
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +73 -21
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -5
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +64 -7
- metadata +91 -2
- metadata.gz.sig +0 -0
data/examples/write_file.rb
CHANGED
@@ -9,18 +9,20 @@
|
|
9
9
|
require 'bundler/setup'
|
10
10
|
require 'ruby_smb'
|
11
11
|
|
12
|
-
address
|
13
|
-
username
|
14
|
-
password
|
15
|
-
share
|
16
|
-
file
|
17
|
-
data
|
12
|
+
address = ARGV[0]
|
13
|
+
username = ARGV[1]
|
14
|
+
password = ARGV[2]
|
15
|
+
share = ARGV[3]
|
16
|
+
file = ARGV[4]
|
17
|
+
data = ARGV[5]
|
18
|
+
smb_versions = ARGV[6]&.split(',') || ['1','2','3']
|
19
|
+
|
18
20
|
path = "\\\\#{address}\\#{share}"
|
19
21
|
|
20
22
|
sock = TCPSocket.new address, 445
|
21
23
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
22
24
|
|
23
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
25
|
+
client = RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password)
|
24
26
|
protocol = client.negotiate
|
25
27
|
status = client.authenticate
|
26
28
|
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -160,9 +160,16 @@ module RubySMB
|
|
160
160
|
|
161
161
|
# The UID set in SMB1
|
162
162
|
# @!attribute [rw] user_id
|
163
|
-
# @return [
|
163
|
+
# @return [Integer]
|
164
164
|
attr_accessor :user_id
|
165
165
|
|
166
|
+
# The Process ID set in SMB1
|
167
|
+
# It is randomly generated during the client initialization, but can
|
168
|
+
# be modified using the accessor.
|
169
|
+
# @!attribute [rw] pid
|
170
|
+
# @return [Integer]
|
171
|
+
attr_accessor :pid
|
172
|
+
|
166
173
|
# The maximum size SMB message that the Client accepts (in bytes)
|
167
174
|
# The default value is equal to {MAX_BUFFER_SIZE}.
|
168
175
|
# @!attribute [rw] max_buffer_size
|
@@ -216,10 +223,10 @@ module RubySMB
|
|
216
223
|
# @return [String]
|
217
224
|
attr_accessor :server_encryption_key
|
218
225
|
|
219
|
-
# Whether or not
|
220
|
-
# @!attribute [rw]
|
226
|
+
# Whether or not the whole session needs to be encrypted (SMB 3.x)
|
227
|
+
# @!attribute [rw] session_encrypt_data
|
221
228
|
# @return [Boolean]
|
222
|
-
attr_accessor :
|
229
|
+
attr_accessor :session_encrypt_data
|
223
230
|
|
224
231
|
# The encryption algorithms supported by the server (SMB 3.x).
|
225
232
|
# @!attribute [rw] server_encryption_algorithms
|
@@ -233,12 +240,39 @@ module RubySMB
|
|
233
240
|
# (constants defined in RubySMB::SMB2::CompressionCapabilities)
|
234
241
|
attr_accessor :server_compression_algorithms
|
235
242
|
|
243
|
+
# The GUID of the server (SMB 2.x and 3.x).
|
244
|
+
# @!attribute [rw] server_guid
|
245
|
+
# @return [String]
|
246
|
+
attr_accessor :server_guid
|
247
|
+
|
248
|
+
# The server's start 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_start_time
|
252
|
+
# @return [Time] the time that the server reports that it was started at
|
253
|
+
attr_accessor :server_start_time
|
254
|
+
|
255
|
+
# The server's current time if it is reported as part of the negotiation
|
256
|
+
# process (SMB 2.x and 3.x). This value is nil if the server does not report
|
257
|
+
# it (reports a value of 0).
|
258
|
+
# @!attribute [rw] server_system_time
|
259
|
+
# @return [Time] the time that the server reports as current
|
260
|
+
attr_accessor :server_system_time
|
261
|
+
|
236
262
|
# The SMB version that has been successfully negotiated. This value is only
|
237
263
|
# set after the NEGOTIATE handshake has been performed.
|
238
264
|
# @!attribute [rw] negotiated_smb_version
|
239
265
|
# @return [Integer] the negotiated SMB version
|
240
266
|
attr_accessor :negotiated_smb_version
|
241
267
|
|
268
|
+
# Whether or not the server supports multi-credit operations. It is
|
269
|
+
# reported by the LARGE_MTU capabiliy as part of the negotiation process
|
270
|
+
# (SMB 2.x and 3.x).
|
271
|
+
# @!attribute [rw] server_supports_multi_credit
|
272
|
+
# @return [Boolean] true if the server supports multi-credit operations,
|
273
|
+
# false otherwise
|
274
|
+
attr_accessor :server_supports_multi_credit
|
275
|
+
|
242
276
|
# @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
|
243
277
|
# @param smb1 [Boolean] whether or not to enable SMB1 support
|
244
278
|
# @param smb2 [Boolean] whether or not to enable SMB2 support
|
@@ -249,6 +283,7 @@ module RubySMB
|
|
249
283
|
raise ArgumentError, 'You must enable at least one Protocol'
|
250
284
|
end
|
251
285
|
@dispatcher = dispatcher
|
286
|
+
@pid = rand(0xFFFF)
|
252
287
|
@domain = domain
|
253
288
|
@local_workstation = local_workstation
|
254
289
|
@password = password.encode('utf-8') || ''.encode('utf-8')
|
@@ -266,14 +301,16 @@ module RubySMB
|
|
266
301
|
@server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
267
302
|
@server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
268
303
|
@server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
304
|
+
@server_supports_multi_credit = false
|
269
305
|
|
270
306
|
# SMB 3.x options
|
271
|
-
@
|
307
|
+
@session_encrypt_data = always_encrypt
|
272
308
|
|
273
309
|
negotiate_version_flag = 0x02000000
|
274
310
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
275
311
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
276
|
-
negotiate_version_flag
|
312
|
+
negotiate_version_flag ^
|
313
|
+
Net::NTLM::FLAGS[:OEM]
|
277
314
|
|
278
315
|
@ntlm_client = Net::NTLM::Client.new(
|
279
316
|
@username,
|
@@ -324,7 +361,7 @@ module RubySMB
|
|
324
361
|
# @param packet [RubySMB::GenericPacket] the packet to set the message id for
|
325
362
|
# @return [RubySMB::GenericPacket] the modified packet
|
326
363
|
def increment_smb_message_id(packet)
|
327
|
-
packet.smb2_header.message_id = smb2_message_id
|
364
|
+
packet.smb2_header.message_id = self.smb2_message_id
|
328
365
|
self.smb2_message_id += 1
|
329
366
|
packet
|
330
367
|
end
|
@@ -347,7 +384,8 @@ module RubySMB
|
|
347
384
|
negotiate_version_flag = 0x02000000
|
348
385
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
349
386
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
350
|
-
negotiate_version_flag
|
387
|
+
negotiate_version_flag ^
|
388
|
+
Net::NTLM::FLAGS[:OEM]
|
351
389
|
|
352
390
|
@ntlm_client = Net::NTLM::Client.new(
|
353
391
|
@username,
|
@@ -373,8 +411,7 @@ module RubySMB
|
|
373
411
|
raise RubySMB::Error::InvalidPacket.new(
|
374
412
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
375
413
|
expected_cmd: RubySMB::SMB2::Packet::LogoffResponse::COMMAND,
|
376
|
-
|
377
|
-
received_cmd: response.smb2_header.command
|
414
|
+
packet: response
|
378
415
|
)
|
379
416
|
end
|
380
417
|
else
|
@@ -385,8 +422,7 @@ module RubySMB
|
|
385
422
|
raise RubySMB::Error::InvalidPacket.new(
|
386
423
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
387
424
|
expected_cmd: RubySMB::SMB1::Packet::LogoffResponse::COMMAND,
|
388
|
-
|
389
|
-
received_cmd: response.smb_header.command
|
425
|
+
packet: response
|
390
426
|
)
|
391
427
|
end
|
392
428
|
end
|
@@ -398,12 +434,16 @@ module RubySMB
|
|
398
434
|
# It will also sign the packet if neccessary.
|
399
435
|
#
|
400
436
|
# @param packet [RubySMB::GenericPacket] the request to be sent
|
437
|
+
# @param encrypt [Boolean] true if encryption has to be enabled for this transaction
|
438
|
+
# (note that if @session_encrypt_data is set, encryption will be enabled
|
439
|
+
# regardless of this parameter value)
|
401
440
|
# @return [String] the raw response data received
|
402
441
|
def send_recv(packet, encrypt: false)
|
403
442
|
version = packet.packet_smb_version
|
404
443
|
case version
|
405
444
|
when 'SMB1'
|
406
|
-
packet.smb_header.uid = user_id if user_id
|
445
|
+
packet.smb_header.uid = self.user_id if self.user_id
|
446
|
+
packet.smb_header.pid_low = self.pid if self.pid
|
407
447
|
packet = smb1_sign(packet)
|
408
448
|
when 'SMB2'
|
409
449
|
packet = increment_smb_message_id(packet)
|
@@ -419,84 +459,107 @@ module RubySMB
|
|
419
459
|
packet = packet
|
420
460
|
end
|
421
461
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
loop do
|
426
|
-
break unless is_status_pending?(raw_response)
|
427
|
-
sleep 1
|
428
|
-
raw_response = recv_encrypt
|
429
|
-
end
|
430
|
-
else
|
431
|
-
dispatcher.send_packet(packet)
|
432
|
-
raw_response = dispatcher.recv_packet
|
433
|
-
loop do
|
434
|
-
break unless is_status_pending?(raw_response)
|
435
|
-
sleep 1
|
436
|
-
raw_response = dispatcher.recv_packet
|
437
|
-
end unless version == 'SMB1'
|
462
|
+
encrypt_data = false
|
463
|
+
if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
|
464
|
+
encrypt_data = true
|
438
465
|
end
|
466
|
+
send_packet(packet, encrypt: encrypt_data)
|
467
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
468
|
+
smb2_header = nil
|
469
|
+
loop do
|
470
|
+
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
|
471
|
+
break unless is_status_pending?(smb2_header)
|
472
|
+
sleep 1
|
473
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
474
|
+
rescue IOError
|
475
|
+
# We're expecting an SMB2 packet, but the server sent an SMB1 packet
|
476
|
+
# instead. This behavior has been observed with older versions of Samba
|
477
|
+
# when something goes wrong on the server side. So, we just ignore it
|
478
|
+
# and expect the caller to handle this wrong response packet.
|
479
|
+
break
|
480
|
+
end unless version == 'SMB1'
|
439
481
|
|
440
482
|
self.sequence_counter += 1 if signing_required && !session_key.empty?
|
483
|
+
# update the SMB2 message ID according to the received Credit Charged
|
484
|
+
self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.server_supports_multi_credit
|
441
485
|
raw_response
|
442
486
|
end
|
443
487
|
|
444
488
|
# Check if the response is an asynchronous operation with STATUS_PENDING
|
445
489
|
# status code.
|
446
490
|
#
|
447
|
-
# @param
|
491
|
+
# @param smb2_header [String] the response packet SMB2 header
|
448
492
|
# @return [Boolean] true if it is a status pending operation, false otherwise
|
449
|
-
def is_status_pending?(
|
450
|
-
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
|
493
|
+
def is_status_pending?(smb2_header)
|
451
494
|
value = smb2_header.nt_status.value
|
452
495
|
status_code = WindowsError::NTStatus.find_by_retval(value).first
|
453
496
|
status_code == WindowsError::NTStatus::STATUS_PENDING &&
|
454
497
|
smb2_header.flags.async_command == 1
|
455
498
|
end
|
456
499
|
|
457
|
-
# Check if the request packet can be encrypted. Per the
|
500
|
+
# Check if the request packet can be encrypted. Per the SMB2 spec,
|
458
501
|
# SessionSetupRequest and NegotiateRequest must not be encrypted.
|
459
502
|
#
|
460
503
|
# @param packet [RubySMB::GenericPacket] the request packet
|
461
504
|
# @return [Boolean] true if the packet can be encrypted
|
462
505
|
def can_be_encrypted?(packet)
|
506
|
+
return false if packet.packet_smb_version == 'SMB1'
|
463
507
|
[RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
|
464
508
|
packet.is_a?(klass)
|
465
509
|
end
|
466
510
|
end
|
467
511
|
|
468
|
-
# Check if the current dialect
|
512
|
+
# Check if the current dialect supports encryption.
|
469
513
|
#
|
470
514
|
# @return [Boolean] true if encryption is supported
|
471
515
|
def encryption_supported?
|
472
516
|
['0x0300', '0x0302', '0x0311'].include?(@dialect)
|
473
517
|
end
|
474
518
|
|
475
|
-
# Encrypt and send a packet
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
519
|
+
# Encrypt (if required) and send a packet.
|
520
|
+
#
|
521
|
+
# @param encrypt [Boolean] true if the packet should be encrypted, false
|
522
|
+
# otherwise
|
523
|
+
def send_packet(packet, encrypt: false)
|
524
|
+
if encrypt
|
525
|
+
begin
|
526
|
+
packet = smb3_encrypt(packet.to_binary_s)
|
527
|
+
rescue RubySMB::Error::RubySMBError => e
|
528
|
+
raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
|
529
|
+
end
|
481
530
|
end
|
482
|
-
dispatcher.send_packet(
|
531
|
+
dispatcher.send_packet(packet)
|
483
532
|
end
|
484
533
|
|
485
|
-
# Receives the raw response through the Dispatcher and decrypt the packet.
|
534
|
+
# Receives the raw response through the Dispatcher and decrypt the packet (if required).
|
486
535
|
#
|
536
|
+
# @param encrypt [Boolean] true if the packet is encrypted, false otherwise
|
487
537
|
# @return [String] the raw unencrypted packet
|
488
|
-
def
|
489
|
-
raw_response = dispatcher.recv_packet
|
538
|
+
def recv_packet(encrypt: false)
|
490
539
|
begin
|
491
|
-
|
492
|
-
rescue
|
493
|
-
|
540
|
+
raw_response = dispatcher.recv_packet
|
541
|
+
rescue RubySMB::Error::CommunicationError => e
|
542
|
+
if encrypt
|
543
|
+
raise RubySMB::Error::EncryptionError, "Communication error with the "\
|
544
|
+
"remote host: #{e.message}. The server supports encryption but was "\
|
545
|
+
"not able to handle the encrypted request."
|
546
|
+
else
|
547
|
+
raise e
|
548
|
+
end
|
494
549
|
end
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
550
|
+
if encrypt
|
551
|
+
begin
|
552
|
+
transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
|
553
|
+
rescue IOError
|
554
|
+
raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
|
555
|
+
end
|
556
|
+
begin
|
557
|
+
raw_response = smb3_decrypt(transform_response)
|
558
|
+
rescue RubySMB::Error::RubySMBError => e
|
559
|
+
raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
|
560
|
+
end
|
499
561
|
end
|
562
|
+
raw_response
|
500
563
|
end
|
501
564
|
|
502
565
|
# Connects to the supplied share
|
@@ -520,7 +583,7 @@ module RubySMB
|
|
520
583
|
# @param [String] host
|
521
584
|
def net_share_enum_all(host)
|
522
585
|
tree = tree_connect("\\\\#{host}\\IPC$")
|
523
|
-
named_pipe = tree.
|
586
|
+
named_pipe = tree.open_pipe(filename: "srvsvc", write: true, read: true)
|
524
587
|
named_pipe.net_share_enum_all(host)
|
525
588
|
end
|
526
589
|
|
@@ -537,6 +600,7 @@ module RubySMB
|
|
537
600
|
self.smb2_message_id = 0
|
538
601
|
self.client_encryption_key = nil
|
539
602
|
self.server_encryption_key = nil
|
603
|
+
self.server_supports_multi_credit = false
|
540
604
|
end
|
541
605
|
|
542
606
|
# Requests a NetBIOS Session Service using the provided name.
|
@@ -574,7 +638,7 @@ module RubySMB
|
|
574
638
|
session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST
|
575
639
|
session_request.called_name = called_name
|
576
640
|
session_request.calling_name = calling_name
|
577
|
-
session_request.session_header.
|
641
|
+
session_request.session_header.stream_protocol_length =
|
578
642
|
session_request.num_bytes - session_request.session_header.num_bytes
|
579
643
|
session_request
|
580
644
|
end
|
@@ -60,8 +60,7 @@ module RubySMB
|
|
60
60
|
raise RubySMB::Error::InvalidPacket.new(
|
61
61
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
62
62
|
expected_cmd: RubySMB::SMB1::Packet::SessionSetupLegacyResponse::COMMAND,
|
63
|
-
|
64
|
-
received_cmd: packet.smb_header.command
|
63
|
+
packet: packet
|
65
64
|
)
|
66
65
|
end
|
67
66
|
packet
|
@@ -154,8 +153,7 @@ module RubySMB
|
|
154
153
|
raise RubySMB::Error::InvalidPacket.new(
|
155
154
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
156
155
|
expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
|
157
|
-
|
158
|
-
received_cmd: packet.smb_header.command
|
156
|
+
packet: packet
|
159
157
|
)
|
160
158
|
end
|
161
159
|
packet
|
@@ -168,8 +166,7 @@ module RubySMB
|
|
168
166
|
raise RubySMB::Error::InvalidPacket.new(
|
169
167
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
170
168
|
expected_cmd: RubySMB::SMB1::Packet::SessionSetupResponse::COMMAND,
|
171
|
-
|
172
|
-
received_cmd: packet.smb_header.command
|
169
|
+
packet: packet
|
173
170
|
)
|
174
171
|
end
|
175
172
|
|
@@ -216,8 +213,8 @@ module RubySMB
|
|
216
213
|
raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
|
217
214
|
response = smb2_ntlmssp_final_packet(raw)
|
218
215
|
|
219
|
-
if @smb3 && !@
|
220
|
-
@
|
216
|
+
if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
|
217
|
+
@session_encrypt_data = true
|
221
218
|
end
|
222
219
|
######
|
223
220
|
# DEBUG
|
@@ -236,8 +233,7 @@ module RubySMB
|
|
236
233
|
raise RubySMB::Error::InvalidPacket.new(
|
237
234
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
238
235
|
expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
|
239
|
-
|
240
|
-
received_cmd: packet.smb2_header.command
|
236
|
+
packet: packet
|
241
237
|
)
|
242
238
|
end
|
243
239
|
|
@@ -251,8 +247,7 @@ module RubySMB
|
|
251
247
|
raise RubySMB::Error::InvalidPacket.new(
|
252
248
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
253
249
|
expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
|
254
|
-
|
255
|
-
received_cmd: packet.smb2_header.command
|
250
|
+
packet: packet
|
256
251
|
)
|
257
252
|
end
|
258
253
|
|
data/lib/ruby_smb/client/echo.rb
CHANGED
@@ -21,8 +21,7 @@ module RubySMB
|
|
21
21
|
raise RubySMB::Error::InvalidPacket.new(
|
22
22
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
23
23
|
expected_cmd: RubySMB::SMB1::Packet::EchoResponse::COMMAND,
|
24
|
-
|
25
|
-
received_cmd: response.smb_header.command
|
24
|
+
packet: response
|
26
25
|
)
|
27
26
|
end
|
28
27
|
response
|
@@ -40,8 +39,7 @@ module RubySMB
|
|
40
39
|
raise RubySMB::Error::InvalidPacket.new(
|
41
40
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
42
41
|
expected_cmd: RubySMB::SMB2::Packet::EchoResponse::COMMAND,
|
43
|
-
|
44
|
-
received_cmd: response.smb2_header.command
|
42
|
+
packet: response
|
45
43
|
)
|
46
44
|
end
|
47
45
|
response
|
@@ -18,12 +18,11 @@ module RubySMB
|
|
18
18
|
# This is only valid for SMB1.
|
19
19
|
response_packet.dialects = request_packet.dialects if response_packet.respond_to? :dialects=
|
20
20
|
version = parse_negotiate_response(response_packet)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
when '0x0311'
|
25
|
-
parse_smb3_encryption_data(request_packet, response_packet)
|
21
|
+
if @dialect == '0x0311'
|
22
|
+
update_preauth_hash(request_packet)
|
23
|
+
update_preauth_hash(response_packet)
|
26
24
|
end
|
25
|
+
|
27
26
|
# If the response contains the SMB2 wildcard revision number dialect;
|
28
27
|
# it indicates that the server implements SMB 2.1 or future dialect
|
29
28
|
# revisions and expects the client to send a subsequent SMB2 Negotiate
|
@@ -84,16 +83,14 @@ module RubySMB
|
|
84
83
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
85
84
|
expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
|
86
85
|
expected_custom: "extended_security=1",
|
87
|
-
|
88
|
-
received_cmd: packet.smb_header.command,
|
86
|
+
packet: packet,
|
89
87
|
received_custom: "extended_security=#{extended_security}"
|
90
88
|
)
|
91
89
|
elsif packet.packet_smb_version == 'SMB2'
|
92
90
|
raise RubySMB::Error::InvalidPacket.new(
|
93
91
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
94
92
|
expected_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
|
95
|
-
|
96
|
-
received_cmd: packet.smb2_header.command
|
93
|
+
packet: packet
|
97
94
|
)
|
98
95
|
else
|
99
96
|
raise RubySMB::Error::InvalidPacket, 'Unknown SMB protocol version'
|
@@ -122,6 +119,7 @@ module RubySMB
|
|
122
119
|
# protocol overhead every time.
|
123
120
|
self.server_max_buffer_size = packet.parameter_block.max_buffer_size - 260
|
124
121
|
self.negotiated_smb_version = 1
|
122
|
+
self.session_encrypt_data = false
|
125
123
|
'SMB1'
|
126
124
|
when RubySMB::SMB2::Packet::NegotiateResponse
|
127
125
|
self.smb1 = false
|
@@ -137,6 +135,23 @@ module RubySMB
|
|
137
135
|
# This value is used in SMB1 only but calculate a valid value anyway
|
138
136
|
self.server_max_buffer_size = [self.server_max_read_size, self.server_max_write_size, self.server_max_transact_size].min
|
139
137
|
self.negotiated_smb_version = self.smb2 ? 2 : 3
|
138
|
+
self.server_guid = packet.server_guid
|
139
|
+
self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
|
140
|
+
self.server_system_time = packet.system_time.to_time if packet.system_time != 0
|
141
|
+
self.server_supports_multi_credit = self.dialect != '0x0202' && packet&.capabilities&.large_mtu == 1
|
142
|
+
case self.dialect
|
143
|
+
when '0x02ff'
|
144
|
+
when '0x0300', '0x0302'
|
145
|
+
if packet&.capabilities&.encryption == 1
|
146
|
+
self.encryption_algorithm = RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM]
|
147
|
+
end
|
148
|
+
self.session_encrypt_data = self.session_encrypt_data && !self.encryption_algorithm.nil?
|
149
|
+
when '0x0311'
|
150
|
+
parse_smb3_capabilities(packet)
|
151
|
+
self.session_encrypt_data = self.session_encrypt_data && !self.encryption_algorithm.nil?
|
152
|
+
else
|
153
|
+
self.session_encrypt_data = false
|
154
|
+
end
|
140
155
|
return "SMB#{self.negotiated_smb_version}"
|
141
156
|
else
|
142
157
|
error = 'Unable to negotiate with remote host'
|
@@ -149,7 +164,7 @@ module RubySMB
|
|
149
164
|
end
|
150
165
|
end
|
151
166
|
|
152
|
-
def
|
167
|
+
def parse_smb3_capabilities(response_packet)
|
153
168
|
nc = response_packet.find_negotiate_context(
|
154
169
|
RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
155
170
|
)
|
@@ -179,8 +194,6 @@ module RubySMB
|
|
179
194
|
'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
|
180
195
|
)
|
181
196
|
end
|
182
|
-
update_preauth_hash(request_packet)
|
183
|
-
update_preauth_hash(response_packet)
|
184
197
|
|
185
198
|
nc = response_packet.find_negotiate_context(
|
186
199
|
RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
|
@@ -198,6 +211,12 @@ module RubySMB
|
|
198
211
|
# while being guaranteed to work with any modern Windows system. We can get more sophisticated
|
199
212
|
# with switching this on and off at a later date if the need arises.
|
200
213
|
packet.smb_header.flags2.extended_security = 1
|
214
|
+
# Recent Mac OS X requires the unicode flag to be set on the Negotiate
|
215
|
+
# SMB Header request, even if this packet does not contain string fields
|
216
|
+
# (see Flags2 SMB_FLAGS2_UNICODE definition in "2.2.3.1 The SMB Header"
|
217
|
+
# documentation:
|
218
|
+
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/69a29f73-de0c-45a6-a1aa-8ceeea42217f
|
219
|
+
packet.smb_header.flags2.unicode = 1
|
201
220
|
# There is no real good reason to ever send an SMB1 Negotiate packet
|
202
221
|
# to Negotiate strictly SMB2, but the protocol WILL support it
|
203
222
|
packet.add_dialect(SMB1_DIALECT_SMB1_DEFAULT) if smb1
|