Skip to content

Commit 57576de

Browse files
committed
Update in-memory cache to fix file_changed?
[#47720609] Msf::ModuleManager#module_info_by_path was not being updated when a module was loaded, so if a load_module was called again, say during start up of prosvc, the module would reload even though there was no change in the file because file_changed? couldn't find an entry for the module's path in module_info_by_path.
1 parent eede805 commit 57576de

File tree

5 files changed

+154
-48
lines changed

5 files changed

+154
-48
lines changed

lib/msf/core/module_manager/cache.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ def cache_empty?
1515
module_info_by_path.empty?
1616
end
1717

18+
# Updates the in-memory cache so that {#file_changed?} will report +false+ if
19+
# the module is loaded again.
20+
#
21+
# @param klass [Class<Msf::Module>] a module class
22+
# @return [void]
23+
def cache_in_memory(klass)
24+
modification_time = File.mtime(klass.file_path)
25+
parent_path = klass.parent.parent_path
26+
27+
module_info_by_path[klass.file_path] = {
28+
:modification_time => modification_time,
29+
:parent_path => parent_path,
30+
:reference_name => klass.refname,
31+
:type => klass.type
32+
}
33+
end
34+
1835
# Forces loading of the module with the given type and module reference name from the cache.
1936
#
2037
# @param [String] type the type of the module.

lib/msf/core/module_manager/loading.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def on_module_load(klass, type, reference_name, info={})
7070
module_set = module_set_by_type[type]
7171
annotated_klass = module_set.add_module(klass, reference_name, info)
7272

73+
cache_in_memory(annotated_klass)
74+
7375
# Automatically subscribe a wrapper around this module to the necessary
7476
# event providers based on whatever events it wishes to receive.
7577
auto_subscribe_module(annotated_klass)

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@
5656

5757
created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption'
5858
end
59+
60+
context 'with module previously loaded' do
61+
before(:each) do
62+
subject.load_module(parent_path, type, module_reference_name)
63+
end
64+
65+
it 'should not load the module' do
66+
subject.load_module(parent_path, type, module_reference_name).should be_false
67+
end
68+
end
5969
end
6070

6171
context 'without existent module_path' do

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

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
11
shared_examples_for 'Msf::ModuleManager::Cache' do
2+
let(:parent_path) do
3+
parent_pathname.to_path
4+
end
5+
6+
let(:parent_pathname) do
7+
Metasploit::Framework.root.join('modules')
8+
end
9+
10+
let(:reference_name) do
11+
'windows/smb/ms08_067_netapi'
12+
end
13+
14+
let(:type) do
15+
'exploit'
16+
end
17+
18+
let(:path) do
19+
pathname.to_path
20+
end
21+
22+
let(:pathname) do
23+
parent_pathname.join(
24+
'exploits',
25+
"#{reference_name}.rb"
26+
)
27+
end
28+
29+
let(:pathname_modification_time) do
30+
pathname.mtime
31+
end
32+
233
context '#cache_empty?' do
334
subject(:cache_empty?) do
435
module_manager.cache_empty?
@@ -27,19 +58,73 @@
2758
end
2859
end
2960

30-
context '#load_cached_module' do
31-
let(:parent_path) do
32-
Metasploit::Framework.root.join('modules').to_path
61+
context '#cache_in_memory' do
62+
def cache_in_memory
63+
module_manager.cache_in_memory(klass)
3364
end
3465

35-
let(:reference_name) do
36-
'windows/smb/ms08_067_netapi'
66+
def module_info_by_path
67+
module_manager.send(:module_info_by_path)
68+
end
69+
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+
)
3778
end
3879

39-
let(:type) do
40-
'exploit'
80+
let(:namespace_module) do
81+
mock('Msf::Modules::Namespace', :parent_path => parent_path)
4182
end
4283

