ruby_smb 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|