Skip to content

Commit 2b70ec2

Browse files
committed
Payload compatible cache_in_memory
[#47720609] Msf::PayloadSet#add_module does NOT return an annotated module class as Msf::ModuleSet#add_module does because a payload module is defined as a ruby Module instead of a ruby Class. Since add_module doesn't always return an annotated_class, the logic in Msf::ModuleManager#on_module_load needed to change to NOT use annotated_class and create #add_module as return [void]. Thus, it is necessary to pass in all the metasploit module metadata to Msf::ModuleManager#cache_in_memory instead of assuming they can be derived from the (payload) Module or (other) Class.
1 parent 57576de commit 2b70ec2

File tree

6 files changed

+133
-109
lines changed

6 files changed

+133
-109
lines changed

lib/msf/core/module_manager/cache.rb

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,39 @@ def cache_empty?
1515
module_info_by_path.empty?
1616
end
1717

18+
# @note path, reference_name, and type must be passed as options because when +class_or_module+ is a payload Module,
19+
# those attributes will either not be set or not exist on the module.
20+
#
1821
# Updates the in-memory cache so that {#file_changed?} will report +false+ if
1922
# the module is loaded again.
2023
#
21-
# @param klass [Class<Msf::Module>] a module class
24+
# @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
25+
# or a payload Module.
26+
# @param options [Hash{Symbol => String}]
27+
# @option options [String] :path the path to the file from which
28+
# +class_or_module+ was loaded.
29+
# @option options [String] :reference_name the reference name for
30+
# +class_or_module+.
31+
# @option options [String] :type the module type
2232
# @return [void]
23-
def cache_in_memory(klass)
24-
modification_time = File.mtime(klass.file_path)
25-
parent_path = klass.parent.parent_path
33+
# @raise [KeyError] unless +:path+ is given.
34+
# @raise [KeyError] unless +:reference_name+ is given.
35+
# @raise [KeyError] unless +:type+ is given.
36+
def cache_in_memory(class_or_module, options={})
37+
options.assert_valid_keys(:path, :reference_name, :type)
38+
39+
path = options.fetch(:path)
40+
modification_time = File.mtime(path)
41+
42+
parent_path = class_or_module.parent.parent_path
43+
reference_name = options.fetch(:reference_name)
44+
type = options.fetch(:type)
2645

27-
module_info_by_path[klass.file_path] = {
46+
module_info_by_path[path] = {
2847
:modification_time => modification_time,
2948
:parent_path => parent_path,
30-
:reference_name => klass.refname,
31-
:type => klass.type
49+
:reference_name => reference_name,
50+
:type => type
3251
}
3352
end
3453

lib/msf/core/module_manager/loading.rb

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,39 @@ def file_changed?(path)
5353

5454
attr_accessor :module_load_error_by_path
5555

56-
# Called when a module is initially loaded such that it can be categorized
57-
# accordingly.
58-
#
59-
# @param klass (see Msf::ModuleSet#add_module)
60-
# @param type [String] The module type.
61-
# @param reference_name (see Msf::ModuleSet#add_module)
62-
# @param info [Hash{String => Array}] additional information about the module
63-
# @option info [Array<String>] 'files' List of paths to the ruby source files
64-
# where +klass+ is defined.
65-
# @option info [Array<String>] 'paths' List of module reference names.
66-
# @option info [String] 'type' The module type, should match positional
67-
# +type+ argument.
68-
# @return [void]
69-
def on_module_load(klass, type, reference_name, info={})
70-
module_set = module_set_by_type[type]
71-
annotated_klass = module_set.add_module(klass, reference_name, info)
72-
73-
cache_in_memory(annotated_klass)
74-
75-
# Automatically subscribe a wrapper around this module to the necessary
76-
# event providers based on whatever events it wishes to receive.
77-
auto_subscribe_module(annotated_klass)
78-
79-
# Notify the framework that a module was loaded
80-
framework.events.on_module_load(reference_name, annotated_klass)
81-
end
56+
# Called when a module is initially loaded such that it can be categorized
57+
# accordingly.
58+
#
59+
# @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
60+
# or a payload Module.
61+
# @param type [String] The module type.
62+
# @param reference_name The module reference name.
63+
# @param info [Hash{String => Array}] additional information about the module
64+
# @option info [Array<String>] 'files' List of paths to the ruby source files
65+
# where +class_or_module+ is defined.
66+
# @option info [Array<String>] 'paths' List of module reference names.
67+
# @option info [String] 'type' The module type, should match positional
68+
# +type+ argument.
69+
# @return [void]
70+
def on_module_load(class_or_module, type, reference_name, info={})
71+
module_set = module_set_by_type[type]
72+
module_set.add_module(class_or_module, reference_name, info)
73+
74+
path = info['files'].first
75+
cache_in_memory(
76+
class_or_module,
77+
:path => path,
78+
:reference_name => reference_name,
79+
:type => type
80+
)
81+
82+
# Automatically subscribe a wrapper around this module to the necessary
83+
# event providers based on whatever events it wishes to receive.
84+
auto_subscribe_module(class_or_module)
85+
86+
# Notify the framework that a module was loaded
87+
framework.events.on_module_load(reference_name, class_or_module)
88+
end
8289

8390
protected
8491

lib/msf/core/payload_set.rb

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -169,29 +169,38 @@ def recalculate
169169
flush_blob_cache
170170
end
171171

172-
#
173172
# This method is called when a new payload module class is loaded up. For
174173
# the payload set we simply create an instance of the class and do some
175174
# magic to figure out if it's a single, stager, or stage. Depending on
176175
# which it is, we add it to the appropriate list.
177176
#
178-
def add_module(pmodule, name, modinfo = nil)
179-
180-
if (md = name.match(/^(singles|stagers|stages)#{File::SEPARATOR}(.*)$/))
177+
# @param payload_module [::Module] The module name.
178+
# @param reference_name [String] The module reference name.
179+
# @param modinfo [Hash{String => Array}] additional information about the
180+
# module.
181+
# @option modinfo [Array<String>] 'files' List of paths to the ruby source
182+
# files where +class_or_module+ is defined.
183+
# @option modinfo [Array<String>] 'paths' List of module reference names.
184+
# @option modinfo [String] 'type' The module type, should match positional
185+
# +type+ argument.
186+
# @return [void]
187+
def add_module(payload_module, reference_name, modinfo={})
188+
189+
if (md = reference_name.match(/^(singles|stagers|stages)#{File::SEPARATOR}(.*)$/))
181190
ptype = md[1]
182-
name = md[2]
191+
reference_name = md[2]
183192
end
184193

185194
# Duplicate the Payload base class and extend it with the module
186195
# class that is passed in. This allows us to inspect the actual
187196
# module to see what type it is, and to grab other information for
188197
# our own evil purposes.
189-
instance = build_payload(pmodule).new
198+
instance = build_payload(payload_module).new
190199

191200
# Create an array of information about this payload module
192201
pinfo =
193202
[
194-
pmodule,
203+
payload_module,
195204
instance.handler_klass,
196205
instance.platform,
197206
instance.arch,
@@ -200,26 +209,12 @@ def add_module(pmodule, name, modinfo = nil)
200209
]
201210

202211
# Use the module's preferred alias if it has one
203-
name = instance.alias if (instance.alias)
212+
reference_name = instance.alias if (instance.alias)
204213

205214
# Store the module and alias name for this payload. We
206215
# also convey other information about the module, such as
207216
# the platforms and architectures it supports
208-
payload_type_modules[instance.payload_type][name] = pinfo
209-
210-
#
211-
# Disable sending singles over stagers for now
212-
#
213-
=begin
214-
# If the payload happens to be a single, but has no defined
215-
# connection, then it can also be staged. Insert it into
216-
# the staged list.
217-
if ((instance.payload_type == Payload::Type::Single) and
218-
((instance.handler_klass == Msf::Handler::None) or
219-
(instance.handler_klass == nil)))
220-
payload_type_modules[Payload::Type::Stage][name] = pinfo
221-
end
222-
=end
217+
payload_type_modules[instance.payload_type][reference_name] = pinfo
223218
end
224219

225220
#

spec/lib/msf/core/modules/loader/directory_spec.rb

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,34 @@
6262
subject.load_module(parent_path, type, module_reference_name)
6363
end
6464

65-
it 'should not load the module' do
66-
subject.load_module(parent_path, type, module_reference_name).should be_false
65+
# Payloads are defined as ruby Modules so they can behave differently
66+
context 'with payload' do
67+
let(:reference_name) do
68+
'stages/windows/x64/vncinject'
69+
end
70+
71+
let(:type) do
72+
'payload'
73+
end
74+
75+
it 'should not load the module' do
76+
subject.load_module(parent_path, type, module_reference_name).should be_false
77+
end
78+
end
79+
80+
# Non-payloads are defined as ruby Classes
81+
context 'without payload' do
82+
let(:reference_name) do
83+
'windows/smb/ms08_067_netapi'
84+
end
85+
86+
let(:type) do
87+
'exploit'
88+
end
89+
90+
it 'should not load the module' do
91+
subject.load_module(parent_path, type, module_reference_name).should be_false
92+
end
6793
end
6894
end
6995
end

spec/support/shared/examples/msf/module_manager/cache.rb

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,20 @@
6060

6161
context '#cache_in_memory' do
6262
def cache_in_memory
63-
module_manager.cache_in_memory(klass)
63+
module_manager.cache_in_memory(
64+
class_or_module,
65+
:path => path,
66+
:reference_name => reference_name,
67+
:type => type
68+
)
6469
end
6570

6671
def module_info_by_path
6772
module_manager.send(:module_info_by_path)
6873
end
6974

70-
let(:klass) do
71-
mock(
72-
'Class<Msf::Module>',
73-
:file_path => path,
74-
:parent => namespace_module,
75-
:refname => reference_name,
76-
:type => type
77-
)
75+
let(:class_or_module) do
76+
mock('Class<Msf::Module> or Module', :parent => namespace_module)
7877
end
7978

8079
let(:namespace_module) do
@@ -96,29 +95,29 @@ def module_info_by_path
9695
cache_in_memory
9796
end
9897

99-
it 'should have entry for file_path' do
100-
module_info_by_path[klass.file_path].should be_a Hash
98+
it 'should have entry for path' do
99+
module_info_by_path[path].should be_a Hash
101100
end
102101

103102
context 'value' do
104103
subject(:value) do
105-
module_info_by_path[klass.file_path]
104+
module_info_by_path[path]
106105
end
107106

108-
it 'should have modification time of file_path for :modification_time' do
107+
it 'should have modification time of :path option for :modification_time' do
109108
value[:modification_time].should == pathname_modification_time
110109
end
111110

112111
it 'should have parent path from namespace module for :parent_path' do
113112
value[:parent_path].should == namespace_module.parent_path
114113
end
115114

116-
it 'should have refname for :reference_name' do
117-
value[:reference_name].should == klass.refname
115+
it 'should use :reference_name option' do
116+
value[:reference_name].should == reference_name
118117
end
119118

120-
it 'should have type for :type' do
121-
value[:type].should == klass.type
119+
it 'should use :type option' do
120+
value[:type].should == type
122121
end
123122
end
124123
end

spec/support/shared/examples/msf/module_manager/loading.rb

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,10 @@
7878
end
7979

8080
context '#on_module_load' do
81-
def module_info_by_path
82-
module_manager.send(:module_info_by_path)
83-
end
84-
85-
def on_module_load
81+
def on_module_load
8682
module_manager.on_module_load(klass, type, reference_name, options)
8783
end
8884

89-
let(:annotated_class) do
90-
# stub out include? so calls in auto_subscribe_module work.
91-
mock(
92-
'Annotated Class',
93-
:file_path => path,
94-
:include? => false,
95-
:parent => namespace_module,
96-
:refname => reference_name,
97-
:type => type
98-
)
99-
end
100-
10185
let(:klass) do
10286
Class.new(Msf::Auxiliary)
10387
end
@@ -149,7 +133,7 @@ def on_module_load
149133
klass,
150134
reference_name,
151135
options
152-
).and_return(annotated_class)
136+
)
153137

154138
on_module_load
155139
end
@@ -160,25 +144,19 @@ def on_module_load
160144
on_module_load
161145
end
162146

163-
context 'annotated class' do
164-
before(:each) do
165-
module_set.stub(:add_module).and_return(annotated_class)
166-
end
167-
168-
it 'should pass annotated class to Msf::ModuleManager#auto_subscribe_module' do
169-
module_manager.should_receive(:auto_subscribe_module).with(annotated_class)
147+
it 'should pass class to #auto_subscribe_module' do
148+
module_manager.should_receive(:auto_subscribe_module).with(klass)
170149

171-
on_module_load
172-
end
150+
on_module_load
151+
end
173152

174-
it 'should fire on_module_load event' do
175-
framework.events.should_receive(:on_module_load).with(
176-
reference_name,
177-
annotated_class
178-
)
153+
it 'should fire on_module_load event with class' do
154+
framework.events.should_receive(:on_module_load).with(
155+
reference_name,
156+
klass
157+
)
179158

180-
on_module_load
181-
end
159+
on_module_load
182160
end
183161
end
184162
end

0 commit comments

Comments
 (0)