ruby_smb 2.0.0 → 2.0.1
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 +0 -0
- data.tar.gz.sig +1 -1
- data/lib/ruby_smb/client.rb +41 -10
- data/lib/ruby_smb/client/authentication.rb +2 -2
- data/lib/ruby_smb/client/negotiation.rb +12 -0
- data/lib/ruby_smb/dispatcher/socket.rb +2 -1
- data/lib/ruby_smb/smb1/tree.rb +9 -0
- data/lib/ruby_smb/smb2/file.rb +12 -12
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +9 -9
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +64 -19
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/file_spec.rb +12 -12
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +6 -6
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a683fe9443f70b1f4e7dc98d02d7f4deaf785ae94097752696c516977bc3a36a
|
4
|
+
data.tar.gz: 5e74a2b2daaedb9b2f096ef05fcd5465a9caa1b9672af6e42d13f18b70e47110
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: efaaf45fb9fb49bd37a3f45681800f26437efdd76bd6b06ee763768c3674292fe3e7bc35946a489300a3d696a7f060cad2802335aabfd766dd6bc8dd9f503fdd
|
7
|
+
data.tar.gz: cef65fae806b5b6bce656ef799f5ca4376a0cc71e1ad5746ddaf44898652580ce990d728960b2a4767898d1e7ebcb8e12d997919123b4cbc4a2d113c7db57822
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
���}�T�e�X��*|�#VȕH�A�p��&n������I�u�rRႠ��� ��8�c��`鏐K���y�(�{Y��K9
9���6�J���_*����� ���G�"�Y�&3="��}<�P��ݘf����L�d�U�Rڨ^O�@����Ab��Մ�Lӊ�������<������o�ǿ�_��q���Ɩ])�����=>z?��\h8�բz�V�����ߨ����~
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -216,10 +216,10 @@ module RubySMB
|
|
216
216
|
# @return [String]
|
217
217
|
attr_accessor :server_encryption_key
|
218
218
|
|
219
|
-
# Whether or not
|
220
|
-
# @!attribute [rw]
|
219
|
+
# Whether or not the whole session needs to be encrypted (SMB 3.x)
|
220
|
+
# @!attribute [rw] session_encrypt_data
|
221
221
|
# @return [Boolean]
|
222
|
-
attr_accessor :
|
222
|
+
attr_accessor :session_encrypt_data
|
223
223
|
|
224
224
|
# The encryption algorithms supported by the server (SMB 3.x).
|
225
225
|
# @!attribute [rw] server_encryption_algorithms
|
@@ -233,6 +233,25 @@ module RubySMB
|
|
233
233
|
# (constants defined in RubySMB::SMB2::CompressionCapabilities)
|
234
234
|
attr_accessor :server_compression_algorithms
|
235
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
|
+
|
236
255
|
# The SMB version that has been successfully negotiated. This value is only
|
237
256
|
# set after the NEGOTIATE handshake has been performed.
|
238
257
|
# @!attribute [rw] negotiated_smb_version
|
@@ -268,12 +287,13 @@ module RubySMB
|
|
268
287
|
@server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
269
288
|
|
270
289
|
# SMB 3.x options
|
271
|
-
@
|
290
|
+
@session_encrypt_data = always_encrypt
|
272
291
|
|
273
292
|
negotiate_version_flag = 0x02000000
|
274
293
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
275
294
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
276
|
-
negotiate_version_flag
|
295
|
+
negotiate_version_flag ^
|
296
|
+
Net::NTLM::FLAGS[:OEM]
|
277
297
|
|
278
298
|
@ntlm_client = Net::NTLM::Client.new(
|
279
299
|
@username,
|
@@ -347,7 +367,8 @@ module RubySMB
|
|
347
367
|
negotiate_version_flag = 0x02000000
|
348
368
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
349
369
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
350
|
-
negotiate_version_flag
|
370
|
+
negotiate_version_flag ^
|
371
|
+
Net::NTLM::FLAGS[:OEM]
|
351
372
|
|
352
373
|
@ntlm_client = Net::NTLM::Client.new(
|
353
374
|
@username,
|
@@ -398,6 +419,9 @@ module RubySMB
|
|
398
419
|
# It will also sign the packet if neccessary.
|
399
420
|
#
|
400
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)
|
401
425
|
# @return [String] the raw response data received
|
402
426
|
def send_recv(packet, encrypt: false)
|
403
427
|
version = packet.packet_smb_version
|
@@ -419,7 +443,7 @@ module RubySMB
|
|
419
443
|
packet = packet
|
420
444
|
end
|
421
445
|
|
422
|
-
if can_be_encrypted?(packet) && encryption_supported? && (@
|
446
|
+
if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
|
423
447
|
send_encrypt(packet)
|
424
448
|
raw_response = recv_encrypt
|
425
449
|
loop do
|
@@ -454,18 +478,19 @@ module RubySMB
|
|
454
478
|
smb2_header.flags.async_command == 1
|
455
479
|
end
|
456
480
|
|
457
|
-
# Check if the request packet can be encrypted. Per the
|
481
|
+
# Check if the request packet can be encrypted. Per the SMB2 spec,
|
458
482
|
# SessionSetupRequest and NegotiateRequest must not be encrypted.
|
459
483
|
#
|
460
484
|
# @param packet [RubySMB::GenericPacket] the request packet
|
461
485
|
# @return [Boolean] true if the packet can be encrypted
|
462
486
|
def can_be_encrypted?(packet)
|
487
|
+
return false if packet.packet_smb_version == 'SMB1'
|
463
488
|
[RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
|
464
489
|
packet.is_a?(klass)
|
465
490
|
end
|
466
491
|
end
|
467
492
|
|
468
|
-
# Check if the current dialect
|
493
|
+
# Check if the current dialect supports encryption.
|
469
494
|
#
|
470
495
|
# @return [Boolean] true if encryption is supported
|
471
496
|
def encryption_supported?
|
@@ -486,7 +511,13 @@ module RubySMB
|
|
486
511
|
#
|
487
512
|
# @return [String] the raw unencrypted packet
|
488
513
|
def recv_encrypt
|
489
|
-
|
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
|
490
521
|
begin
|
491
522
|
transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
|
492
523
|
rescue IOError
|
@@ -216,8 +216,8 @@ module RubySMB
|
|
216
216
|
raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
|
217
217
|
response = smb2_ntlmssp_final_packet(raw)
|
218
218
|
|
219
|
-
if @smb3 && !@
|
220
|
-
@
|
219
|
+
if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
|
220
|
+
@session_encrypt_data = true
|
221
221
|
end
|
222
222
|
######
|
223
223
|
# DEBUG
|
@@ -24,6 +24,7 @@ module RubySMB
|
|
24
24
|
when '0x0311'
|
25
25
|
parse_smb3_encryption_data(request_packet, response_packet)
|
26
26
|
end
|
27
|
+
|
27
28
|
# If the response contains the SMB2 wildcard revision number dialect;
|
28
29
|
# it indicates that the server implements SMB 2.1 or future dialect
|
29
30
|
# revisions and expects the client to send a subsequent SMB2 Negotiate
|
@@ -128,6 +129,8 @@ module RubySMB
|
|
128
129
|
unless packet.dialect_revision.to_i == 0x02ff
|
129
130
|
self.smb2 = packet.dialect_revision.to_i >= 0x0200 && packet.dialect_revision.to_i < 0x0300
|
130
131
|
self.smb3 = packet.dialect_revision.to_i >= 0x0300 && packet.dialect_revision.to_i < 0x0400
|
132
|
+
# Only enable session encryption if the server supports it
|
133
|
+
@session_encrypt_data = self.smb3 && @session_encrypt_data && packet.capabilities.encryption == 1
|
131
134
|
end
|
132
135
|
self.signing_required = packet.security_mode.signing_required == 1 if self.smb2 || self.smb3
|
133
136
|
self.dialect = "0x%04x" % packet.dialect_revision
|
@@ -137,6 +140,9 @@ module RubySMB
|
|
137
140
|
# This value is used in SMB1 only but calculate a valid value anyway
|
138
141
|
self.server_max_buffer_size = [self.server_max_read_size, self.server_max_write_size, self.server_max_transact_size].min
|
139
142
|
self.negotiated_smb_version = self.smb2 ? 2 : 3
|
143
|
+
self.server_guid = packet.server_guid
|
144
|
+
self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
|
145
|
+
self.server_system_time = packet.system_time.to_time if packet.system_time != 0
|
140
146
|
return "SMB#{self.negotiated_smb_version}"
|
141
147
|
else
|
142
148
|
error = 'Unable to negotiate with remote host'
|
@@ -198,6 +204,12 @@ module RubySMB
|
|
198
204
|
# while being guaranteed to work with any modern Windows system. We can get more sophisticated
|
199
205
|
# with switching this on and off at a later date if the need arises.
|
200
206
|
packet.smb_header.flags2.extended_security = 1
|
207
|
+
# Recent Mac OS X requires the unicode flag to be set on the Negotiate
|
208
|
+
# SMB Header request, even if this packet does not contain string fields
|
209
|
+
# (see Flags2 SMB_FLAGS2_UNICODE definition in "2.2.3.1 The SMB Header"
|
210
|
+
# documentation:
|
211
|
+
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/69a29f73-de0c-45a6-a1aa-8ceeea42217f
|
212
|
+
packet.smb_header.flags2.unicode = 1
|
201
213
|
# There is no real good reason to ever send an SMB1 Negotiate packet
|
202
214
|
# to Negotiate strictly SMB2, but the protocol WILL support it
|
203
215
|
packet.add_dialect(SMB1_DIALECT_SMB1_DEFAULT) if smb1
|
@@ -61,13 +61,14 @@ module RubySMB
|
|
61
61
|
# which are assumed to be the NetBiosSessionService header.
|
62
62
|
# @raise [RubySMB::Error::CommunicationError] if the read timeout expires or an error occurs when reading the socket
|
63
63
|
def recv_packet(full_response: false)
|
64
|
+
raise RubySMB::Error::CommunicationError, 'Connection has already been closed' if @tcp_socket.closed?
|
64
65
|
if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
|
65
66
|
raise RubySMB::Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
|
66
67
|
end
|
67
68
|
|
68
69
|
begin
|
69
70
|
nbss_data = @tcp_socket.read(4)
|
70
|
-
raise
|
71
|
+
raise RubySMB::Error::CommunicationError, 'Socket read returned nil' if nbss_data.nil?
|
71
72
|
nbss_header = RubySMB::Nbss::SessionHeader.read(nbss_data)
|
72
73
|
rescue IOError
|
73
74
|
raise ::RubySMB::Error::NetBiosSessionService, 'NBSS Header is missing'
|
data/lib/ruby_smb/smb1/tree.rb
CHANGED
@@ -123,6 +123,15 @@ module RubySMB
|
|
123
123
|
raw_response = @client.send_recv(nt_create_andx_request)
|
124
124
|
response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
|
125
125
|
unless response.valid?
|
126
|
+
if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
|
127
|
+
response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
|
128
|
+
response.smb_header.command == response.original_command
|
129
|
+
raise RubySMB::Error::InvalidPacket.new(
|
130
|
+
'The response seems to be an SMB1 NtCreateAndxResponse but an '\
|
131
|
+
'error occurs while parsing it. It is probably missing the '\
|
132
|
+
'required extended information.'
|
133
|
+
)
|
134
|
+
end
|
126
135
|
raise RubySMB::Error::InvalidPacket.new(
|
127
136
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
128
137
|
expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
|
data/lib/ruby_smb/smb2/file.rb
CHANGED
@@ -52,10 +52,10 @@ module RubySMB
|
|
52
52
|
# @return [RubySMB::SMB2::Tree]
|
53
53
|
attr_accessor :tree
|
54
54
|
|
55
|
-
# Whether or not
|
56
|
-
# @!attribute [rw]
|
55
|
+
# Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
|
56
|
+
# @!attribute [rw] tree_connect_encrypt_data
|
57
57
|
# @return [Boolean]
|
58
|
-
attr_accessor :
|
58
|
+
attr_accessor :tree_connect_encrypt_data
|
59
59
|
|
60
60
|
def initialize(tree:, response:, name:, encrypt: false)
|
61
61
|
raise ArgumentError, 'No Tree Provided' if tree.nil?
|
@@ -71,7 +71,7 @@ module RubySMB
|
|
71
71
|
@last_write = response.last_write.to_datetime
|
72
72
|
@size = response.end_of_file
|
73
73
|
@size_on_disk = response.allocation_size
|
74
|
-
@
|
74
|
+
@tree_connect_encrypt_data = encrypt
|
75
75
|
end
|
76
76
|
|
77
77
|
# Appends the supplied data to the end of the file.
|
@@ -89,7 +89,7 @@ module RubySMB
|
|
89
89
|
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
90
90
|
def close
|
91
91
|
close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
|
92
|
-
raw_response = tree.client.send_recv(close_request, encrypt: @
|
92
|
+
raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
|
93
93
|
response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
|
94
94
|
unless response.valid?
|
95
95
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -122,7 +122,7 @@ module RubySMB
|
|
122
122
|
end
|
123
123
|
|
124
124
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
125
|
-
raw_response = tree.client.send_recv(read_request, encrypt: @
|
125
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
126
126
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
127
127
|
unless response.valid?
|
128
128
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -145,7 +145,7 @@ module RubySMB
|
|
145
145
|
atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
|
146
146
|
|
147
147
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
148
|
-
raw_response = tree.client.send_recv(read_request, encrypt: @
|
148
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
149
149
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
150
150
|
unless response.valid?
|
151
151
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -179,7 +179,7 @@ module RubySMB
|
|
179
179
|
|
180
180
|
def send_recv_read(read_length: 0, offset: 0)
|
181
181
|
read_request = read_packet(read_length: read_length, offset: offset)
|
182
|
-
raw_response = tree.client.send_recv(read_request, encrypt: @
|
182
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
183
183
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
184
184
|
unless response.valid?
|
185
185
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -200,7 +200,7 @@ module RubySMB
|
|
200
200
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
201
201
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
202
202
|
def delete
|
203
|
-
raw_response = tree.client.send_recv(delete_packet, encrypt: @
|
203
|
+
raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
|
204
204
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
205
205
|
unless response.valid?
|
206
206
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -250,7 +250,7 @@ module RubySMB
|
|
250
250
|
|
251
251
|
while buffer.length > 0 do
|
252
252
|
write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
|
253
|
-
raw_response = tree.client.send_recv(write_request, encrypt: @
|
253
|
+
raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
|
254
254
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
255
255
|
unless response.valid?
|
256
256
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -283,7 +283,7 @@ module RubySMB
|
|
283
283
|
|
284
284
|
def send_recv_write(data:'', offset: 0)
|
285
285
|
pkt = write_packet(data: data, offset: offset)
|
286
|
-
raw_response = tree.client.send_recv(pkt, encrypt: @
|
286
|
+
raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
|
287
287
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
288
288
|
unless response.valid?
|
289
289
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -305,7 +305,7 @@ module RubySMB
|
|
305
305
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
306
306
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
307
307
|
def rename(new_file_name)
|
308
|
-
raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @
|
308
|
+
raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
|
309
309
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
310
310
|
unless response.valid?
|
311
311
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -15,7 +15,7 @@ module RubySMB
|
|
15
15
|
uint16 :dialect_revision, label: 'Dialect Revision'
|
16
16
|
uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: -> { negotiate_context_list.size }, onlyif: -> { has_negotiate_context? }
|
17
17
|
uint16 :reserved1, label: 'Reserved', initial_value: 0, onlyif: -> { !has_negotiate_context? }
|
18
|
-
string :server_guid, label: 'Server GUID',
|
18
|
+
string :server_guid, label: 'Server GUID', length: 16
|
19
19
|
smb2_capabilities :capabilities
|
20
20
|
uint32 :max_transact_size, label: 'Max Transaction Size'
|
21
21
|
uint32 :max_read_size, label: 'Max Read Size'
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -23,10 +23,10 @@ module RubySMB
|
|
23
23
|
# @return [Integer]
|
24
24
|
attr_accessor :id
|
25
25
|
|
26
|
-
# Whether or not
|
27
|
-
# @!attribute [rw]
|
26
|
+
# Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
|
27
|
+
# @!attribute [rw] tree_connect_encrypt_data
|
28
28
|
# @return [Boolean]
|
29
|
-
attr_accessor :
|
29
|
+
attr_accessor :tree_connect_encrypt_data
|
30
30
|
|
31
31
|
def initialize(client:, share:, response:, encrypt: false)
|
32
32
|
@client = client
|
@@ -34,7 +34,7 @@ module RubySMB
|
|
34
34
|
@id = response.smb2_header.tree_id
|
35
35
|
@permissions = response.maximal_access
|
36
36
|
@share_type = response.share_type
|
37
|
-
@
|
37
|
+
@tree_connect_encrypt_data = encrypt
|
38
38
|
end
|
39
39
|
|
40
40
|
# Disconnects this Tree from the current session
|
@@ -44,7 +44,7 @@ module RubySMB
|
|
44
44
|
def disconnect!
|
45
45
|
request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
|
46
46
|
request = set_header_fields(request)
|
47
|
-
raw_response = client.send_recv(request, encrypt: @
|
47
|
+
raw_response = client.send_recv(request, encrypt: @tree_connect_encrypt_data)
|
48
48
|
response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
|
49
49
|
unless response.valid?
|
50
50
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -102,7 +102,7 @@ module RubySMB
|
|
102
102
|
create_request.create_disposition = disposition
|
103
103
|
create_request.name = filename
|
104
104
|
|
105
|
-
raw_response = client.send_recv(create_request, encrypt: @
|
105
|
+
raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
|
106
106
|
response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
107
107
|
unless response.valid?
|
108
108
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -118,7 +118,7 @@ module RubySMB
|
|
118
118
|
|
119
119
|
case @share_type
|
120
120
|
when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK
|
121
|
-
RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @
|
121
|
+
RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @tree_connect_encrypt_data)
|
122
122
|
when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE
|
123
123
|
RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response)
|
124
124
|
# when RubySMB::SMB2::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
|
@@ -154,7 +154,7 @@ module RubySMB
|
|
154
154
|
files = []
|
155
155
|
|
156
156
|
loop do
|
157
|
-
response = client.send_recv(directory_request, encrypt: @
|
157
|
+
response = client.send_recv(directory_request, encrypt: @tree_connect_encrypt_data)
|
158
158
|
directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
|
159
159
|
unless directory_response.valid?
|
160
160
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -199,7 +199,7 @@ module RubySMB
|
|
199
199
|
|
200
200
|
create_request = open_directory_packet(directory: directory, disposition: disposition,
|
201
201
|
impersonation: impersonation, read: read, write: write, delete: delete)
|
202
|
-
raw_response = client.send_recv(create_request, encrypt: @
|
202
|
+
raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
|
203
203
|
response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
204
204
|
unless response.valid?
|
205
205
|
raise RubySMB::Error::InvalidPacket.new(
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -46,7 +46,7 @@ RSpec.describe RubySMB::Client do
|
|
46
46
|
it { is_expected.to respond_to :encryption_algorithm }
|
47
47
|
it { is_expected.to respond_to :client_encryption_key }
|
48
48
|
it { is_expected.to respond_to :server_encryption_key }
|
49
|
-
it { is_expected.to respond_to :
|
49
|
+
it { is_expected.to respond_to :session_encrypt_data }
|
50
50
|
it { is_expected.to respond_to :server_encryption_algorithms }
|
51
51
|
it { is_expected.to respond_to :server_compression_algorithms }
|
52
52
|
it { is_expected.to respond_to :negotiated_smb_version }
|
@@ -106,9 +106,9 @@ RSpec.describe RubySMB::Client do
|
|
106
106
|
expect(client.password).to eq password
|
107
107
|
end
|
108
108
|
|
109
|
-
it 'sets the
|
109
|
+
it 'sets the session_encrypt_data attribute' do
|
110
110
|
client = described_class.new(dispatcher, username: username, password: password, always_encrypt: true)
|
111
|
-
expect(client.
|
111
|
+
expect(client.session_encrypt_data).to eq true
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'creates an NTLM client' do
|
@@ -125,7 +125,7 @@ RSpec.describe RubySMB::Client do
|
|
125
125
|
expect(opt[:workstation]).to eq(local_workstation)
|
126
126
|
expect(opt[:domain]).to eq(domain)
|
127
127
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
128
|
-
Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000
|
128
|
+
Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000 ^ Net::NTLM::FLAGS[:OEM]
|
129
129
|
expect(opt[:flags]).to eq(flags)
|
130
130
|
end
|
131
131
|
|
@@ -191,11 +191,6 @@ RSpec.describe RubySMB::Client do
|
|
191
191
|
allow(dispatcher).to receive(:recv_packet).and_return('A')
|
192
192
|
end
|
193
193
|
|
194
|
-
it 'checks the packet version' do
|
195
|
-
expect(smb1_request).to receive(:packet_smb_version).and_call_original
|
196
|
-
client.send_recv(smb1_request)
|
197
|
-
end
|
198
|
-
|
199
194
|
context 'when signing' do
|
200
195
|
it 'calls #smb1_sign if it is an SMB1 packet' do
|
201
196
|
expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
|
@@ -322,6 +317,11 @@ RSpec.describe RubySMB::Client do
|
|
322
317
|
expect(client.can_be_encrypted?(packet)).to be true
|
323
318
|
end
|
324
319
|
|
320
|
+
it 'returns false if it is an SMB1 packet' do
|
321
|
+
packet = RubySMB::SMB1::Packet::LogoffRequest.new
|
322
|
+
expect(client.can_be_encrypted?(packet)).to be false
|
323
|
+
end
|
324
|
+
|
325
325
|
[RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].each do |klass|
|
326
326
|
it "returns false if the packet is a #{klass}" do
|
327
327
|
packet = klass.new
|
@@ -385,6 +385,15 @@ RSpec.describe RubySMB::Client do
|
|
385
385
|
expect(dispatcher).to have_received(:recv_packet)
|
386
386
|
end
|
387
387
|
|
388
|
+
it 'raises an EncryptionError exception if an error occurs while receiving the response' do
|
389
|
+
allow(dispatcher).to receive(:recv_packet).and_raise(RubySMB::Error::CommunicationError)
|
390
|
+
expect { client.recv_encrypt }.to raise_error(
|
391
|
+
RubySMB::Error::EncryptionError,
|
392
|
+
'Communication error with the remote host: RubySMB::Error::CommunicationError. '\
|
393
|
+
'The server supports encryption but was not able to handle the encrypted request.'
|
394
|
+
)
|
395
|
+
end
|
396
|
+
|
388
397
|
it 'parses the response as a Transform response packet' do
|
389
398
|
expect(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).with(packet.to_binary_s)
|
390
399
|
client.recv_encrypt
|
@@ -392,7 +401,7 @@ RSpec.describe RubySMB::Client do
|
|
392
401
|
|
393
402
|
it 'raises an InvalidPacket exception if an error occurs while parsing the response' do
|
394
403
|
allow(RubySMB::SMB2::Packet::TransformHeader).to receive(:read).and_raise(IOError)
|
395
|
-
expect { client.recv_encrypt}.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
|
404
|
+
expect { client.recv_encrypt }.to raise_error(RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet')
|
396
405
|
end
|
397
406
|
|
398
407
|
it 'decrypts the Transform response packet' do
|
@@ -403,10 +412,10 @@ RSpec.describe RubySMB::Client do
|
|
403
412
|
end
|
404
413
|
|
405
414
|
it 'raises an EncryptionError exception if an error occurs while decrypting' do
|
406
|
-
allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError
|
407
|
-
expect { client.recv_encrypt}.to raise_error(
|
415
|
+
allow(client).to receive(:smb3_decrypt).and_raise(RubySMB::Error::RubySMBError)
|
416
|
+
expect { client.recv_encrypt }.to raise_error(
|
408
417
|
RubySMB::Error::EncryptionError,
|
409
|
-
|
418
|
+
'Error while decrypting RubySMB::SMB2::Packet::TransformHeader packet (SMB 0x0300}): RubySMB::Error::RubySMBError'
|
410
419
|
)
|
411
420
|
end
|
412
421
|
end
|
@@ -1050,6 +1059,42 @@ RSpec.describe RubySMB::Client do
|
|
1050
1059
|
it 'returns the string \'SMB2\'' do
|
1051
1060
|
expect(client.parse_negotiate_response(smb3_response)).to eq ('SMB3')
|
1052
1061
|
end
|
1062
|
+
|
1063
|
+
context 'when the server supports encryption' do
|
1064
|
+
before :example do
|
1065
|
+
smb3_response.capabilities.encryption = 1
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
it 'keeps session encryption enabled if it was already' do
|
1069
|
+
client.session_encrypt_data = true
|
1070
|
+
client.parse_negotiate_response(smb3_response)
|
1071
|
+
expect(client.session_encrypt_data).to be true
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
it 'keeps session encryption disabled if it was already' do
|
1075
|
+
client.session_encrypt_data = false
|
1076
|
+
client.parse_negotiate_response(smb3_response)
|
1077
|
+
expect(client.session_encrypt_data).to be false
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
context 'when the server does not support encryption' do
|
1082
|
+
before :example do
|
1083
|
+
smb3_response.capabilities.encryption = 0
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
it 'disables session encryption if it was already enabled' do
|
1087
|
+
client.session_encrypt_data = true
|
1088
|
+
client.parse_negotiate_response(smb3_response)
|
1089
|
+
expect(client.session_encrypt_data).to be false
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
it 'keeps session encryption disabled if it was already' do
|
1093
|
+
client.session_encrypt_data = false
|
1094
|
+
client.parse_negotiate_response(smb3_response)
|
1095
|
+
expect(client.session_encrypt_data).to be false
|
1096
|
+
end
|
1097
|
+
end
|
1053
1098
|
end
|
1054
1099
|
|
1055
1100
|
context 'when the response contains the SMB2 wildcard revision number dialect' do
|
@@ -1710,22 +1755,22 @@ RSpec.describe RubySMB::Client do
|
|
1710
1755
|
smb2_client.smb2_authenticate
|
1711
1756
|
end
|
1712
1757
|
|
1713
|
-
context 'when setting the
|
1758
|
+
context 'when setting the session_encrypt_data parameter' do
|
1714
1759
|
before :example do
|
1715
1760
|
smb2_client.smb3 = true
|
1716
|
-
smb2_client.
|
1761
|
+
smb2_client.session_encrypt_data = false
|
1717
1762
|
end
|
1718
1763
|
|
1719
|
-
it 'sets the
|
1764
|
+
it 'sets the session_encrypt_data parameter to true if the server requires encryption' do
|
1720
1765
|
final_response_packet.session_flags.encrypt_data = 1
|
1721
1766
|
smb2_client.smb2_authenticate
|
1722
|
-
expect(smb2_client.
|
1767
|
+
expect(smb2_client.session_encrypt_data).to be true
|
1723
1768
|
end
|
1724
1769
|
|
1725
|
-
it 'does not set the
|
1770
|
+
it 'does not set the session_encrypt_data parameter if the server does not require encryption' do
|
1726
1771
|
final_response_packet.session_flags.encrypt_data = 0
|
1727
1772
|
smb2_client.smb2_authenticate
|
1728
|
-
expect(smb2_client.
|
1773
|
+
expect(smb2_client.session_encrypt_data).to be false
|
1729
1774
|
end
|
1730
1775
|
end
|
1731
1776
|
end
|
@@ -51,9 +51,9 @@ RSpec.describe RubySMB::Dispatcher::Socket do
|
|
51
51
|
let(:session_header) { RubySMB::Nbss::SessionHeader.new }
|
52
52
|
|
53
53
|
context 'when reading from the socket results in a nil value' do
|
54
|
-
it 'should raise Error::
|
54
|
+
it 'should raise Error::CommunicationError' do
|
55
55
|
smb_socket.tcp_socket = blank_socket
|
56
|
-
expect { smb_socket.recv_packet }.to raise_error(::RubySMB::Error::
|
56
|
+
expect { smb_socket.recv_packet }.to raise_error(::RubySMB::Error::CommunicationError)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -43,7 +43,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
43
43
|
it { is_expected.to respond_to :size }
|
44
44
|
it { is_expected.to respond_to :size_on_disk }
|
45
45
|
it { is_expected.to respond_to :tree }
|
46
|
-
it { is_expected.to respond_to :
|
46
|
+
it { is_expected.to respond_to :tree_connect_encrypt_data }
|
47
47
|
|
48
48
|
it 'pulls the attributes from the response packet' do
|
49
49
|
expect(file.attributes).to eq create_response.file_attributes
|
@@ -73,8 +73,8 @@ RSpec.describe RubySMB::SMB2::File do
|
|
73
73
|
expect(file.size_on_disk).to eq create_response.allocation_size
|
74
74
|
end
|
75
75
|
|
76
|
-
it 'sets the
|
77
|
-
expect(file.
|
76
|
+
it 'sets the tree_connect_encrypt_data flag to false by default' do
|
77
|
+
expect(file.tree_connect_encrypt_data).to be false
|
78
78
|
end
|
79
79
|
|
80
80
|
describe '#set_header_fields' do
|
@@ -132,7 +132,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
132
132
|
end
|
133
133
|
|
134
134
|
it 'calls Client #send_recv with encryption set if required' do
|
135
|
-
file.
|
135
|
+
file.tree_connect_encrypt_data = true
|
136
136
|
expect(client).to receive(:send_recv).with(small_read, encrypt: true)
|
137
137
|
file.read
|
138
138
|
end
|
@@ -177,7 +177,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
177
177
|
it 'calls Client #send_recv with encryption set if required' do
|
178
178
|
read_request = double('Read Request')
|
179
179
|
allow(file).to receive(:read_packet).and_return(read_request)
|
180
|
-
file.
|
180
|
+
file.tree_connect_encrypt_data = true
|
181
181
|
expect(client).to receive(:send_recv).twice.with(read_request, encrypt: true)
|
182
182
|
file.read(bytes: (described_class::MAX_PACKET_SIZE * 2))
|
183
183
|
end
|
@@ -235,7 +235,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
235
235
|
it 'calls Client #send_recv with encryption set if required' do
|
236
236
|
write_request = double('Write Request')
|
237
237
|
allow(file).to receive(:write_packet).and_return(write_request)
|
238
|
-
file.
|
238
|
+
file.tree_connect_encrypt_data = true
|
239
239
|
expect(client).to receive(:send_recv).once.with(write_request, encrypt: true).and_return(write_response.to_binary_s)
|
240
240
|
file.write(data: 'test')
|
241
241
|
end
|
@@ -250,7 +250,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
250
250
|
it 'calls Client #send_recv with encryption set if required' do
|
251
251
|
write_request = double('Write Request')
|
252
252
|
allow(file).to receive(:write_packet).and_return(write_request)
|
253
|
-
file.
|
253
|
+
file.tree_connect_encrypt_data = true
|
254
254
|
expect(client).to receive(:send_recv).twice.with(write_request, encrypt: true).and_return(write_response.to_binary_s)
|
255
255
|
file.write(data: SecureRandom.random_bytes(described_class::MAX_PACKET_SIZE + 1))
|
256
256
|
end
|
@@ -307,7 +307,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
307
307
|
it 'calls Client #send_recv with encryption set if required' do
|
308
308
|
allow(file).to receive(:delete_packet)
|
309
309
|
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
310
|
-
file.
|
310
|
+
file.tree_connect_encrypt_data = true
|
311
311
|
expect(client).to receive(:send_recv).with(small_delete, encrypt: true)
|
312
312
|
file.delete
|
313
313
|
end
|
@@ -349,7 +349,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
349
349
|
|
350
350
|
it 'calls Client #send_recv with encryption set if required' do
|
351
351
|
allow(RubySMB::SMB2::Packet::SetInfoResponse).to receive(:read).and_return(small_response)
|
352
|
-
file.
|
352
|
+
file.tree_connect_encrypt_data = true
|
353
353
|
expect(client).to receive(:send_recv).with(small_rename, encrypt: true)
|
354
354
|
file.rename('new_file.txt')
|
355
355
|
end
|
@@ -393,7 +393,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
393
393
|
end
|
394
394
|
|
395
395
|
it 'calls Client #send_recv with encryption set if required' do
|
396
|
-
file.
|
396
|
+
file.tree_connect_encrypt_data = true
|
397
397
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
398
398
|
file.close
|
399
399
|
end
|
@@ -465,7 +465,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
465
465
|
it 'calls Client #send_recv with encryption set if required' do
|
466
466
|
request = double('Request')
|
467
467
|
allow(file).to receive(:read_packet).and_return(request)
|
468
|
-
file.
|
468
|
+
file.tree_connect_encrypt_data = true
|
469
469
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
470
470
|
file.send_recv_read
|
471
471
|
end
|
@@ -528,7 +528,7 @@ RSpec.describe RubySMB::SMB2::File do
|
|
528
528
|
end
|
529
529
|
|
530
530
|
it 'calls Client #send_recv with encryption set if required' do
|
531
|
-
file.
|
531
|
+
file.tree_connect_encrypt_data = true
|
532
532
|
expect(client).to receive(:send_recv).with(request, encrypt: true)
|
533
533
|
file.send_recv_write
|
534
534
|
end
|
@@ -26,7 +26,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
26
26
|
it { is_expected.to respond_to :permissions }
|
27
27
|
it { is_expected.to respond_to :share }
|
28
28
|
it { is_expected.to respond_to :id }
|
29
|
-
it { is_expected.to respond_to :
|
29
|
+
it { is_expected.to respond_to :tree_connect_encrypt_data }
|
30
30
|
|
31
31
|
it 'inherits the client that spawned it' do
|
32
32
|
expect(tree.client).to eq client
|
@@ -51,7 +51,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
51
51
|
|
52
52
|
it 'calls Client #send_recv with encryption set if required' do
|
53
53
|
allow(tree).to receive(:set_header_fields).and_return(disco_req)
|
54
|
-
tree.
|
54
|
+
tree.tree_connect_encrypt_data = true
|
55
55
|
expect(client).to receive(:send_recv).with(disco_req, encrypt: true).and_return(disco_resp.to_binary_s)
|
56
56
|
tree.disconnect!
|
57
57
|
end
|
@@ -147,7 +147,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
147
147
|
|
148
148
|
it 'calls Client #send_recv with encryption set if required' do
|
149
149
|
allow(tree).to receive(:open_directory_packet).and_return(create_req)
|
150
|
-
tree.
|
150
|
+
tree.tree_connect_encrypt_data = true
|
151
151
|
expect(client).to receive(:send_recv).with(create_req, encrypt: true).and_return(create_response.to_binary_s)
|
152
152
|
tree.open_directory
|
153
153
|
end
|
@@ -229,7 +229,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
229
229
|
|
230
230
|
it 'calls Client #send_recv with encryption set if required' do
|
231
231
|
allow(tree).to receive(:set_header_fields).and_return(query_dir_req)
|
232
|
-
tree.
|
232
|
+
tree.tree_connect_encrypt_data = true
|
233
233
|
expect(client).to receive(:send_recv).with(query_dir_req, encrypt: true)
|
234
234
|
tree.list
|
235
235
|
end
|
@@ -437,7 +437,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
437
437
|
end
|
438
438
|
|
439
439
|
it 'calls Client #send_recv with encryption set if required' do
|
440
|
-
tree.
|
440
|
+
tree.tree_connect_encrypt_data = true
|
441
441
|
expect(client).to receive(:send_recv).with(create_request, encrypt: true).and_return(create_response.to_binary_s)
|
442
442
|
tree.open_file(filename: filename)
|
443
443
|
end
|
@@ -459,7 +459,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
459
459
|
context 'when encryption is required' do
|
460
460
|
it 'returns the expected RubySMB::SMB2::File object' do
|
461
461
|
file_obj = RubySMB::SMB2::File.new(name: filename, tree: tree, response: create_response, encrypt: true)
|
462
|
-
tree.
|
462
|
+
tree.tree_connect_encrypt_data = true
|
463
463
|
expect(RubySMB::SMB2::File).to receive(:new).with(name: filename, tree: tree, response: create_response, encrypt: true).and_return(file_obj)
|
464
464
|
expect(tree.open_file(filename: filename)).to eq(file_obj)
|
465
465
|
end
|
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: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -97,7 +97,7 @@ cert_chain:
|
|
97
97
|
JI/W23RbIRksG2pioMhd4dCXq3FLLlkOV1YfCwWixNB+iIhQPPZVaPNfgPhCn4Dt
|
98
98
|
DeGjje/qA4fkLtRmOtb9PUBq3ToRDE4=
|
99
99
|
-----END CERTIFICATE-----
|
100
|
-
date: 2020-06-
|
100
|
+
date: 2020-06-19 00:00:00.000000000 Z
|
101
101
|
dependencies:
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: redcarpet
|
metadata.gz.sig
CHANGED
Binary file
|