ruby_smb 3.0.2 → 3.0.3

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