Skip to content

Commit a5665d5

Browse files
committed
Land rapid7#7766, Add Automatic Targeting to all Exploits
2 parents 19319f1 + dcd7ba1 commit a5665d5

File tree

10 files changed

+705
-7
lines changed

10 files changed

+705
-7
lines changed

lib/msf/core/db_manager/host.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ def report_host(opts)
181181
opts[:name] = opts[:name][0,255]
182182
end
183183

184+
if opts[:os_name]
185+
os_name, os_flavor = split_windows_os_name(opts[:os_name])
186+
opts[:os_name] = os_name if os_name.present?
187+
if opts[:os_flavor].present?
188+
opts[:os_flavor] = os_flavor + opts[:os_flavor]
189+
else
190+
opts[:os_flavor] = os_flavor
191+
end
192+
end
193+
184194
opts.each { |k,v|
185195
if (host.attribute_names.include?(k.to_s))
186196
unless host.attribute_locked?(k.to_s)
@@ -213,6 +223,13 @@ def report_host(opts)
213223
}
214224
end
215225

226+
def split_windows_os_name(os_name)
227+
return [] if os_name.nil?
228+
flavor_match = os_name.match(/Windows\s+(.*)/)
229+
return [] if flavor_match.nil?
230+
["Windows", flavor_match.captures.first]
231+
end
232+
216233
#
217234
# Update a host's attributes via semi-standardized sysinfo hash (Meterpreter)
218235
#
@@ -273,7 +290,8 @@ def update_host_via_sysinfo(opts)
273290
end
274291

275292
if info['OS'] =~ /^Windows\s*([^\(]+)\(([^\)]+)\)/i
276-
res[:os_name] = "Windows #{$1.strip}"
293+
res[:os_name] = "Windows"
294+
res[:os_flavor] = $1.strip
277295
build = $2.strip
278296

279297
if build =~ /Service Pack (\d+)/

lib/msf/core/exploit.rb

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ module Stance
162162
#
163163
###
164164
class Remote < Exploit
165+
require 'msf/core/exploit/auto_target'
166+
include Msf::Exploit::AutoTarget
165167

166168
#
167169
# Initializes the socket array.
@@ -285,9 +287,22 @@ def initialize(info = {})
285287
# to the information hash.
286288
super(info)
287289

290+
# Skip this whole routine if there are no targets
291+
unless info['Targets'].nil?
292+
# Add an Automatic Target to the Exploit if it doesn't have one
293+
unless has_auto_target?(info['Targets'])
294+
# Don't add the automatic target unless there's already more than one target to pick from
295+
if info['Targets'].count > 1
296+
auto = ["Automatic", { 'AutoGenerated' => true}]
297+
info['Targets'].unshift(auto)
298+
end
299+
end
300+
end
301+
302+
288303
self.targets = Rex::Transformer.transform(info['Targets'], Array,
289304
[ Target ], 'Targets')
290-
self.default_target = info['DefaultTarget']
305+
self.default_target = info['DefaultTarget'] || 0
291306
self.payload_info = info['Payload'] || {}
292307
self.successful = false
293308
self.session_count = 0
@@ -321,6 +336,14 @@ def initialize(info = {})
321336
], Msf::Exploit)
322337
end
323338

339+
def has_auto_target?(targets=[])
340+
target_names = targets.collect { |target| target.first}
341+
target_names.each do |target|
342+
return true if target =~ /Automatic/
343+
end
344+
return false
345+
end
346+
324347
##
325348
#
326349
# Core exploit interface
@@ -665,8 +688,21 @@ def passive?
665688
# default target, that one will be automatically used.
666689
#
667690
def target
668-
target_idx = target_index
691+
if self.respond_to?(:auto_targeted_index)
692+
if auto_target?
693+
auto_idx = auto_targeted_index
694+
if auto_idx.present?
695+
datastore['TARGET'] = auto_idx
696+
else
697+
# If our inserted Automatic Target was selected but we failed to
698+
# find a suitable target, we just grab the original first target.
699+
datastore['TARGET'] = 1
700+
end
701+
end
669702

703+
end
704+
705+
target_idx = target_index
670706
return (target_idx) ? targets[target_idx.to_i] : nil
671707
end
672708

@@ -676,9 +712,10 @@ def target
676712
def target_index
677713
target_idx = datastore['TARGET']
678714

715+
default_idx = default_target || 0
679716
# Use the default target if one was not supplied.
680-
if (target_idx == nil and default_target and default_target >= 0)
681-
target_idx = default_target
717+
if (target_idx == nil and default_idx and default_idx >= 0)
718+
target_idx = default_idx
682719
end
683720
return (target_idx) ? target_idx.to_i : nil
684721
end