84+
it 'should update module_info_by_path' do
85+
expect {
86+
cache_in_memory
87+
}.to change { module_info_by_path }
88+
end
89+
90+
context 'module_info_by_path' do
91+
subject(:module_info_by_path) do
92+
module_manager.send(:module_info_by_path)
93+
end
94+
95+
before(:each) do
96+
cache_in_memory
97+
end
98+
99+
it 'should have entry for file_path' do
100+
module_info_by_path[klass.file_path].should be_a Hash
101+
end
102+
103+
context 'value' do
104+
subject(:value) do
105+
module_info_by_path[klass.file_path]
106+
end
107+
108+
it 'should have modification time of file_path for :modification_time' do
109+
value[:modification_time].should == pathname_modification_time
110+
end
111+
112+
it 'should have parent path from namespace module for :parent_path' do
113+
value[:parent_path].should == namespace_module.parent_path
114+
end
115+
116+
it 'should have refname for :reference_name' do
117+
value[:reference_name].should == klass.refname
118+
end
119+
120+
it 'should have type for :type' do
121+
value[:type].should == klass.type
122+
end
123+
end
124+
end
125+
end
126+
127+
context '#load_cached_module' do
43128
subject(:load_cached_module) do
44129
module_manager.load_cached_module(type, reference_name)
45130
end
@@ -284,39 +369,8 @@ def module_info_by_path_from_database!
284369
end
285370

286371
context 'with database cache' do
287-
let(:parent_path) do
288-
parent_pathname.to_path
289-
end
290-
291-
let(:parent_pathname) do
292-
Metasploit::Framework.root.join('modules')
293-
end
294-
295-
let(:path) do
296-
pathname.to_path
297-
end
298-
299-
let(:pathname) do
300-
parent_pathname.join(
301-
'exploits',
302-
"#{reference_name}.rb"
303-
)
304-
end
305-
306-
let(:pathname_modification_time) do
307-
pathname.mtime
308-
end
309-
310-
let(:type) do
311-
'exploit'
312-
end
313-
314-
let(:reference_name) do
315-
'windows/smb/ms08_067_netapi'
316-
end
317-
318372
#
319-
# Let!s (let + before(:each))
373+
# Let!s (let + before(:each))
320374
#
321375

322376
let!(:mdm_module_detail) do

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,24 @@
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+
8185
def on_module_load
8286
module_manager.on_module_load(klass, type, reference_name, options)
8387
end
8488

8589
let(:annotated_class) do
8690
# stub out include? so calls in auto_subscribe_module work.
87-
mock('Annotated Class', :include? => false)
91+
mock(
92+
'Annotated Class',
93+
:file_path => path,
94+
:include? => false,
95+
:parent => namespace_module,
96+
:refname => reference_name,
97+
:type => type
98+
)
8899
end
89100

90101
let(:klass) do
@@ -95,6 +106,10 @@ def on_module_load
95106
module_manager.module_set(type)
96107
end
97108

109+
let(:namespace_module) do
110+
mock('Namespace Module', :parent_path => parent_path)
111+
end
112+
98113
let(:options) do
99114
{
100115
'files' => [
@@ -107,24 +122,28 @@ def on_module_load
107122
}
108123
end
109124

125+
let(:parent_path) do
126+
Metasploit::Framework.root.join('modules')
127+
end
128+
110129
let(:path) do
111130
type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type]
112131

113-
Metasploit::Framework.root.join(
114-
'modules',
115-
type_directory,
116-
"#{reference_name}.rb"
117-
).to_path
132+
File.join(parent_path, type_directory, "#{reference_name}.rb")
118133
end
119134

120135
let(:reference_name) do
121-
FactoryGirl.generate :mdm_module_detail_refname
136+
'admin/2wire/xslt_password_reset'
122137
end
123138

124139
let(:type) do
125140
klass.type
126141
end
127142

143+
before(:each) do
144+
klass.stub(:parent => namespace_module)
145+
end
146+
128147
it "should add module to type's module_set" do
129148
module_set.should_receive(:add_module).with(
130149
klass,
@@ -135,6 +154,12 @@ def on_module_load
135154
on_module_load
136155
end
137156

157+
it 'should call cache_in_memory' do
158+
module_manager.should_receive(:cache_in_memory)
159+
160+
on_module_load
161+
end
162+
138163
context 'annotated class' do
139164
before(:each) do
140165
module_set.stub(:add_module).and_return(annotated_class)
@@ -155,7 +180,5 @@ def on_module_load
155180
on_module_load
156181
end
157182
end
158-
159-
160183
end
161184
end

0 commit comments

Comments
 (0)