ruby_smb 3.2.7 → 3.3.0
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/lib/ruby_smb/server/server_client/share_io.rb +2 -2
- data/lib/ruby_smb/server/share/provider/disk.rb +2 -2
- data/lib/ruby_smb/server/share/provider/processor.rb +25 -0
- data/lib/ruby_smb/server/share/provider/virtual_disk.rb +3 -3
- data/lib/ruby_smb/server/share/provider.rb +48 -1
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +3 -1
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/response_spec.rb +4 -6
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +5 -0
- data.tar.gz.sig +0 -0
- 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: 2f696c845250348795f67d3d5b163c65b432acdbcd01bc86d0a6cd81ddcbfc67
|
4
|
+
data.tar.gz: b43dcb4fa6ff4497e61329b8b5a17049a6b445fcdf5c3df61dc380960ea02b9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8368cf1844e47161a57051d7b8dfa807b665c898153636b1baedb2211acd4685ef67c5ba167581ce84a9ec7c8c17e812d1f876bb70f34b647bb075bb94861349
|
7
|
+
data.tar.gz: 3b33a185fb3b981147b005e13793aa1f8a09e11c2e5e0dc9b75469ef980ec019bc2dd5e9bef187910f9b5be012d7ca710799b45aa0bdf79547ae3d48de8e22e7
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -11,7 +11,7 @@ module RubySMB
|
|
11
11
|
end
|
12
12
|
|
13
13
|
logger.debug("Received #{SMB1::Commands.name(request.smb_header.command)} request for share: #{share_processor.provider.name}")
|
14
|
-
share_processor.
|
14
|
+
share_processor.share_io(__callee__, request)
|
15
15
|
end
|
16
16
|
|
17
17
|
alias :do_close_smb1 :proxy_share_io_smb1
|
@@ -29,7 +29,7 @@ module RubySMB
|
|
29
29
|
end
|
30
30
|
|
31
31
|
logger.debug("Received #{SMB2::Commands.name(request.smb2_header.command)} request for share: #{share_processor.provider.name}")
|
32
|
-
share_processor.
|
32
|
+
share_processor.share_io(__callee__, request)
|
33
33
|
end
|
34
34
|
|
35
35
|
alias :do_close_smb2 :proxy_share_io_smb2
|
@@ -14,13 +14,13 @@ module RubySMB
|
|
14
14
|
# @param [String] name The name of this share.
|
15
15
|
# @param [String, Pathname] path The local file system path to share. This path must be an absolute path to an existing
|
16
16
|
# directory.
|
17
|
-
def initialize(name, path)
|
17
|
+
def initialize(name, path, hooks: nil)
|
18
18
|
path = Pathname.new(File.expand_path(path)) if path.is_a?(String)
|
19
19
|
raise ArgumentError.new('path must be a directory') unless path.directory? # it needs to exist
|
20
20
|
raise ArgumentError.new('path must be absolute') unless path.absolute? # it needs to be absolute so it is independent of the cwd
|
21
21
|
|
22
22
|
@path = path
|
23
|
-
super(name)
|
23
|
+
super(name, hooks: hooks)
|
24
24
|
end
|
25
25
|
|
26
26
|
attr_accessor :path
|
@@ -80,6 +80,31 @@ module RubySMB
|
|
80
80
|
@server_client.server
|
81
81
|
end
|
82
82
|
|
83
|
+
# Forward a share IO method for a particular request. This is a choke point to allow any hooks that were
|
84
|
+
# registered with the share provider to be executed before and after the specified method is invoked to
|
85
|
+
# process the request and generate the response. This is used for both SMB1 and SMB2 requests.
|
86
|
+
#
|
87
|
+
# @param [Symbol] method_name The method name to forward the request to
|
88
|
+
# @param [RubySMB::GenericPacket] request The request packet to be processed
|
89
|
+
# @return [RubySMB::GenericPacket]
|
90
|
+
def share_io(method_name, request)
|
91
|
+
@provider.hooks.each do |hook|
|
92
|
+
next unless hook.request_class == request.class && hook.location == :before
|
93
|
+
|
94
|
+
request = hook.callback.call(@session, request) || request
|
95
|
+
end
|
96
|
+
|
97
|
+
response = send(method_name, request)
|
98
|
+
|
99
|
+
@provider.hooks.each do |hook|
|
100
|
+
next unless hook.request_class == request.class && hook.location == :after
|
101
|
+
|
102
|
+
response = hook.callback.call(@session, request, response) || response
|
103
|
+
end
|
104
|
+
|
105
|
+
response
|
106
|
+
end
|
107
|
+
|
83
108
|
# The underlying share provider that this is a processor for.
|
84
109
|
# @!attribute [r] provider
|
85
110
|
# @return [RubySMB::Server::Share::Provider::Base]
|
@@ -14,9 +14,9 @@ module RubySMB
|
|
14
14
|
private_constant :HASH_METHODS
|
15
15
|
|
16
16
|
# @param [String] name The name of this share.
|
17
|
-
def initialize(name)
|
17
|
+
def initialize(name, hooks: nil)
|
18
18
|
@vfs = {}
|
19
|
-
super(name, add(VirtualPathname.new(self, File::SEPARATOR)))
|
19
|
+
super(name, add(VirtualPathname.new(self, File::SEPARATOR)), hooks: hooks)
|
20
20
|
end
|
21
21
|
|
22
22
|
# Add a dynamic file to the virtual file system. A dynamic file is one whose contents are generated by the
|
@@ -86,7 +86,7 @@ module RubySMB
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def new_processor(server_client, session)
|
89
|
-
scoped_virtual_disk = self.class.new(@name)
|
89
|
+
scoped_virtual_disk = self.class.new(@name, hooks: @hooks)
|
90
90
|
@vfs.each_value do |path|
|
91
91
|
path = path.dup
|
92
92
|
path.virtual_disk = scoped_virtual_disk if path.is_a?(VirtualPathname)
|
@@ -6,9 +6,51 @@ module RubySMB
|
|
6
6
|
# type and name. It is shared across all client connections and
|
7
7
|
# sessions.
|
8
8
|
class Base
|
9
|
+
Hook = Struct.new(:request_class, :location, :callback)
|
10
|
+
|
9
11
|
# @param [String] name The name of this share.
|
10
|
-
def initialize(name)
|
12
|
+
def initialize(name, hooks: nil)
|
11
13
|
@name = name
|
14
|
+
@hooks = hooks || []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a hook to be called when the specified request class is processed. Any hook that was previously
|
18
|
+
# installed for the request class and location will be removed. A hook installed with a location of :before
|
19
|
+
# will be called with the session and request as the only two arguments. The return value, if provided, will
|
20
|
+
# replace the request that is to be processed. A hook installed with a location of :after will be called with
|
21
|
+
# the session, request and response as the only three arguments. The return value, if provided, will replace
|
22
|
+
# the response that is to be sent to the client.
|
23
|
+
#
|
24
|
+
# @param [RubySMB::GenericPacket] request_class The request class to register the hook for.
|
25
|
+
# @param [Proc] callback The routine to be executed when the request class is being processed.
|
26
|
+
# @param [Symbol] location When the callback should be invoked. Must be either :before or :after.
|
27
|
+
def add_hook(request_class, callback: nil, location: :before, &block)
|
28
|
+
unless %i[ before after ].include?(location)
|
29
|
+
raise ArgumentError, 'the location argument must be :before or :after'
|
30
|
+
end
|
31
|
+
|
32
|
+
unless callback.nil? ^ block.nil?
|
33
|
+
raise ArgumentError, 'either a callback or a block must be specified'
|
34
|
+
end
|
35
|
+
|
36
|
+
# Remove any hooks that were previously installed, this enforces that only one hook can be present at a time
|
37
|
+
# for any particular request class and location combination.
|
38
|
+
remove_hook(request_class, location: location)
|
39
|
+
@hooks << Hook.new(request_class, location, callback || block)
|
40
|
+
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Remove a hook for the specified request class.
|
45
|
+
#
|
46
|
+
# @param [RubySMB::GenericPacket] request_class The request class to register the hook for.
|
47
|
+
# @param [Symbol] location When the callback should be invoked.
|
48
|
+
def remove_hook(request_class, location: :before)
|
49
|
+
@hooks.filter! do |hook|
|
50
|
+
hook.request_class == request_class && hook.location == location
|
51
|
+
end
|
52
|
+
|
53
|
+
nil
|
12
54
|
end
|
13
55
|
|
14
56
|
# Create a new, session-specific processor instance for this share.
|
@@ -28,6 +70,11 @@ module RubySMB
|
|
28
70
|
# @!attribute [r] name
|
29
71
|
# @return [String]
|
30
72
|
attr_accessor :name
|
73
|
+
|
74
|
+
# The hooks installed for this share.
|
75
|
+
# @!attribute [r] hooks
|
76
|
+
# @return [Array]
|
77
|
+
attr_accessor :hooks
|
31
78
|
end
|
32
79
|
end
|
33
80
|
end
|
@@ -35,7 +35,7 @@ module RubySMB
|
|
35
35
|
end
|
36
36
|
|
37
37
|
uint64 :allocation_size, label: 'Allocation Size'
|
38
|
-
smb_ext_file_attributes :ext_file_attributes, label: '
|
38
|
+
smb_ext_file_attributes :ext_file_attributes, label: 'Extended File Attributes'
|
39
39
|
share_access :share_access, label: 'Share Access'
|
40
40
|
# The following constants are defined in RubySMB::Dispositions
|
41
41
|
uint32 :create_disposition, label: 'Create Disposition'
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -85,6 +85,7 @@ module RubySMB
|
|
85
85
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a QueryDirectoryResponse packet
|
86
86
|
def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
|
87
87
|
create_response = open_directory(directory: directory)
|
88
|
+
opened_directory = RubySMB::SMB2::File.new(tree: self, response: create_response, name: directory)
|
88
89
|
file_id = create_response.file_id
|
89
90
|
|
90
91
|
directory_request = RubySMB::SMB2::Packet::QueryDirectoryRequest.new
|
@@ -127,8 +128,9 @@ module RubySMB
|
|
127
128
|
# Reset the message id so the client can update appropriately.
|
128
129
|
directory_request.smb2_header.message_id = 0
|
129
130
|
end
|
130
|
-
|
131
131
|
files
|
132
|
+
ensure
|
133
|
+
opened_directory.close if opened_directory
|
132
134
|
end
|
133
135
|
|
134
136
|
# 'Opens' a directory file on the remote end, using a CreateRequest. This
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -131,7 +131,7 @@ RSpec.describe RubySMB::Dcerpc::Response do
|
|
131
131
|
end
|
132
132
|
|
133
133
|
describe '#stub_length' do
|
134
|
-
let(:stub_length) { rand(0xFF) }
|
134
|
+
let(:stub_length) { rand(1..0xFF) }
|
135
135
|
before :example do
|
136
136
|
packet.stub = 'A' * stub_length
|
137
137
|
end
|
@@ -142,7 +142,7 @@ RSpec.describe RubySMB::Dcerpc::Response do
|
|
142
142
|
|
143
143
|
context 'with auth verifier' do
|
144
144
|
it 'returns the correct stub length' do
|
145
|
-
auth_size = rand(0xFF)
|
145
|
+
auth_size = rand(1..0xFF)
|
146
146
|
packet.pdu_header.auth_length = auth_size
|
147
147
|
packet.auth_value = 'B' * auth_size
|
148
148
|
expect(packet.stub_length).to eq(stub_length + packet.auth_pad.num_bytes)
|
@@ -152,8 +152,8 @@ RSpec.describe RubySMB::Dcerpc::Response do
|
|
152
152
|
|
153
153
|
describe '#read' do
|
154
154
|
let(:response) { described_class.new }
|
155
|
-
let(:auth_size) { rand(0xFF) }
|
156
|
-
let(:stub_size) { rand(0xFF) }
|
155
|
+
let(:auth_size) { rand(1..0xFF) }
|
156
|
+
let(:stub_size) { rand(1..0xFF) }
|
157
157
|
before :example do
|
158
158
|
response.pdu_header.auth_length = auth_size
|
159
159
|
response.stub = 'A' * stub_size
|
@@ -176,5 +176,3 @@ RSpec.describe RubySMB::Dcerpc::Response do
|
|
176
176
|
expect(described_class.read(binary)).to eq(packet)
|
177
177
|
end
|
178
178
|
end
|
179
|
-
|
180
|
-
|
@@ -166,10 +166,14 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
166
166
|
let(:create_res) { double('create response') }
|
167
167
|
let(:query_dir_req) { RubySMB::SMB2::Packet::QueryDirectoryRequest.new }
|
168
168
|
let(:query_dir_res) { RubySMB::SMB2::Packet::QueryDirectoryResponse.new }
|
169
|
+
let(:open_dir) { instance_double(RubySMB::SMB2::File) }
|
169
170
|
|
170
171
|
before :example do
|
171
172
|
allow(tree).to receive(:open_directory).and_return(create_res)
|
172
173
|
allow(create_res).to receive(:file_id)
|
174
|
+
allow(RubySMB::SMB2::File).to receive(:new).and_return(open_dir)
|
175
|
+
allow(open_dir).to receive(:close)
|
176
|
+
allow(create_res).to receive(:file_attributes)
|
173
177
|
allow(RubySMB::SMB2::Packet::QueryDirectoryRequest).to receive(:new).and_return(query_dir_req)
|
174
178
|
allow(client).to receive(:send_recv)
|
175
179
|
allow(RubySMB::SMB2::Packet::QueryDirectoryResponse).to receive(:read).and_return(query_dir_res)
|
@@ -180,6 +184,7 @@ RSpec.describe RubySMB::SMB2::Tree do
|
|
180
184
|
dir = '/dir'
|
181
185
|
expect(tree).to receive(:open_directory).with(directory: dir).and_return(create_res)
|
182
186
|
tree.list(directory: dir)
|
187
|
+
expect(open_dir).to have_received(:close)
|
183
188
|
end
|
184
189
|
|
185
190
|
it 'uses the File ID from the create response' do
|
data.tar.gz.sig
CHANGED
Binary file
|
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.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -38,7 +38,7 @@ cert_chain:
|
|
38
38
|
DgscAao7wB3xW2BWEp1KnaDWkf1x9ttgoBEYyuYwU7uatB67kBQG1PKvLt79wHvz
|
39
39
|
Dxs+KOjGbBRfMnPgVGYkORKVrZIwlaboHbDKxcVW5xv+oZc7KYXWGg==
|
40
40
|
-----END CERTIFICATE-----
|
41
|
-
date: 2023-11
|
41
|
+
date: 2023-12-11 00:00:00.000000000 Z
|
42
42
|
dependencies:
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: redcarpet
|
metadata.gz.sig
CHANGED
Binary file
|