ruby_smb 1.0.5 → 2.0.3
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/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 +29 -0
- data/examples/enum_registry_values.rb +31 -0
- 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 +13 -13
- 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 +33 -0
- 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 -1
- data/lib/ruby_smb/client.rb +239 -21
- 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 +154 -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 +40 -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 +6 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +41 -9
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -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/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 +421 -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/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 +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 +40 -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/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 +28 -1
- data/lib/ruby_smb/field/stringz16.rb +17 -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 +8 -14
- 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 +81 -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 +51 -61
- 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 +2 -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 +80 -3
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +32 -20
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1583 -102
- 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 +1729 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -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/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/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/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 +104 -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 +95 -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 +35 -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 +138 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- 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 +884 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
- data/spec/lib/ruby_smb/error_spec.rb +59 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
- 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 +216 -147
- 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 +146 -68
- 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 +3 -24
- 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 +226 -148
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
- metadata +257 -81
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
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]
|
@@ -145,9 +160,16 @@ module RubySMB
|
|
145
160
|
|
146
161
|
# The UID set in SMB1
|
147
162
|
# @!attribute [rw] user_id
|
148
|
-
# @return [
|
163
|
+
# @return [Integer]
|
149
164
|
attr_accessor :user_id
|
150
165
|
|
166
|
+
# The Process ID set in SMB1
|
167
|
+
# It is randomly generated during the client initialization, but can
|
168
|
+
# be modified using the accessor.
|
169
|
+
# @!attribute [rw] pid
|
170
|
+
# @return [Integer]
|
171
|
+
attr_accessor :pid
|
172
|
+
|
151
173
|
# The maximum size SMB message that the Client accepts (in bytes)
|
152
174
|
# The default value is equal to {MAX_BUFFER_SIZE}.
|
153
175
|
# @!attribute [rw] max_buffer_size
|
@@ -176,15 +198,92 @@ module RubySMB
|
|
176
198
|
# @return [Integer]
|
177
199
|
attr_accessor :server_max_transact_size
|
178
200
|
|
201
|
+
# The algorithm to compute the preauthentication integrity hash (SMB 3.1.1).
|
202
|
+
# @!attribute [rw] preauth_integrity_hash_algorithm
|
203
|
+
# @return [String]
|
204
|
+
attr_accessor :preauth_integrity_hash_algorithm
|
205
|
+
|
206
|
+
# The preauthentication integrity hash value (SMB 3.1.1).
|
207
|
+
# @!attribute [rw] preauth_integrity_hash_value
|
208
|
+
# @return [String]
|
209
|
+
attr_accessor :preauth_integrity_hash_value
|
210
|
+
|
211
|
+
# The algorithm for encryption (SMB 3.x).
|
212
|
+
# @!attribute [rw] encryption_algorithm
|
213
|
+
# @return [String]
|
214
|
+
attr_accessor :encryption_algorithm
|
215
|
+
|
216
|
+
# The client encryption key (SMB 3.x).
|
217
|
+
# @!attribute [rw] client_encryption_key
|
218
|
+
# @return [String]
|
219
|
+
attr_accessor :client_encryption_key
|
220
|
+
|
221
|
+
# The server encryption key (SMB 3.x).
|
222
|
+
# @!attribute [rw] server_encryption_key
|
223
|
+
# @return [String]
|
224
|
+
attr_accessor :server_encryption_key
|
225
|
+
|
226
|
+
# Whether or not the whole session needs to be encrypted (SMB 3.x)
|
227
|
+
# @!attribute [rw] session_encrypt_data
|
228
|
+
# @return [Boolean]
|
229
|
+
attr_accessor :session_encrypt_data
|
230
|
+
|
231
|
+
# The encryption algorithms supported by the server (SMB 3.x).
|
232
|
+
# @!attribute [rw] server_encryption_algorithms
|
233
|
+
# @return [Array<Integer>] list of supported encryption algorithms
|
234
|
+
# (constants defined in RubySMB::SMB2::EncryptionCapabilities)
|
235
|
+
attr_accessor :server_encryption_algorithms
|
236
|
+
|
237
|
+
# The compression algorithms supported by the server (SMB 3.x).
|
238
|
+
# @!attribute [rw] server_compression_algorithms
|
239
|
+
# @return [Array<Integer>] list of supported compression algorithms
|
240
|
+
# (constants defined in RubySMB::SMB2::CompressionCapabilities)
|
241
|
+
attr_accessor :server_compression_algorithms
|
242
|
+
|
243
|
+
# The GUID of the server (SMB 2.x and 3.x).
|
244
|
+
# @!attribute [rw] server_guid
|
245
|
+
# @return [String]
|
246
|
+
attr_accessor :server_guid
|
247
|
+
|
248
|
+
# The server's start time if it is reported as part of the negotiation
|
249
|
+
# process (SMB 2.x and 3.x). This value is nil if the server does not report
|
250
|
+
# it (reports a value of 0).
|
251
|
+
# @!attribute [rw] server_start_time
|
252
|
+
# @return [Time] the time that the server reports that it was started at
|
253
|
+
attr_accessor :server_start_time
|
254
|
+
|
255
|
+
# The server's current time if it is reported as part of the negotiation
|
256
|
+
# process (SMB 2.x and 3.x). This value is nil if the server does not report
|
257
|
+
# it (reports a value of 0).
|
258
|
+
# @!attribute [rw] server_system_time
|
259
|
+
# @return [Time] the time that the server reports as current
|
260
|
+
attr_accessor :server_system_time
|
261
|
+
|
262
|
+
# The SMB version that has been successfully negotiated. This value is only
|
263
|
+
# set after the NEGOTIATE handshake has been performed.
|
264
|
+
# @!attribute [rw] negotiated_smb_version
|
265
|
+
# @return [Integer] the negotiated SMB version
|
266
|
+
attr_accessor :negotiated_smb_version
|
267
|
+
|
268
|
+
# Whether or not the server supports multi-credit operations. It is
|
269
|
+
# reported by the LARGE_MTU capabiliy as part of the negotiation process
|
270
|
+
# (SMB 2.x and 3.x).
|
271
|
+
# @!attribute [rw] server_supports_multi_credit
|
272
|
+
# @return [Boolean] true if the server supports multi-credit operations,
|
273
|
+
# false otherwise
|
274
|
+
attr_accessor :server_supports_multi_credit
|
275
|
+
|
179
276
|
# @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
|
180
277
|
# @param smb1 [Boolean] whether or not to enable SMB1 support
|
181
278
|
# @param smb2 [Boolean] whether or not to enable SMB2 support
|
182
|
-
|
279
|
+
# @param smb3 [Boolean] whether or not to enable SMB3 support
|
280
|
+
def initialize(dispatcher, smb1: true, smb2: true, smb3: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION', always_encrypt: true)
|
183
281
|
raise ArgumentError, 'No Dispatcher provided' unless dispatcher.is_a? RubySMB::Dispatcher::Base
|
184
|
-
if smb1 == false && smb2 == false
|
282
|
+
if smb1 == false && smb2 == false && smb3 == false
|
185
283
|
raise ArgumentError, 'You must enable at least one Protocol'
|
186
284
|
end
|
187
285
|
@dispatcher = dispatcher
|
286
|
+
@pid = rand(0xFFFF)
|
188
287
|
@domain = domain
|
189
288
|
@local_workstation = local_workstation
|
190
289
|
@password = password.encode('utf-8') || ''.encode('utf-8')
|
@@ -194,6 +293,7 @@ module RubySMB
|
|
194
293
|
@signing_required = false
|
195
294
|
@smb1 = smb1
|
196
295
|
@smb2 = smb2
|
296
|
+
@smb3 = smb3
|
197
297
|
@username = username.encode('utf-8') || ''.encode('utf-8')
|
198
298
|
@max_buffer_size = MAX_BUFFER_SIZE
|
199
299
|
# These sizes will be modifed during negotiation
|
@@ -201,11 +301,16 @@ module RubySMB
|
|
201
301
|
@server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
202
302
|
@server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
203
303
|
@server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
|
304
|
+
@server_supports_multi_credit = false
|
305
|
+
|
306
|
+
# SMB 3.x options
|
307
|
+
@session_encrypt_data = always_encrypt
|
204
308
|
|
205
309
|
negotiate_version_flag = 0x02000000
|
206
310
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
207
311
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
208
|
-
negotiate_version_flag
|
312
|
+
negotiate_version_flag ^
|
313
|
+
Net::NTLM::FLAGS[:OEM]
|
209
314
|
|
210
315
|
@ntlm_client = Net::NTLM::Client.new(
|
211
316
|
@username,
|
@@ -241,7 +346,7 @@ module RubySMB
|
|
241
346
|
# @param data [String] the data the server should echo back (ignored in SMB2)
|
242
347
|
# @return [WindowsError::ErrorCode] the NTStatus of the last response received
|
243
348
|
def echo(count: 1, data: '')
|
244
|
-
response = if smb2
|
349
|
+
response = if smb2 || smb3
|
245
350
|
smb2_echo
|
246
351
|
else
|
247
352
|
smb1_echo(count: count, data: data)
|
@@ -256,10 +361,8 @@ module RubySMB
|
|
256
361
|
# @param packet [RubySMB::GenericPacket] the packet to set the message id for
|
257
362
|
# @return [RubySMB::GenericPacket] the modified packet
|
258
363
|
def increment_smb_message_id(packet)
|
259
|
-
|
260
|
-
|
261
|
-
self.smb2_message_id += 1
|
262
|
-
end
|
364
|
+
packet.smb2_header.message_id = self.smb2_message_id
|
365
|
+
self.smb2_message_id += 1
|
263
366
|
packet
|
264
367
|
end
|
265
368
|
|
@@ -281,7 +384,8 @@ module RubySMB
|
|
281
384
|
negotiate_version_flag = 0x02000000
|
282
385
|
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
283
386
|
Net::NTLM::FLAGS[:TARGET_INFO] |
|
284
|
-
negotiate_version_flag
|
387
|
+
negotiate_version_flag ^
|
388
|
+
Net::NTLM::FLAGS[:OEM]
|
285
389
|
|
286
390
|
@ntlm_client = Net::NTLM::Client.new(
|
287
391
|
@username,
|
@@ -299,7 +403,7 @@ module RubySMB
|
|
299
403
|
# @return [WindowsError::ErrorCode] the NTStatus of the response
|
300
404
|
# @raise [RubySMB::Error::InvalidPacket] if the response packet is not a LogoffResponse packet
|
301
405
|
def logoff!
|
302
|
-
if smb2
|
406
|
+
if smb2 || smb3
|
303
407
|
request = RubySMB::SMB2::Packet::LogoffRequest.new
|
304
408
|
raw_response = send_recv(request)
|
305
409
|
response = RubySMB::SMB2::Packet::LogoffResponse.read(raw_response)
|
@@ -332,25 +436,125 @@ module RubySMB
|
|
332
436
|
# It will also sign the packet if neccessary.
|
333
437
|
#
|
334
438
|
# @param packet [RubySMB::GenericPacket] the request to be sent
|
439
|
+
# @param encrypt [Boolean] true if encryption has to be enabled for this transaction
|
440
|
+
# (note that if @session_encrypt_data is set, encryption will be enabled
|
441
|
+
# regardless of this parameter value)
|
335
442
|
# @return [String] the raw response data received
|
336
|
-
def send_recv(packet)
|
337
|
-
|
443
|
+
def send_recv(packet, encrypt: false)
|
444
|
+
version = packet.packet_smb_version
|
445
|
+
case version
|
338
446
|
when 'SMB1'
|
339
|
-
packet.smb_header.uid = user_id if user_id
|
447
|
+
packet.smb_header.uid = self.user_id if self.user_id
|
448
|
+
packet.smb_header.pid_low = self.pid if self.pid
|
340
449
|
packet = smb1_sign(packet)
|
341
450
|
when 'SMB2'
|
342
451
|
packet = increment_smb_message_id(packet)
|
343
452
|
packet.smb2_header.session_id = session_id
|
344
453
|
unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)
|
345
|
-
|
454
|
+
if self.smb2
|
455
|
+
packet = smb2_sign(packet)
|
456
|
+
elsif self.smb3
|
457
|
+
packet = smb3_sign(packet)
|
458
|
+
end
|
346
459
|
end
|
347
460
|
else
|
348
461
|
packet = packet
|
349
462
|
end
|
350
|
-
|
351
|
-
|
463
|
+
|
464
|
+
encrypt_data = false
|
465
|
+
if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
|
466
|
+
encrypt_data = true
|
467
|
+
end
|
468
|
+
send_packet(packet, encrypt: encrypt_data)
|
469
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
470
|
+
smb2_header = nil
|
471
|
+
loop do
|
472
|
+
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
|
473
|
+
break unless is_status_pending?(smb2_header)
|
474
|
+
sleep 1
|
475
|
+
raw_response = recv_packet(encrypt: encrypt_data)
|
476
|
+
end unless version == 'SMB1'
|
352
477
|
|
353
478
|
self.sequence_counter += 1 if signing_required && !session_key.empty?
|
479
|
+
# update the SMB2 message ID according to the received Credit Charged
|
480
|
+
self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.dialect != '0x0202'
|
481
|
+
raw_response
|
482
|
+
end
|
483
|
+
|
484
|
+
# Check if the response is an asynchronous operation with STATUS_PENDING
|
485
|
+
# status code.
|
486
|
+
#
|
487
|
+
# @param smb2_header [String] the response packet SMB2 header
|
488
|
+
# @return [Boolean] true if it is a status pending operation, false otherwise
|
489
|
+
def is_status_pending?(smb2_header)
|
490
|
+
value = smb2_header.nt_status.value
|
491
|
+
status_code = WindowsError::NTStatus.find_by_retval(value).first
|
492
|
+
status_code == WindowsError::NTStatus::STATUS_PENDING &&
|
493
|
+
smb2_header.flags.async_command == 1
|
494
|
+
end
|
495
|
+
|
496
|
+
# Check if the request packet can be encrypted. Per the SMB2 spec,
|
497
|
+
# SessionSetupRequest and NegotiateRequest must not be encrypted.
|
498
|
+
#
|
499
|
+
# @param packet [RubySMB::GenericPacket] the request packet
|
500
|
+
# @return [Boolean] true if the packet can be encrypted
|
501
|
+
def can_be_encrypted?(packet)
|
502
|
+
return false if packet.packet_smb_version == 'SMB1'
|
503
|
+
[RubySMB::SMB2::Packet::SessionSetupRequest, RubySMB::SMB2::Packet::NegotiateRequest].none? do |klass|
|
504
|
+
packet.is_a?(klass)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
# Check if the current dialect supports encryption.
|
509
|
+
#
|
510
|
+
# @return [Boolean] true if encryption is supported
|
511
|
+
def encryption_supported?
|
512
|
+
['0x0300', '0x0302', '0x0311'].include?(@dialect)
|
513
|
+
end
|
514
|
+
|
515
|
+
# Encrypt (if required) and send a packet.
|
516
|
+
#
|
517
|
+
# @param encrypt [Boolean] true if the packet should be encrypted, false
|
518
|
+
# otherwise
|
519
|
+
def send_packet(packet, encrypt: false)
|
520
|
+
if encrypt
|
521
|
+
begin
|
522
|
+
packet = smb3_encrypt(packet.to_binary_s)
|
523
|
+
rescue RubySMB::Error::RubySMBError => e
|
524
|
+
raise RubySMB::Error::EncryptionError, "Error while encrypting #{packet.class.name} packet (SMB #{@dialect}): #{e}"
|
525
|
+
end
|
526
|
+
end
|
527
|
+
dispatcher.send_packet(packet)
|
528
|
+
end
|
529
|
+
|
530
|
+
# Receives the raw response through the Dispatcher and decrypt the packet (if required).
|
531
|
+
#
|
532
|
+
# @param encrypt [Boolean] true if the packet is encrypted, false otherwise
|
533
|
+
# @return [String] the raw unencrypted packet
|
534
|
+
def recv_packet(encrypt: false)
|
535
|
+
begin
|
536
|
+
raw_response = dispatcher.recv_packet
|
537
|
+
rescue RubySMB::Error::CommunicationError => e
|
538
|
+
if encrypt
|
539
|
+
raise RubySMB::Error::EncryptionError, "Communication error with the "\
|
540
|
+
"remote host: #{e.message}. The server supports encryption but was "\
|
541
|
+
"not able to handle the encrypted request."
|
542
|
+
else
|
543
|
+
raise e
|
544
|
+
end
|
545
|
+
end
|
546
|
+
if encrypt
|
547
|
+
begin
|
548
|
+
transform_response = RubySMB::SMB2::Packet::TransformHeader.read(raw_response)
|
549
|
+
rescue IOError
|
550
|
+
raise RubySMB::Error::InvalidPacket, 'Not a SMB2 TransformHeader packet'
|
551
|
+
end
|
552
|
+
begin
|
553
|
+
raw_response = smb3_decrypt(transform_response)
|
554
|
+
rescue RubySMB::Error::RubySMBError => e
|
555
|
+
raise RubySMB::Error::EncryptionError, "Error while decrypting #{transform_response.class.name} packet (SMB #@dialect}): #{e}"
|
556
|
+
end
|
557
|
+
end
|
354
558
|
raw_response
|
355
559
|
end
|
356
560
|
|
@@ -360,7 +564,7 @@ module RubySMB
|
|
360
564
|
# @return [RubySMB::SMB1::Tree] if talking over SMB1
|
361
565
|
# @return [RubySMB::SMB2::Tree] if talking over SMB2
|
362
566
|
def tree_connect(share)
|
363
|
-
connected_tree = if smb2
|
567
|
+
connected_tree = if smb2 || smb3
|
364
568
|
smb2_tree_connect(share)
|
365
569
|
else
|
366
570
|
smb1_tree_connect(share)
|
@@ -390,6 +594,9 @@ module RubySMB
|
|
390
594
|
self.session_key = ''
|
391
595
|
self.sequence_counter = 0
|
392
596
|
self.smb2_message_id = 0
|
597
|
+
self.client_encryption_key = nil
|
598
|
+
self.server_encryption_key = nil
|
599
|
+
self.server_supports_multi_credit = false
|
393
600
|
end
|
394
601
|
|
395
602
|
# Requests a NetBIOS Session Service using the provided name.
|
@@ -427,10 +634,21 @@ module RubySMB
|
|
427
634
|
session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST
|
428
635
|
session_request.called_name = called_name
|
429
636
|
session_request.calling_name = calling_name
|
430
|
-
session_request.session_header.
|
637
|
+
session_request.session_header.stream_protocol_length =
|
431
638
|
session_request.num_bytes - session_request.session_header.num_bytes
|
432
639
|
session_request
|
433
640
|
end
|
434
641
|
|
642
|
+
def update_preauth_hash(data)
|
643
|
+
unless @preauth_integrity_hash_algorithm
|
644
|
+
raise RubySMB::Error::EncryptionError.new(
|
645
|
+
'Cannot compute the Preauth Integrity Hash value: Preauth Integrity Hash Algorithm is nil'
|
646
|
+
)
|
647
|
+
end
|
648
|
+
@preauth_integrity_hash_value = OpenSSL::Digest.digest(
|
649
|
+
@preauth_integrity_hash_algorithm,
|
650
|
+
@preauth_integrity_hash_value + data.to_binary_s
|
651
|
+
)
|
652
|
+
end
|
435
653
|
end
|
436
654
|
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
|
|