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 +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
|