ruby_smb 3.0.2 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a0b34939805bf96ca0b83284b946fc2d96c1e5d7b6e63c67c4d0c40b73d257d
4
- data.tar.gz: f8c1e25526de8821ec484c820b32734e0cfcefce6e34de2a90b25ce876225e61
3
+ metadata.gz: e7ce1d9892eb5ccf2881dd78a8e1983926554f69de301560539e7d7da37061b0
4
+ data.tar.gz: '01692d97a27c63f470b7c2899ad9cfb2828c9e254f0747176a29555d462bbf1d'
5
5
  SHA512:
6
- metadata.gz: b669ae3cdc6e7c875c92890ac15608ab45b8d223eb064756d340e046a5dd3b4273971ebdf8419a033ae87fc75af8f08d7e7ce5466186e2e531adada3efaedab4
7
- data.tar.gz: 052ad0693a7f9ea8b3b767a58ecc710e37f2c9c0ef7c910b8c385b22b9a6eee678596a49de385cb0d0200964e620b171bd667a76b7b393f96a1ebded64e6f5b7
6
+ metadata.gz: 2fafe3f08b518b285b33832e0809c9b5f4687800457bec2c1945dd33f4c7109466926a5f55a537a3cdf9851f1c0c6b9979aa0f8d75f0004749f8bdd56ba26ab4
7
+ data.tar.gz: 070550512fe2ffadaecb97ea014e8fa5563b722b0184303c022f901bb60466991979673adf0dedfb411585b910e92d435308f7d900326ee6d8aa8ead5e1cdf0c
checksums.yaml.gz.sig CHANGED
Binary file
@@ -4,14 +4,15 @@
4
4
  # including protocol negotiation and authentication.
5
5
 
6
6
  require 'bundler/setup'
7
+ require 'optparse'
7
8
  require 'ruby_smb'
8
9
 
9
- def run_authentication(address, smb1, smb2, smb3, username, password)
10
+ def run_authentication(address, smb1, smb2, smb3)
10
11
  # Create our socket and add it to the dispatcher
11
12
  sock = TCPSocket.new address, 445
12
13
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
13
14
 
14
- client = RubySMB::Client.new(dispatcher, smb1: smb1, smb2: smb2, smb3: smb3, username: username, password: password)
15
+ client = RubySMB::Client.new(dispatcher, smb1: smb1, smb2: smb2, smb3: smb3, username: '', password: '')
15
16
  protocol = client.negotiate
16
17
  status = client.authenticate
17
18
  puts "#{protocol} : #{status}"
@@ -22,9 +23,31 @@ def run_authentication(address, smb1, smb2, smb3, username, password)
22
23
  end
23
24
  end
24
25
 
25
- address = ARGV[0]
26
- username = ''
27
- password = ''
26
+ args = ARGV.dup
27
+ options = {
28
+ smbv1: true,
29
+ smbv2: true,
30
+ smbv3: true,
31
+ target: nil
32
+ }
33
+ options[:target ] = args.pop
34
+ optparser = OptionParser.new do |opts|
35
+ opts.banner = "Usage: #{File.basename(__FILE__)} [options] target"
36
+ opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
37
+ options[:smbv1] = smbv1
38
+ end
39
+ opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
40
+ options[:smbv2] = smbv2
41
+ end
42
+ opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
43
+ options[:smbv3] = smbv3
44
+ end
45
+ end
46
+ optparser.parse!(args)
47
+
48
+ if options[:target].nil?
49
+ abort(optparser.help)
50
+ end
28
51
 
29
52
  # Negotiate with only SMB1 enabled
30
- run_authentication(address, true, false, false, username, password)
53
+ run_authentication(options[:target], options[:smbv1], options[:smbv2], options[:smbv3])
@@ -15,13 +15,13 @@ options = {
15
15
  }
16
16
  OptionParser.new do |opts|
17
17
  opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
