ruby_smb 1.1.0 → 2.0.4
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +3 -5
- data/Gemfile +6 -2
- 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.rb +51 -8
- 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_file_encryption.rb +56 -0
- 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.rb +4 -0
- data/lib/ruby_smb/client.rb +246 -26
- data/lib/ruby_smb/client/authentication.rb +32 -18
- data/lib/ruby_smb/client/echo.rb +2 -4
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +156 -16
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +6 -8
- data/lib/ruby_smb/client/utils.rb +24 -17
- data/lib/ruby_smb/client/winreg.rb +1 -1
- data/lib/ruby_smb/crypto.rb +30 -0
- data/lib/ruby_smb/dcerpc.rb +2 -0
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +209 -44
- data/lib/ruby_smb/dcerpc/request.rb +13 -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 +5 -4
- data/lib/ruby_smb/error.rb +49 -6
- 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/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +13 -28
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
- data/lib/ruby_smb/smb1/pipe.rb +8 -8
- data/lib/ruby_smb/smb1/tree.rb +25 -12
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
- data/lib/ruby_smb/smb2/file.rb +59 -77
- data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
- data/lib/ruby_smb/smb2/pipe.rb +8 -20
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +44 -28
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +3 -1
- data/spec/lib/ruby_smb/client_spec.rb +1408 -70
- data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
- 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 +88 -0
- 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/packet/session_setup_legacy_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
- 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/bit_field/session_flags_spec.rb +9 -0
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +147 -71
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
- data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -45
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +111 -9
- metadata +194 -75
- metadata.gz.sig +2 -1
@@ -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,14 +166,13 @@ 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
|
|
176
173
|
status_code = packet.status_code
|
177
174
|
unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
|
178
|
-
raise RubySMB::Error::UnexpectedStatusCode, status_code
|
175
|
+
raise RubySMB::Error::UnexpectedStatusCode, status_code
|
179
176
|
end
|
180
177
|
|
181
178
|
packet
|
@@ -201,6 +198,9 @@ module RubySMB
|
|
201
198
|
def smb2_authenticate
|
202
199
|
response = smb2_ntlmssp_negotiate
|
203
200
|
challenge_packet = smb2_ntlmssp_challenge_packet(response)
|
201
|
+
if @dialect == '0x0311'
|
202
|
+
update_preauth_hash(challenge_packet)
|
203
|
+
end
|
204
204
|
@session_id = challenge_packet.smb2_header.session_id
|
205
205
|
type2_b64_message = smb2_type2_message(challenge_packet)
|
206
206
|
type3_message = @ntlm_client.init_context(type2_b64_message)
|
@@ -213,6 +213,16 @@ module RubySMB
|
|
213
213
|
raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
|
214
214
|
response = smb2_ntlmssp_final_packet(raw)
|
215
215
|
|
216
|
+
if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
|
217
|
+
@session_encrypt_data = true
|
218
|
+
end
|
219
|
+
######
|
220
|
+
# DEBUG
|
221
|
+
#puts "Session ID = #{@session_id.to_binary_s.each_byte.map {|e| '%02x' % e}.join}"
|
222
|
+
#puts "Session key = #{@session_key.each_byte.map {|e| '%02x' % e}.join}"
|
223
|
+
#puts "PreAuthHash = #{@preauth_integrity_hash_value.each_byte.map {|e| '%02x' % e}.join}" if @preauth_integrity_hash_value
|
224
|
+
######
|
225
|
+
|
216
226
|
response.status_code
|
217
227
|
end
|
218
228
|
|
@@ -223,8 +233,7 @@ module RubySMB
|
|
223
233
|
raise RubySMB::Error::InvalidPacket.new(
|
224
234
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
225
235
|
expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
|
226
|
-
|
227
|
-
received_cmd: packet.smb2_header.command
|
236
|
+
packet: packet
|
228
237
|
)
|
229
238
|
end
|
230
239
|
|
@@ -238,14 +247,13 @@ module RubySMB
|
|
238
247
|
raise RubySMB::Error::InvalidPacket.new(
|
239
248
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
240
249
|
expected_cmd: RubySMB::SMB2::Packet::SessionSetupResponse::COMMAND,
|
241
|
-
|
242
|
-
received_cmd: packet.smb2_header.command
|
250
|
+
packet: packet
|
243
251
|
)
|
244
252
|
end
|
245
253
|
|
246
254
|
status_code = packet.status_code
|
247
255
|
unless status_code.name == 'STATUS_MORE_PROCESSING_REQUIRED'
|
248
|
-
raise RubySMB::Error::UnexpectedStatusCode, status_code
|
256
|
+
raise RubySMB::Error::UnexpectedStatusCode, status_code
|
249
257
|
end
|
250
258
|
packet
|
251
259
|
end
|
@@ -256,7 +264,11 @@ module RubySMB
|
|
256
264
|
# @return [String] the binary string response from the server
|
257
265
|
def smb2_ntlmssp_negotiate
|
258
266
|
packet = smb2_ntlmssp_negotiate_packet
|
259
|
-
send_recv(packet)
|
267
|
+
response = send_recv(packet)
|
268
|
+
if @dialect == '0x0311'
|
269
|
+
update_preauth_hash(packet)
|
270
|
+
end
|
271
|
+
response
|
260
272
|
end
|
261
273
|
|
262
274
|
# Creates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
|
@@ -268,10 +280,7 @@ module RubySMB
|
|
268
280
|
type1_message = ntlm_client.init_context
|
269
281
|
packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
|
270
282
|
packet.set_type1_blob(type1_message.serialize)
|
271
|
-
|
272
|
-
# the Message ID can be out of sync at this point so we re-synch it here.
|
273
|
-
packet.smb2_header.message_id = 1
|
274
|
-
self.smb2_message_id = 2
|
283
|
+
packet.security_mode.signing_enabled = 1
|
275
284
|
packet
|
276
285
|
end
|
277
286
|
|
@@ -294,7 +303,11 @@ module RubySMB
|
|
294
303
|
# @return [String] the raw binary response from the server
|
295
304
|
def smb2_ntlmssp_authenticate(type3_message, user_id)
|
296
305
|
packet = smb2_ntlmssp_auth_packet(type3_message, user_id)
|
297
|
-
send_recv(packet)
|
306
|
+
response = send_recv(packet)
|
307
|
+
if @dialect == '0x0311'
|
308
|
+
update_preauth_hash(packet)
|
309
|
+
end
|
310
|
+
response
|
298
311
|
end
|
299
312
|
|
300
313
|
# Generates the {RubySMB::SMB2::Packet::SessionSetupRequest} packet
|
@@ -307,6 +320,7 @@ module RubySMB
|
|
307
320
|
packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
|
308
321
|
packet.smb2_header.session_id = session_id
|
309
322
|
packet.set_type3_blob(type3_message.serialize)
|
323
|
+
packet.security_mode.signing_enabled = 1
|
310
324
|
packet
|
311
325
|
end
|
312
326
|
|
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
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RubySMB
|
2
|
+
class Client
|
3
|
+
# Contains the methods for handling encryption / decryption
|
4
|
+
module Encryption
|
5
|
+
def smb3_encrypt(data)
|
6
|
+
unless @client_encryption_key
|
7
|
+
case @dialect
|
8
|
+
when '0x0300', '0x0302'
|
9
|
+
@client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
10
|
+
@session_key,
|
11
|
+
"SMB2AESCCM\x00",
|
12
|
+
"ServerIn \x00"
|
13
|
+
)
|
14
|
+
when '0x0311'
|
15
|
+
@client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
16
|
+
@session_key,
|
17
|
+
"SMBC2SCipherKey\x00",
|
18
|
+
@preauth_integrity_hash_value
|
19
|
+
)
|
20
|
+
else
|
21
|
+
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 encryption')
|
22
|
+
end
|
23
|
+
######
|
24
|
+
# DEBUG
|
25
|
+
#puts "Client encryption key = #{@client_encryption_key.each_byte.map {|e| '%02x' % e}.join}"
|
26
|
+
######
|
27
|
+
end
|
28
|
+
|
29
|
+
th = RubySMB::SMB2::Packet::TransformHeader.new(flags: 1, session_id: @session_id)
|
30
|
+
th.encrypt(data, @client_encryption_key, algorithm: @encryption_algorithm)
|
31
|
+
th
|
32
|
+
end
|
33
|
+
|
34
|
+
def smb3_decrypt(th)
|
35
|
+
unless @server_encryption_key
|
36
|
+
case @dialect
|
37
|
+
when '0x0300', '0x0302'
|
38
|
+
@server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
39
|
+
@session_key,
|
40
|
+
"SMB2AESCCM\x00",
|
41
|
+
"ServerOut\x00"
|
42
|
+
)
|
43
|
+
when '0x0311'
|
44
|
+
@server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
|
45
|
+
@session_key,
|
46
|
+
"SMBS2CCipherKey\x00",
|
47
|
+
@preauth_integrity_hash_value
|
48
|
+
)
|
49
|
+
else
|
50
|
+
raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 decryption')
|
51
|
+
end
|
52
|
+
######
|
53
|
+
# DEBUG
|
54
|
+
#puts "Server encryption key = #{@server_encryption_key.each_byte.map {|e| '%02x' % e}.join}"
|
55
|
+
######
|
56
|
+
end
|
57
|
+
|
58
|
+
th.decrypt(@server_encryption_key, algorithm: @encryption_algorithm)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -17,10 +17,28 @@ module RubySMB
|
|
17
17
|
# internally to be able to retrieve the negotiated dialect later on.
|
18
18
|
# This is only valid for SMB1.
|
19
19
|
response_packet.dialects = request_packet.dialects if response_packet.respond_to? :dialects=
|
20
|
-
parse_negotiate_response(response_packet)
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
version = parse_negotiate_response(response_packet)
|
21
|
+
if @dialect == '0x0311'
|
22
|
+
update_preauth_hash(request_packet)
|
23
|
+
update_preauth_hash(response_packet)
|
24
|
+
end
|
25
|
+
|
26
|
+
# If the response contains the SMB2 wildcard revision number dialect;
|
27
|
+
# it indicates that the server implements SMB 2.1 or future dialect
|
28
|
+
# revisions and expects the client to send a subsequent SMB2 Negotiate
|
29
|
+
# request to negotiate the actual SMB 2 Protocol revision to be used.
|
30
|
+
# The wildcard revision number is sent only in response to a
|
31
|
+
# multi-protocol negotiate request with the "SMB 2.???" dialect string.
|
32
|
+
if @dialect == '0x02ff'
|
33
|
+
self.smb2_message_id += 1
|
34
|
+
version = negotiate
|
35
|
+
end
|
36
|
+
version
|
37
|
+
rescue RubySMB::Error::InvalidPacket, Errno::ECONNRESET, RubySMB::Error::CommunicationError => e
|
38
|
+
version = request_packet.packet_smb_version
|
39
|
+
version = 'SMB3' if version == 'SMB2' && !@smb2 && @smb3
|
40
|
+
version = 'SMB2 or SMB3' if version == 'SMB2' && @smb2 && @smb3
|
41
|
+
error = "Unable to negotiate #{version} with the remote host: #{e.message}"
|
24
42
|
raise RubySMB::Error::NegotiationFailure, error
|
25
43
|
end
|
26
44
|
|
@@ -32,8 +50,8 @@ module RubySMB
|
|
32
50
|
def negotiate_request
|
33
51
|
if smb1
|
34
52
|
smb1_negotiate_request
|
35
|
-
|
36
|
-
|
53
|
+
else
|
54
|
+
smb2_3_negotiate_request
|
37
55
|
end
|
38
56
|
end
|
39
57
|
|
@@ -50,7 +68,7 @@ module RubySMB
|
|
50
68
|
packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.read raw_data
|
51
69
|
response = packet if packet.valid?
|
52
70
|
end
|
53
|
-
if smb2 && response.nil?
|
71
|
+
if (smb2 || smb3) && response.nil?
|
54
72
|
packet = RubySMB::SMB2::Packet::NegotiateResponse.read raw_data
|
55
73
|
response = packet if packet.valid?
|
56
74
|
end
|
@@ -65,16 +83,14 @@ module RubySMB
|
|
65
83
|
expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
|
66
84
|
expected_cmd: RubySMB::SMB1::Packet::NegotiateResponseExtended::COMMAND,
|
67
85
|
expected_custom: "extended_security=1",
|
68
|
-
|
69
|
-
received_cmd: packet.smb_header.command,
|
86
|
+
packet: packet,
|
70
87
|
received_custom: "extended_security=#{extended_security}"
|
71
88
|
)
|
72
89
|
elsif packet.packet_smb_version == 'SMB2'
|
73
90
|
raise RubySMB::Error::InvalidPacket.new(
|
74
91
|
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
75
92
|
expected_cmd: RubySMB::SMB2::Packet::NegotiateResponse::COMMAND,
|
76
|
-
|
77
|
-
received_cmd: packet.smb2_header.command
|
93
|
+
packet: packet
|
78
94
|
)
|
79
95
|
else
|
80
96
|
raise RubySMB::Error::InvalidPacket, 'Unknown SMB protocol version'
|
@@ -95,26 +111,94 @@ module RubySMB
|
|
95
111
|
when RubySMB::SMB1::Packet::NegotiateResponseExtended
|
96
112
|
self.smb1 = true
|
97
113
|
self.smb2 = false
|
114
|
+
self.smb3 = false
|
98
115
|
self.signing_required = packet.parameter_block.security_mode.security_signatures_required == 1
|
99
116
|
self.dialect = packet.negotiated_dialect.to_s
|
100
117
|
# MaxBufferSize is largest message server will receive, measured from start of the SMB header. Subtract 260
|
101
118
|
# for protocol overhead. Then this value can be used for max read/write size without having to factor in
|
102
119
|
# protocol overhead every time.
|
103
120
|
self.server_max_buffer_size = packet.parameter_block.max_buffer_size - 260
|
121
|
+
self.negotiated_smb_version = 1
|
122
|
+
self.session_encrypt_data = false
|
104
123
|
'SMB1'
|
105
124
|
when RubySMB::SMB2::Packet::NegotiateResponse
|
106
125
|
self.smb1 = false
|
107
|
-
|
108
|
-
|
126
|
+
unless packet.dialect_revision.to_i == 0x02ff
|
127
|
+
self.smb2 = packet.dialect_revision.to_i >= 0x0200 && packet.dialect_revision.to_i < 0x0300
|
128
|
+
self.smb3 = packet.dialect_revision.to_i >= 0x0300 && packet.dialect_revision.to_i < 0x0400
|
129
|
+
end
|
130
|
+
self.signing_required = packet.security_mode.signing_required == 1 if self.smb2 || self.smb3
|
109
131
|
self.dialect = "0x%04x" % packet.dialect_revision
|
110
132
|
self.server_max_read_size = packet.max_read_size
|
111
133
|
self.server_max_write_size = packet.max_write_size
|
112
134
|
self.server_max_transact_size = packet.max_transact_size
|
113
135
|
# This value is used in SMB1 only but calculate a valid value anyway
|
114
136
|
self.server_max_buffer_size = [self.server_max_read_size, self.server_max_write_size, self.server_max_transact_size].min
|
115
|
-
|
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
|
155
|
+
return "SMB#{self.negotiated_smb_version}"
|
156
|
+
else
|
157
|
+
error = 'Unable to negotiate with remote host'
|
158
|
+
if packet.status_code == WindowsError::NTStatus::STATUS_NOT_SUPPORTED
|
159
|
+
error << ", SMB2" if @smb2
|
160
|
+
error << ", SMB3" if @smb3
|
161
|
+
error << ' not supported'
|
162
|
+
end
|
163
|
+
raise RubySMB::Error::NegotiationFailure, error
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_smb3_capabilities(response_packet)
|
168
|
+
nc = response_packet.find_negotiate_context(
|
169
|
+
RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
170
|
+
)
|
171
|
+
@preauth_integrity_hash_algorithm = RubySMB::SMB2::PreauthIntegrityCapabilities::HASH_ALGORITM_MAP[nc&.data&.hash_algorithms&.first]
|
172
|
+
unless @preauth_integrity_hash_algorithm
|
173
|
+
raise RubySMB::Error::EncryptionError.new(
|
174
|
+
'Unable to retrieve the Preauth Integrity Hash Algorithm from the Negotiate response'
|
175
|
+
)
|
176
|
+
end
|
177
|
+
# Set the encryption the client will use, prioritizing AES_128_GCM over AES_128_CCM
|
178
|
+
nc = response_packet.find_negotiate_context(
|
179
|
+
RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
180
|
+
)
|
181
|
+
@server_encryption_algorithms = nc&.data&.ciphers&.to_ary
|
182
|
+
if @server_encryption_algorithms.nil? || @server_encryption_algorithms.empty?
|
183
|
+
raise RubySMB::Error::EncryptionError.new(
|
184
|
+
'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
|
185
|
+
)
|
186
|
+
end
|
187
|
+
if @server_encryption_algorithms.include?(RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM)
|
188
|
+
@encryption_algorithm = RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM]
|
189
|
+
else
|
190
|
+
@encryption_algorithm = RubySMB::SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[@server_encryption_algorithms.first]
|
191
|
+
end
|
192
|
+
unless @encryption_algorithm
|
193
|
+
raise RubySMB::Error::EncryptionError.new(
|
194
|
+
'Unable to retrieve the encryption cipher list supported by the server from the Negotiate response'
|
195
|
+
)
|
116
196
|
end
|
117
197
|
|
198
|
+
nc = response_packet.find_negotiate_context(
|
199
|
+
RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
|
200
|
+
)
|
201
|
+
@server_compression_algorithms = nc&.data&.compression_algorithms&.to_ary || []
|
118
202
|
end
|
119
203
|
|
120
204
|
# Create a {RubySMB::SMB1::Packet::NegotiateRequest} packet with the
|
@@ -127,10 +211,17 @@ module RubySMB
|
|
127
211
|
# while being guaranteed to work with any modern Windows system. We can get more sophisticated
|
128
212
|
# with switching this on and off at a later date if the need arises.
|
129
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
|
130
220
|
# There is no real good reason to ever send an SMB1 Negotiate packet
|
131
221
|
# to Negotiate strictly SMB2, but the protocol WILL support it
|
132
222
|
packet.add_dialect(SMB1_DIALECT_SMB1_DEFAULT) if smb1
|
133
223
|
packet.add_dialect(SMB1_DIALECT_SMB2_DEFAULT) if smb2
|
224
|
+
packet.add_dialect(SMB1_DIALECT_SMB2_WILDCARD) if smb2 || smb3
|
134
225
|
packet
|
135
226
|
end
|
136
227
|
|
@@ -139,11 +230,60 @@ module RubySMB
|
|
139
230
|
# may want to communicate over SMB1
|
140
231
|
#
|
141
232
|
# @ return [RubySMB::SMB2::Packet::NegotiateRequest] a completed SMB2 Negotiate Request packet
|
142
|
-
def
|
233
|
+
def smb2_3_negotiate_request
|
143
234
|
packet = RubySMB::SMB2::Packet::NegotiateRequest.new
|
144
235
|
packet.security_mode.signing_enabled = 1
|
145
|
-
packet.add_dialect(SMB2_DIALECT_DEFAULT)
|
146
236
|
packet.client_guid = SecureRandom.random_bytes(16)
|
237
|
+
packet.set_dialects(SMB2_DIALECT_DEFAULT.map {|d| d.to_i(16)}) if smb2
|
238
|
+
packet = add_smb3_to_negotiate_request(packet) if smb3
|
239
|
+
packet
|
240
|
+
end
|
241
|
+
|
242
|
+
# This adds SMBv3 specific information: SMBv3 supported dialects,
|
243
|
+
# encryption capability, Negotiate Contexts if the dialect requires them
|
244
|
+
#
|
245
|
+
# @param packet [RubySMB::SMB2::Packet::NegotiateRequest] the NegotiateRequest
|
246
|
+
# to add SMB3 specific info to
|
247
|
+
# @param dialects [Array<String>] the dialects to negotiate. This must be
|
248
|
+
# an array of strings. Default is SMB3_DIALECT_DEFAULT
|
249
|
+
# @return [RubySMB::SMB2::Packet::NegotiateRequest] a completed SMB3 Negotiate Request packet
|
250
|
+
# @raise [ArgumentError] if dialects is not an array of strings
|
251
|
+
def add_smb3_to_negotiate_request(packet, dialects = SMB3_DIALECT_DEFAULT)
|
252
|
+
dialects.each do |dialect|
|
253
|
+
raise ArgumentError, 'Must be an array of strings' unless dialect.is_a? String
|
254
|
+
packet.add_dialect(dialect.to_i(16))
|
255
|
+
end
|
256
|
+
packet.capabilities.encryption = 1
|
257
|
+
|
258
|
+
if packet.dialects.include?(0x0311)
|
259
|
+
nc = RubySMB::SMB2::NegotiateContext.new(
|
260
|
+
context_type: RubySMB::SMB2::NegotiateContext::SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
261
|
+
)
|
262
|
+
nc.data.hash_algorithms << RubySMB::SMB2::PreauthIntegrityCapabilities::SHA_512
|
263
|
+
nc.data.salt = SecureRandom.random_bytes(32)
|
264
|
+
packet.add_negotiate_context(nc)
|
265
|
+
|
266
|
+
@preauth_integrity_hash_value = "\x00" * 64
|
267
|
+
nc = RubySMB::SMB2::NegotiateContext.new(
|
268
|
+
context_type: RubySMB::SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES
|
269
|
+
)
|
270
|
+
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_CCM
|
271
|
+
nc.data.ciphers << RubySMB::SMB2::EncryptionCapabilities::AES_128_GCM
|
272
|
+
packet.add_negotiate_context(nc)
|
273
|
+
|
274
|
+
nc = RubySMB::SMB2::NegotiateContext.new(
|
275
|
+
context_type: RubySMB::SMB2::NegotiateContext::SMB2_COMPRESSION_CAPABILITIES
|
276
|
+
)
|
277
|
+
# Adding all possible compression algorithm even if we don't support
|
278
|
+
# them yet. This will force the server to disclose the support
|
279
|
+
# algorithms in the repsonse.
|
280
|
+
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZNT1
|
281
|
+
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77
|
282
|
+
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::LZ77_Huffman
|
283
|
+
nc.data.compression_algorithms << RubySMB::SMB2::CompressionCapabilities::Pattern_V1
|
284
|
+
packet.add_negotiate_context(nc)
|
285
|
+
end
|
286
|
+
|
147
287
|
packet
|
148
288
|
end
|
149
289
|
end
|