ruby_smb 3.2.7 → 3.3.0

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: 3c3642cdede7c8763296f8249419acad490605d06b8dc546411dab0ab25a7250
4
- data.tar.gz: ff2403b3cd0e5b633fcee3db672dee4c4f582d13ac7bfbf3cd1dd7b4c9d2e5f4
3
+ metadata.gz: 2f696c845250348795f67d3d5b163c65b432acdbcd01bc86d0a6cd81ddcbfc67
4
+ data.tar.gz: b43dcb4fa6ff4497e61329b8b5a17049a6b445fcdf5c3df61dc380960ea02b9c
5
5
  SHA512:
6
- metadata.gz: 97bd6b1bdf5b205eb8c21f1d5d980e03a616f7f25dba1219b280a199dbeb72fecc7c9dfe2efd2afa847a3e524f3ea167cc364d79e2be8b72e7e8788cdd5c29b9
7
- data.tar.gz: 61ea09ea2cd46411ead8e4bef0ce4c9eb194d8e741b1a1b1bfdd582a260d89e04edc57bd4e47ae0603a1ae125846062dde03a47ff1b17095b8b605b725461d44
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.send(__callee__, request)
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.send(__callee__, request)
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: 'Extented File Attributes'
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'
@@ -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
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.2.7'.freeze
2
+ VERSION = '3.3.0'.freeze
3
3
  end
@@ -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.2.7
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-13 00:00:00.000000000 Z
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