18
- opts.on("--[no-]smbv1", "Enabled or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
18
+ opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
19
19
  options[:smbv1] = smbv1
20
20
  end
21
- opts.on("--[no-]smbv2", "Enabled or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
21
+ opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
22
22
  options[:smbv2] = smbv2
23
23
  end
24
- opts.on("--[no-]smbv3", "Enabled or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
24
+ opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
25
25
  options[:smbv3] = smbv3
26
26
  end
27
27
  end.parse!
@@ -30,13 +30,13 @@ OptionParser.new do |opts|
30
30
  opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
31
31
  options[:allow_anonymous] = allow_anonymous
32
32
  end
33
- opts.on("--[no-]smbv1", "Enabled or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
33
+ opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
34
34
  options[:smbv1] = smbv1
35
35
  end
36
- opts.on("--[no-]smbv2", "Enabled or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
36
+ opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
37
37
  options[:smbv2] = smbv2
38
38
  end
39
- opts.on("--[no-]smbv3", "Enabled or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
39
+ opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
40
40
  options[:smbv3] = smbv3
41
41
  end
42
42
  opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
@@ -7,34 +7,75 @@
7
7
  # and read the file short.txt
8
8
 
9
9
  require 'bundler/setup'
10
+ require 'optparse'
10
11
  require 'ruby_smb'
11
12
 
12
- address = ARGV[0]
13
- username = ARGV[1]
14
- password = ARGV[2]
15
- share = ARGV[3]
16
- file = ARGV[4]
17
- smb_versions = ARGV[5]&.split(',') || ['1','2','3']
13
+ args = ARGV.dup
14
+ options = {
15
+ domain: '.',
16
+ username: '',
17
+ password: '',
18
+ share: nil,
19
+ smbv1: true,
20
+ smbv2: true,
21
+ smbv3: true,
22
+ target: nil
23
+ }
24
+ options[:file] = args.pop
25
+ options[:share] = args.pop
26
+ options[:target ] = args.pop
27
+ optparser = OptionParser.new do |opts|
28
+ opts.banner = "Usage: #{File.basename(__FILE__)} [options] target share file"
29
+ opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
30
+ options[:smbv1] = smbv1
31
+ end
32
+ opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
33
+ options[:smbv2] = smbv2
34
+ end
35
+ opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
36
+ options[:smbv3] = smbv3
37
+ end
38
+ opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
39
+ if username.include?('\\')
40
+ options[:domain], options[:username] = username.split('\\', 2)
41
+ else
42
+ options[:username] = username
43
+ end
44
+ end
45
+ opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
46
+ options[:password] = password
47
+ end
48
+ end
49
+ optparser.parse!(args)
50
+
51
+ if options[:target].nil? || options[:share].nil? || options[:file].nil?
52
+ abort(optparser.help)
53
+ end
18
54
 
19
- path = "\\\\#{address}\\#{share}"
55
+ path = "\\\\#{options[:target]}\\#{options[:share]}"
20
56
 
21
- sock = TCPSocket.new address, 445
57
+ sock = TCPSocket.new options[:target], 445
22
58
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
23
59
 
24
- client = RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password)
60
+ client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
25
61
  protocol = client.negotiate
26
62
  status = client.authenticate
27
63
 
28
64
  puts "#{protocol} : #{status}"
65
+ unless status == WindowsError::NTStatus::STATUS_SUCCESS
66
+ puts 'Authentication failed!'
67
+ exit(1)
68
+ end
29
69
 
30
70
  begin
31
71
  tree = client.tree_connect(path)
32
72
  puts "Connected to #{path} successfully!"
33
73
  rescue StandardError => e
34
74
  puts "Failed to connect to #{path}: #{e.message}"
75
+ exit(1)
35
76
  end
36
77
 
37
- file = tree.open_file(filename: file)
78
+ file = tree.open_file(filename: options[:file])
38
79
 
39
80
  data = file.read
40
81
  puts data
@@ -6,24 +6,64 @@
6
6
  # This will try to connect to \\192.168.172.138\TEST_SHARE with the msfadmin:msfadmin credentials
7
7
 
8
8
  require 'bundler/setup'
9
+ require 'optparse'
9
10
  require 'ruby_smb'
10
11
 
11
- address = ARGV[0]
12
- username = ARGV[1]
13
- password = ARGV[2]
14
- share = ARGV[3]
15
- smb_versions = ARGV[4]&.split(',') || ['1','2','3']
12
+ args = ARGV.dup
13
+ options = {
14
+ domain: '.',
15
+ username: '',
16
+ password: '',
17
+ share: nil,
18
+ smbv1: true,
19
+ smbv2: true,
20
+ smbv3: true,
21
+ target: nil
22
+ }
23
+ options[:share] = args.pop
24
+ options[:target ] = args.pop
25
+ optparser = OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{File.basename(__FILE__)} [options] target share"
27
+ opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
28
+ options[:smbv1] = smbv1
29
+ end
30
+ opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
31
+ options[:smbv2] = smbv2
32
+ end
33
+ opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
34
+ options[:smbv3] = smbv3
35
+ end
36
+ opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
37
+ if username.include?('\\')
38
+ options[:domain], options[:username] = username.split('\\', 2)
39
+ else
40
+ options[:username] = username
41
+ end
42
+ end
43
+ opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
44
+ options[:password] = password
45
+ end
46
+ end
47
+ optparser.parse!(args)
48
+
49
+ if options[:target].nil? || options[:share].nil?
50
+ abort(optparser.help)
51
+ end
16
52
 
17
- path = "\\\\#{address}\\#{share}"
53
+ path = "\\\\#{options[:target]}\\#{options[:share]}"
18
54
 
19
- sock = TCPSocket.new address, 445
55
+ sock = TCPSocket.new options[:target], 445
20
56
  dispatcher = RubySMB::Dispatcher::Socket.new(sock)
21
57
 
22
- client = RubySMB::Client.new(dispatcher, smb1: smb_versions.include?('1'), smb2: smb_versions.include?('2'), smb3: smb_versions.include?('3'), username: username, password: password)
58
+ client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
23
59
  protocol = client.negotiate
24
60
  status = client.authenticate
25
61
 
26
62
  puts "#{protocol} : #{status}"
63
+ unless status == WindowsError::NTStatus::STATUS_SUCCESS
64
+ puts 'Authentication failed!'
65
+ exit(1)
66
+ end
27
67
 
28
68
  begin
29
69
  tree = client.tree_connect(path)
@@ -31,4 +71,5 @@ begin
31
71
  tree.disconnect!
32
72
  rescue StandardError => e
33
73
  puts "Failed to connect to #{path}: #{e.message}"
74
+ exit(1)
34
75
  end
@@ -212,9 +212,17 @@ module RubySMB
212
212
 
213
213
  raw = smb2_ntlmssp_authenticate(type3_message, @session_id)
214
214
  response = smb2_ntlmssp_final_packet(raw)
215
-
216
- if @smb3 && !@session_encrypt_data && response.session_flags.encrypt_data == 1
217
- @session_encrypt_data = true
215
+ @session_is_guest = response.session_flags.guest == 1
216
+
217
+ if @smb3
218
+ if response.session_flags.encrypt_data == 1
219
+ # if the server indicates that encryption is required, enable it
220
+ @session_encrypt_data = true
221
+ elsif (@session_is_guest && password != '') || (username == '' && password == '')
222
+ # disable encryption when necessary
223
+ @session_encrypt_data = false
224
+ end
225
+ # otherwise, leave encryption to the default value that it was initialized to
218
226
  end
219
227
  ######
220
228
  # DEBUG
@@ -292,6 +292,7 @@ module RubySMB
292
292
  @sequence_counter = 0
293
293
  @session_id = 0x00
294
294
  @session_key = ''
295
+ @session_is_guest = false
295
296
  @signing_required = false
296
297
  @smb1 = smb1
297
298
  @smb2 = smb2
@@ -306,6 +307,8 @@ module RubySMB
306
307
  @server_supports_multi_credit = false
307
308
 
308
309
  # SMB 3.x options
310
+ # this merely initializes the default value for session encryption, it may be changed as necessary when a
311
+ # session setup response is received
309
312
  @session_encrypt_data = always_encrypt
310
313
 
311
314
  @ntlm_client = Net::NTLM::Client.new(
@@ -444,8 +447,13 @@ module RubySMB
444
447
  unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest) || session_key.empty?
445
448
  if self.smb2 && signing_required
446
449
  packet = smb2_sign(packet)
447
- elsif self.smb3 && (signing_required || packet.is_a?(RubySMB::SMB2::Packet::TreeConnectRequest))
448
- packet = smb3_sign(packet)
450
+ elsif self.smb3
451
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/652e0c14-5014-4470-999d-b174d7b2da87
452
+ if @dialect == '0x0311' && (signing_required || (!@session_is_guest && packet.is_a?(RubySMB::SMB2::Packet::TreeConnectRequest)))
453
+ packet = smb3_sign(packet)
454
+ elsif signing_required
455
+ packet = smb3_sign(packet)
456
+ end
449
457
  end
450
458
  end
451
459
  else
@@ -589,6 +597,7 @@ module RubySMB
589
597
  self.session_id = 0x00
590
598
  self.user_id = 0x00
591
599
  self.session_key = ''
600
+ self.session_is_guest = false
592
601
  self.sequence_counter = 0
593
602
  self.smb2_message_id = 0
594
603
  self.client_encryption_key = nil
@@ -59,8 +59,8 @@ module RubySMB
59
59
  # Make sure we don't modify the caller's hash options
60
60
  opts = opts.dup
61
61
  opts[:filename] = opts[:filename].dup
62
- opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\')
63
- open_file(**opts)
62
+ opts[:filename].prepend('\\') unless opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
63
+ _open(**opts)
64
64
  end
65
65
 
66
66
  # Open a file on the remote share.
@@ -80,83 +80,12 @@ module RubySMB
80
80
  # @return [RubySMB::SMB1::File] handle to the created file
81
81
  # @raise [RubySMB::Error::InvalidPacket] if the response command is not SMB_COM_NT_CREATE_ANDX
82
82
  # @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
83
- def open_file(filename:, flags: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
84
- impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
85
- nt_create_andx_request = RubySMB::SMB1::Packet::NtCreateAndxRequest.new
86
- nt_create_andx_request = set_header_fields(nt_create_andx_request)
87
-
88
- nt_create_andx_request.parameter_block.ext_file_attributes.normal = 1
89
-
90
- if flags
91
- nt_create_andx_request.parameter_block.flags = flags
92
- else
93
- nt_create_andx_request.parameter_block.flags.request_extended_response = 1
94
- end
95
-
96
- if options
97
- nt_create_andx_request.parameter_block.create_options = options
98
- else
99
- nt_create_andx_request.parameter_block.create_options.directory_file = 0
100
- nt_create_andx_request.parameter_block.create_options.non_directory_file = 1
101
- end
102
-
103
- if read
104
- nt_create_andx_request.parameter_block.share_access.share_read = 1
105
- nt_create_andx_request.parameter_block.desired_access.read_data = 1
106
- nt_create_andx_request.parameter_block.desired_access.read_ea = 1
107
- nt_create_andx_request.parameter_block.desired_access.read_attr = 1
108
- nt_create_andx_request.parameter_block.desired_access.read_control = 1
109
- end
110
-
111
- if write
112
- nt_create_andx_request.parameter_block.share_access.share_write = 1
113
- nt_create_andx_request.parameter_block.desired_access.write_data = 1
114
- nt_create_andx_request.parameter_block.desired_access.append_data = 1
115
- nt_create_andx_request.parameter_block.desired_access.write_ea = 1
116
- nt_create_andx_request.parameter_block.desired_access.write_attr = 1
117
- end
118
-
119
- if delete
120
- nt_create_andx_request.parameter_block.share_access.share_delete = 1
121
- nt_create_andx_request.parameter_block.desired_access.delete_access = 1
122
- end
123
-
124
- nt_create_andx_request.parameter_block.impersonation_level = impersonation
125
- nt_create_andx_request.parameter_block.create_disposition = disposition
126
-
127
- unicode_enabled = nt_create_andx_request.smb_header.flags2.unicode == 1
128
- nt_create_andx_request.data_block.file_name = add_null_termination(str: filename, unicode: unicode_enabled)
129
-
130
- raw_response = @client.send_recv(nt_create_andx_request)
131
- response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
132
- unless response.valid?
133
- if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
134
- response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
135
- response.smb_header.command == response.original_command
136
- raise RubySMB::Error::InvalidPacket.new(
137
- 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
138
- 'error occurs while parsing it. It is probably missing the '\
139
- 'required extended information.'
140
- )
141
- end
142
- raise RubySMB::Error::InvalidPacket.new(
143
- expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
144
- expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
145
- packet: response
146
- )
147
- end
148
- unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
149
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code
150
- end
151
-
152
- case response.parameter_block.resource_type
153
- when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
154
- RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
155
- when RubySMB::SMB1::ResourceType::DISK
156
- RubySMB::SMB1::File.new(name: filename, tree: self, response: response)
157
- else
158
- raise RubySMB::Error::RubySMBError
159
- end
83
+ def open_file(opts)
84
+ # Make sure we don't modify the caller's hash options
85
+ opts = opts.dup
86
+ opts[:filename] = opts[:filename].dup
87
+ opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
88
+ _open(**opts)
160
89
  end
161
90
 
162
91
  # List `directory` on the remote share.
@@ -264,6 +193,85 @@ module RubySMB
264
193
 
265
194
  private
266
195
 
196
+ def _open(filename:, flags: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
197
+ impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
198
+ nt_create_andx_request = RubySMB::SMB1::Packet::NtCreateAndxRequest.new
199
+ nt_create_andx_request = set_header_fields(nt_create_andx_request)
200
+
201
+ nt_create_andx_request.parameter_block.ext_file_attributes.normal = 1
202
+
203
+ if flags
204
+ nt_create_andx_request.parameter_block.flags = flags
205
+ else
206
+ nt_create_andx_request.parameter_block.flags.request_extended_response = 1
207
+ end
208
+
209
+ if options
210
+ nt_create_andx_request.parameter_block.create_options = options
211
+ else
212
+ nt_create_andx_request.parameter_block.create_options.directory_file = 0
213
+ nt_create_andx_request.parameter_block.create_options.non_directory_file = 1
214
+ end
215
+
216
+ if read
217
+ nt_create_andx_request.parameter_block.share_access.share_read = 1
218
+ nt_create_andx_request.parameter_block.desired_access.read_data = 1
219
+ nt_create_andx_request.parameter_block.desired_access.read_ea = 1
220
+ nt_create_andx_request.parameter_block.desired_access.read_attr = 1
221
+ nt_create_andx_request.parameter_block.desired_access.read_control = 1
222
+ end
223
+
224
+ if write
225
+ nt_create_andx_request.parameter_block.share_access.share_write = 1
226
+ nt_create_andx_request.parameter_block.desired_access.write_data = 1
227
+ nt_create_andx_request.parameter_block.desired_access.append_data = 1
228
+ nt_create_andx_request.parameter_block.desired_access.write_ea = 1
229
+ nt_create_andx_request.parameter_block.desired_access.write_attr = 1
230
+ end
231
+
232
+ if delete
233
+ nt_create_andx_request.parameter_block.share_access.share_delete = 1
234
+ nt_create_andx_request.parameter_block.desired_access.delete_access = 1
235
+ end
236
+
237
+ nt_create_andx_request.parameter_block.impersonation_level = impersonation
238
+ nt_create_andx_request.parameter_block.create_disposition = disposition
239
+
240
+ unicode_enabled = nt_create_andx_request.smb_header.flags2.unicode == 1
241
+ nt_create_andx_request.data_block.file_name = add_null_termination(str: filename, unicode: unicode_enabled)
242
+
243
+ raw_response = @client.send_recv(nt_create_andx_request)
244
+ response = RubySMB::SMB1::Packet::NtCreateAndxResponse.read(raw_response)
245
+ unless response.valid?
246
+ if response.is_a?(RubySMB::SMB1::Packet::EmptyPacket) &&
247
+ response.smb_header.protocol == RubySMB::SMB1::SMB_PROTOCOL_ID &&
248
+ response.smb_header.command == response.original_command
249
+ raise RubySMB::Error::InvalidPacket.new(
250
+ 'The response seems to be an SMB1 NtCreateAndxResponse but an '\
251
+ 'error occurs while parsing it. It is probably missing the '\
252
+ 'required extended information.'
253
+ )
254
+ end
255
+ raise RubySMB::Error::InvalidPacket.new(
256
+ expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
257
+ expected_cmd: RubySMB::SMB1::Packet::NtCreateAndxResponse::COMMAND,
258
+ packet: response
259
+ )
260
+ end
261
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
262
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
263
+ end
264
+
265
+ case response.parameter_block.resource_type
266
+ when RubySMB::SMB1::ResourceType::BYTE_MODE_PIPE, RubySMB::SMB1::ResourceType::MESSAGE_MODE_PIPE
267
+ RubySMB::SMB1::Pipe.new(name: filename, tree: self, response: response)
268
+ when RubySMB::SMB1::ResourceType::DISK
269
+ RubySMB::SMB1::File.new(name: filename, tree: self, response: response)
270
+ else
271
+ raise RubySMB::Error::RubySMBError
272
+ end
273
+ end
274
+
267
275
  # Sets ParameterBlock options for FIND_FIRST2 and
268
276
  # FIND_NEXT2 requests. In particular we need to do this
269
277
  # to tell the server to ignore the Trans2DataBlock as we are
@@ -60,78 +60,16 @@ module RubySMB
60
60
  # Make sure we don't modify the caller's hash options
61
61
  opts = opts.dup
62
62
  opts[:filename] = opts[:filename].dup
63
- opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with? '\\'
64
- open_file(**opts)
63
+ opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
64
+ _open(**opts)
65
65
  end
66
66
 
67
- def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
68
- impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
69
-
70
- create_request = RubySMB::SMB2::Packet::CreateRequest.new
71
- create_request = set_header_fields(create_request)
72
-
73
- # If the user supplied file attributes, use those, otherwise set some
74
- # sane defaults.
75
- if attributes
76
- create_request.file_attributes = attributes
77
- else
78
- create_request.file_attributes.directory = 0
79
- create_request.file_attributes.normal = 1
80
- end
81
-
82
- # If the user supplied Create Options, use those, otherwise set some
83
- # sane defaults.
84
- if options
85
- create_request.create_options = options
86
- else
87
- create_request.create_options.directory_file = 0
88
- create_request.create_options.non_directory_file = 1
89
- end
90
-
91
- if read
92
- create_request.share_access.read_access = 1
93
- create_request.desired_access.read_data = 1
94
- end
95
-
96
- if write
97
- create_request.share_access.write_access = 1
98
- create_request.desired_access.write_data = 1
99
- create_request.desired_access.append_data = 1
100
- end
101
-
102
- if delete
103
- create_request.share_access.delete_access = 1
104
- create_request.desired_access.delete_access = 1
105
- end
106
-
107
- create_request.requested_oplock = 0xff
108
- create_request.impersonation_level = impersonation
109
- create_request.create_disposition = disposition
110
- create_request.name = filename
111
-
112
- raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
113
- response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
114
- unless response.valid?
115
- raise RubySMB::Error::InvalidPacket.new(
116
- expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
117
- expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
118
- packet: response
119
- )
120
- end
121
- unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
122
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code
123
- end
124
-
125
- case @share_type
126
- when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK
127
- RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @tree_connect_encrypt_data)
128
- when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE
129
- RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response)
130
- # when RubySMB::SMB2::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
131
- # it's a printer!
132
- else
133
- raise RubySMB::Error::RubySMBError, 'Unsupported share type'
134
- end
67
+ def open_file(opts)
68
+ # Make sure we don't modify the caller's hash options
69
+ opts = opts.dup
70
+ opts[:filename] = opts[:filename].dup
71
+ opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with?('\\'.encode(opts[:filename].encoding))
72
+ _open(**opts)
135
73
  end
136
74
 
137
75
  # List `directory` on the remote share.
@@ -270,6 +208,78 @@ module RubySMB
270
208
  request.smb2_header.credits = 256
271
209
  request
272
210
  end
211
+
212
+ private
213
+
214
+ def _open(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
215
+ impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
216
+
217
+ create_request = RubySMB::SMB2::Packet::CreateRequest.new
218
+ create_request = set_header_fields(create_request)
219
+
220
+ # If the user supplied file attributes, use those, otherwise set some
221
+ # sane defaults.
222
+ if attributes
223
+ create_request.file_attributes = attributes
224
+ else
225
+ create_request.file_attributes.directory = 0
226
+ create_request.file_attributes.normal = 1
227
+ end
228
+
229
+ # If the user supplied Create Options, use those, otherwise set some
230
+ # sane defaults.
231
+ if options
232
+ create_request.create_options = options
233
+ else
234
+ create_request.create_options.directory_file = 0
235
+ create_request.create_options.non_directory_file = 1
236
+ end
237
+
238
+ if read
239
+ create_request.share_access.read_access = 1
240
+ create_request.desired_access.read_data = 1
241
+ end
242
+
243
+ if write
244
+ create_request.share_access.write_access = 1
245
+ create_request.desired_access.write_data = 1
246
+ create_request.desired_access.append_data = 1
247
+ end
248
+
249
+ if delete
250
+ create_request.share_access.delete_access = 1
251
+ create_request.desired_access.delete_access = 1
252
+ end
253
+
254
+ create_request.requested_oplock = 0xff
255
+ create_request.impersonation_level = impersonation
256
+ create_request.create_disposition = disposition
257
+ create_request.name = filename
258
+
259
+ raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data)
260
+ response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
261
+ unless response.valid?
262
+ raise RubySMB::Error::InvalidPacket.new(
263
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
264
+ expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
265
+ packet: response
266
+ )
267
+ end
268
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
269
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
270
+ end
271
+
272
+ case @share_type
273
+ when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK
274
+ RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @tree_connect_encrypt_data)
275
+ when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE
276
+ RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response)
277
+ # when RubySMB::SMB2::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
278
+ # it's a printer!
279
+ else
280
+ raise RubySMB::Error::RubySMBError, 'Unsupported share type'
281
+ end
282
+ end
273
283
  end
274
284
  end
275
285
  end
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.0.2'.freeze
2
+ VERSION = '3.0.3'.freeze
3
3
  end
@@ -495,17 +495,17 @@ RSpec.describe RubySMB::SMB1::Tree do
495
495
  describe '#open_pipe' do
496
496
  let(:opts) { { filename: 'test', write: true } }
497
497
  before :example do
498
- allow(tree).to receive(:open_file)
498
+ allow(tree).to receive(:_open)
499
499
  end
500
500
 
501
501
  it 'calls #open_file with the provided options' do
502
502
  opts[:filename] ='\\test'
503
- expect(tree).to receive(:open_file).with(opts)
503
+ expect(tree).to receive(:_open).with(opts)
504
504
  tree.open_pipe(**opts)
505
505
  end
506
506
 
507
507
  it 'prepends the filename with \\ if needed' do
508
- expect(tree).to receive(:open_file).with(filename: '\\test', write: true)
508
+ expect(tree).to receive(:_open).with(filename: '\\test', write: true)
509
509
  tree.open_pipe(**opts)
510
510
  end
511
511
 
@@ -535,17 +535,17 @@ RSpec.describe RubySMB::SMB2::Tree do
535
535
  describe '#open_pipe' do
536
536
  let(:opts) { { filename: '\\test', write: true } }
537
537
  before :example do
538
- allow(tree).to receive(:open_file)
538
+ allow(tree).to receive(:_open)
539
539
  end
540
540
 
541
541
  it 'calls #open_file with the provided options' do
542
542
  opts[:filename] ='test'
543
- expect(tree).to receive(:open_file).with(**opts)
543
+ expect(tree).to receive(:_open).with(**opts)
544
544
  tree.open_pipe(**opts)
545
545
  end
546
546
 
547
547
  it 'remove the leading \\ from the filename if needed' do
548
- expect(tree).to receive(:open_file).with(filename: 'test', write: true)
548
+ expect(tree).to receive(:_open).with(filename: 'test', write: true)
549
549
  tree.open_pipe(**opts)
550
550
  end
551
551
 
data.tar.gz.sig CHANGED
@@ -1,2 +1,3 @@
1
- v!�Ja�m.��y�����:;��*]q����鋊K��B�M6$��r=�X��n)����� ���C~�'��^�k5�;�/38XrM�t��� L��Q��m�ig6#�Q�A�C�,K�hÀ`#'��톟�������>��j"$�Ӽ�SdR�Գ���������i�΍ꎈ{�f�8N��\Gx��bO��vh����%�Mx���r[iM�u���E��Ŀ@�N�hg��
2
- ����l�����ֆ�� `3���
1
+ 8z�n:�"bѠny�Lv04���MF�����D3��'�H@����� f��W}��~x�Y0||�H��f�:�����mn�� ���+�hӷ��f�����~
2
+ h
3
+ 2ئbJj�����$�]dxGy���]E�^F���0!��#�!��6�UdP�gG�d+od��X;BVš���� �FU���]����r��7�J^�+r���.�x/:�?�ǒ+9,k�3�&��Bl�����H�w3Lx+;,�<�
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_smb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -97,7 +97,7 @@ cert_chain:
97
97
  EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
98
98
  9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
99
99
  -----END CERTIFICATE-----
100
- date: 2022-02-04 00:00:00.000000000 Z
100
+ date: 2022-02-11 00:00:00.000000000 Z
101
101
  dependencies:
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: redcarpet
metadata.gz.sig CHANGED
Binary file