Skip to content

Commit c6b1955

Browse files
committed
Land rapid7#6729, Speed up the datastore
2 parents 4fb7472 + 3da4517 commit c6b1955

File tree

3 files changed

+49
-68
lines changed

3 files changed

+49
-68
lines changed

lib/msf/core/data_store.rb

Lines changed: 46 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# -*- coding: binary -*-
2+
3+
require 'set'
4+
25
module Msf
36

47
###
@@ -13,17 +16,19 @@ class DataStore < Hash
1316
# Initializes the data store's internal state.
1417
#
1518
def initialize()
16-
@options = Hash.new
17-
@imported = Hash.new
18-
@imported_by = Hash.new
19+
@options = Hash.new
20+
@imported = Hash.new
21+
@imported_by = Hash.new
22+
@original_keys = Set.new
1923
end
2024

2125
#
2226
# Clears the imported flag for the supplied key since it's being set
2327
# directly.
2428
#
2529
def []=(k, v)
26-
k = find_key_case(k)
30+
add_key(k)
31+
k = k.downcase
2732
@imported[k] = false
2833
@imported_by[k] = nil
2934

@@ -44,31 +49,32 @@ def []=(k, v)
4449
# Case-insensitive wrapper around hash lookup
4550
#
4651
def [](k)
47-
super(find_key_case(k))
52+
super(k.downcase)
4853
end
4954

5055
#
5156
# Case-insensitive wrapper around store
5257
#
5358
def store(k,v)
54-
super(find_key_case(k), v)
59+
add_key(k)
60+
super(k.downcase, v)
5561
end
5662

5763
#
5864
# Case-insensitive wrapper around delete
5965
#
6066
def delete(k)
61-
super(find_key_case(k))
67+
super(k.downcase)
6268
end
6369

64-
65-
#
66-
# Updates a value in the datastore with the specified name, k, to the
67-
# specified value, v. This update does not alter the imported status of
68-
# the value.
69-
#
70-
def update_value(k, v)
71-
self.store(k, v)
70+
# Override Hash's to_h method so we can include the original case of each key
71+
# (failing to do this breaks a number of places in framework and pro that use
72+
# serialized datastores)
73+
def to_h
74+
@original_keys.reduce({}) do |acc, key|
75+
acc[key] = self[key]
76+
acc
77+
end
7278
end
7379

7480
#
@@ -128,15 +134,16 @@ def import_options_from_s(option_str, delim = nil)
128134
# Imports options from a hash and stores them in the datastore.
129135
#
130136
def import_options_from_hash(option_hash, imported = true, imported_by = nil)
131-
option_hash.each_pair { |key, val|
137+
option_hash.each_pair do |key, val|
132138
import_option(key, val, imported, imported_by)
133-
}
139+
end
134140
end
135141

136142
def import_option(key, val, imported=true, imported_by=nil, option=nil)
137143
self.store(key, val)
138144

139-
@options[key] = option
145+
key = key.downcase
146+
@options[key] = option
140147
@imported[key] = imported
141148
@imported_by[key] = imported_by
142149
end
@@ -145,21 +152,9 @@ def import_option(key, val, imported=true, imported_by=nil, option=nil)
145152
# Serializes the options in the datastore to a string.
146153
#
147154
def to_s(delim = ' ')
148-
str = ''
149-
150-
keys.sort.each { |key|
151-
str << "#{key}=#{self[key]}" + ((str.length) ? delim : '')
152-
}
153-
154-
return str
155-
end
156-
157-
def to_h
158-
datastore_hash = {}
159-
self.keys.each do |k|
160-
datastore_hash[k.to_s] = self[k].to_s
155+
@original_keys.reduce('') do |acc, key|
156+
acc << "#{key}=#{self[key]}#{delim}"
161157
end
162-
datastore_hash
163158
end
164159

165160
#
@@ -199,9 +194,10 @@ def from_file(path, name = 'global')
199194
# not include default option values.
200195
#
201196
def user_defined
202-
reject { |k, v|
203-
@imported[k] == true
204-
}
197+
@original_keys.reduce({}) do |acc, k|
198+
acc[k] = self[k] unless @imported[k.downcase]
199+
acc
200+
end
205201
end
206202

207203
#
@@ -222,40 +218,26 @@ def clear_non_user_defined
222218
# Completely clear all values in the hash
223219
#
224220
def clear
225-
self.keys.each {|k| self.delete(k) }
226-
self
221+
@options.clear
222+
@imported.clear
223+
@imported_by.clear
224+
@original_keys.clear
225+
super
227226
end
228227

229-
#
230-
# Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
231-
# "can't add a new key into hash during iteration"
232-
#
228+
# Yield the original-cased key
233229
def each(&block)
234-
list = []
235-
self.keys.sort.each do |sidx|
236-
list << [sidx, self[sidx]]
230+
@original_keys.each do |key|
231+
block.call(key, self[key])
237232
end
238-
list.each(&block)
239233
end
240234

241-
protected
242-
243-
#
244-
# Case-insensitive key lookup
245-
#
246-
def find_key_case(k)
235+
protected
247236

248-
# Scan each key looking for a match
249-
self.each_key do |rk|
250-
if (rk.downcase == k.downcase)
251-
return rk
252-
end
253-
end
254-
255-
# Fall through to the non-existent value
256-
return k
237+
# Keep track of the original, case-sensitive key
238+
def add_key(k)
239+
@original_keys.add(k) unless include? k.downcase
257240
end
258-
259241
end
260242

261243
###
@@ -278,7 +260,7 @@ def initialize(m)
278260
# if we can't directly find it
279261
#
280262
def fetch(key)
281-
key = find_key_case(key)
263+
key = key.downcase
282264
val = nil
283265
val = super if(@imported_by[key] != 'self')
284266
if (val.nil? and @_module and @_module.framework)
@@ -292,7 +274,7 @@ def fetch(key)
292274
# Same as fetch
293275
#
294276
def [](key)
295-
key = find_key_case(key)
277+
key = key.downcase
296278
val = nil
297279
val = super if(@imported_by[key] != 'self')
298280
if (val.nil? and @_module and @_module.framework)
@@ -315,11 +297,10 @@ def default?(key)
315297
def copy
316298
clone = self.class.new(@_module)
317299
self.keys.each do |k|
318-
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
300+
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k.downcase], @imported_by[k.downcase])
319301
end
320302
clone
321303
end
322304
end
323305

324306
end
325-

lib/msf/core/exploit/powershell.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def make_subs(script, subs)
5858
# @return [String] Encoded script
5959
def encode_script(script_in, eof = nil)
6060
opts = {}
61-
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
61+
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k|
6262
mod_method = k.split('::').last.intern
6363
opts[mod_method.to_sym] = true
6464
end
@@ -76,7 +76,7 @@ def encode_script(script_in, eof = nil)
7676
# @return [String] Compressed script with decompression stub
7777
def compress_script(script_in, eof=nil)
7878
opts = {}
79-
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
79+
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k|
8080
mod_method = k.split('::').last.intern
8181
opts[mod_method.to_sym] = true
8282
end

modules/post/multi/recon/local_exploit_suggester.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def is_session_type_compat?(mod)
8888

8989

9090
def set_module_options(mod)
91-
self.datastore.each_pair do |k,v|
91+
self.datastore.each do |k,v|
9292
mod.datastore[k] = v
9393
end
9494
if !mod.datastore['SESSION'] && session.present?

0 commit comments

Comments
 (0)