Merge pull request #643 from antisnatchor/master
RESTful API, from antisnatchor with love :D
This commit is contained in:
1
Gemfile
1
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"
|
||||
|
||||
3
beef
3
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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,4 +39,10 @@ require 'core/module'
|
||||
require 'core/modules'
|
||||
require 'core/extension'
|
||||
require 'core/extensions'
|
||||
require 'core/hbmanager'
|
||||
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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,8 +28,6 @@ module Models
|
||||
property :path, Text, :lazy => false
|
||||
|
||||
has n, :commands
|
||||
has 1, :dynamic_command_info
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
44
core/main/rest/api.rb
Normal file
44
core/main/rest/api.rb
Normal file
@@ -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
|
||||
77
core/main/rest/handlers/hookedbrowsers.rb
Normal file
77
core/main/rest/handlers/hookedbrowsers.rb
Normal file
@@ -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
|
||||
74
core/main/rest/handlers/logs.rb
Normal file
74
core/main/rest/handlers/logs.rb
Normal file
@@ -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
|
||||
147
core/main/rest/handlers/modules.rb
Normal file
147
core/main/rest/handlers/modules.rb
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user