lib/msf/core/exploit/auto_target.rb

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
2+
module Msf
3+
module Exploit::AutoTarget
4+
5+
# Checks to see if the auto-generated Automatic Targeting
6+
# has been selected. If the module had an already defined
7+
# Automatic target, then we let the module handle the targeting
8+
# itself.
9+
#
10+
# @return [Boolean] whether or not to use our automatic targeting routine
11+
def auto_target?
12+
selected_target = targets[target_index]
13+
return false if selected_target.nil?
14+
if selected_target.name =~ /Automatic/ && selected_target['AutoGenerated'] == true
15+
true
16+
else
17+
false
18+
end
19+
end
20+
21+
# Returns the Target Index of the automatically selected Target from
22+
# our Automatic Targeting routine.
23+
#
24+
# @return [Integer] the index of the selected Target
25+
# @return [nil] if no target could be selected
26+
def auto_targeted_index
27+
selected_target = select_target
28+
return nil if selected_target.nil?
29+
targets.each_with_index do |target, index|
30+
return index if target == selected_target
31+
end
32+
nil
33+
end
34+
35+
# Chooses the best possible Target for what we know about
36+
# the targeted host.
37+
#
38+
# @return [Msf::Module::Target] the Target that our automatic routine selected
39+
def select_target
40+
return nil unless auto_target?
41+
host_record = target_host
42+
return nil if host_record.nil?
43+
filtered_targets = filter_by_os(host_record)
44+
filtered_targets.first
45+
end
46+
47+
# Finds an <Mdm::Host> for the RHOST if one exists
48+
#
49+
# @return [Mdm:Host] the Host record if one exists
50+
# @return [nil] if no Host record is present, or the DB is not active
51+
def target_host
52+
return nil unless self.respond_to?(:rhost)
53+
return nil unless framework.db.active
54+
current_workspace = framework.db.find_workspace(self.workspace)
55+
current_workspace.hosts.where(address: rhost).first
56+
end
57+
58+
# Returns the best matching Targets based on the target host's
59+
# OS information. It looks at the OS Family, OS Name, and OS SP.
60+
#
61+
# @param host_record [Mdm::Host] the target host record
62+
# @return [Array<Msf::Module::Target>] an array of matching targets
63+
def filter_by_os(host_record)
64+
filtered_by_family = filter_by_os_family(host_record)
65+
filtered_by_name = filter_by_os_name(filtered_by_family, host_record)
66+
# If Filtering by name gave us no results, then we reset back to the family filter group
67+
filtered_by_name = filtered_by_family if filtered_by_name.empty?
68+
filtered_by_sp = filter_by_os_sp(filtered_by_name,host_record)
69+
# If Filtering by SP was a bust, revert back one level
70+
filtered_by_sp = filtered_by_name if filtered_by_sp.empty?
71+
filtered_by_sp
72+
end
73+
74+
# Returns all Targets that match the target host's OS Family
75+
# e.g Windows, Linux, OS X, etc
76+
#
77+
# @param host_record [Mdm::Host] the target host record
78+
# @return [Array<Msf::Module::Target>] an array of matching targets
79+
def filter_by_os_family(host_record)
80+
return [] if host_record.os_family.blank?
81+
filtered_targets = targets.collect do |target|
82+
if target.name =~ /#{host_record.os_family}/
83+
target
84+
else
85+
nil
86+
end
87+
end
88+
filtered_targets.compact
89+
end
90+
91+
# Returns all Targets that match the target host's OS Name
92+
# e.g Windows 7, Windows XP, Windows Vista, etc
93+
#
94+
# @param potential_targets [Array<Msf::Module::Target>] the filtered targets that we wish to filter further
95+
# @param host_record [Mdm::Host] the target host record
96+
# @return [Array<Msf::Module::Target>] an array of matching targets
97+
def filter_by_os_name(potential_targets, host_record)
98+
return [] if host_record.os_name.blank?
99+
filtered_targets = []
100+
potential_targets.each do |target|
101+
filtered_targets << target if target.name =~ /#{host_record.os_name}/
102+
end
103+
filtered_targets
104+
end
105+
106+
# Returns all Targets that match the target host's OS SP
107+
#
108+
# @param potential_targets [Array<Msf::Module::Target>] the filtered targets that we wish to filter further
109+
# @param host_record [Mdm::Host] the target host record
110+
# @return [Array<Msf::Module::Target>] an array of matching targets
111+
def filter_by_os_sp(potential_targets, host_record)
112+
return [] if host_record.os_sp.blank?
113+
filtered_targets = []
114+
potential_targets.each do |target|
115+
filtered_targets << target if target.name =~ /#{host_record.os_sp}/
116+
end
117+
filtered_targets
118+
end
119+
end
120+
end

lib/msf/core/exploit/remote/firefox_privilege_escalation.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ def run_payload
151151

152152
# @return [Boolean] the user has selected a javascript (non-native) target
153153
def js_target?
154-
target.arch[0] == ARCH_FIREFOX
154+
if target.arch
155+
target.arch[0] == ARCH_FIREFOX
156+
else
157+
false
158+
end
155159
end
156160

157161
end

0 commit comments

Comments
 (0)