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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/examples/anonymous_auth.rb +29 -6
- data/examples/auth_capture.rb +3 -3
- data/examples/file_server.rb +3 -3
- data/examples/read_file.rb +51 -10
- data/examples/tree_connect.rb +49 -8
- data/lib/ruby_smb/client/authentication.rb +11 -3
- data/lib/ruby_smb/client.rb +11 -2
- data/lib/ruby_smb/smb1/tree.rb +87 -79
- data/lib/ruby_smb/smb2/tree.rb +80 -70
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +3 -3
- data.tar.gz.sig +3 -2
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7ce1d9892eb5ccf2881dd78a8e1983926554f69de301560539e7d7da37061b0
|
4
|
+
data.tar.gz: '01692d97a27c63f470b7c2899ad9cfb2828c9e254f0747176a29555d462bbf1d'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fafe3f08b518b285b33832e0809c9b5f4687800457bec2c1945dd33f4c7109466926a5f55a537a3cdf9851f1c0c6b9979aa0f8d75f0004749f8bdd56ba26ab4
|
7
|
+
data.tar.gz: 070550512fe2ffadaecb97ea014e8fa5563b722b0184303c022f901bb60466991979673adf0dedfb411585b910e92d435308f7d900326ee6d8aa8ead5e1cdf0c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/examples/anonymous_auth.rb
CHANGED
@@ -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
|
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:
|
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
|
-
|
26
|
-
|
27
|
-
|
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(
|
53
|
+
run_authentication(options[:target], options[:smbv1], options[:smbv2], options[:smbv3])
|
data/examples/auth_capture.rb
CHANGED
@@ -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", "
|
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", "
|
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", "
|
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!
|
data/examples/file_server.rb
CHANGED
@@ -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", "
|
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", "
|
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", "
|
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|
|
data/examples/read_file.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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 = "\\\\#{
|
55
|
+
path = "\\\\#{options[:target]}\\#{options[:share]}"
|
20
56
|
|
21
|
-
sock = TCPSocket.new
|
57
|
+
sock = TCPSocket.new options[:target], 445
|
22
58
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
23
59
|
|
24
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
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
|
data/examples/tree_connect.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
53
|
+
path = "\\\\#{options[:target]}\\#{options[:share]}"
|
18
54
|
|
19
|
-
sock = TCPSocket.new
|
55
|
+
sock = TCPSocket.new options[:target], 445
|
20
56
|
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
21
57
|
|
22
|
-
client = RubySMB::Client.new(dispatcher, smb1:
|
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
|
-
|
217
|
-
|
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
|
data/lib/ruby_smb/client.rb
CHANGED
@@ -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
|
448
|
-
|
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
|
data/lib/ruby_smb/smb1/tree.rb
CHANGED
@@ -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
|
-
|
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(
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -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
|
-
|
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(
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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
|
-
|
2
|
-
|
1
|
+
8z�n:�"bѠny�Lv04���MF�����D3��'�H@����� f��W}��~x�Y0||�H��f�:�����m�n�� ���+�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.
|
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-
|
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
|