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