Add metasploit extension REST API /msf
This commit is contained in:
@@ -11,6 +11,7 @@ module BeEF
|
|||||||
module MetasploitHooks
|
module MetasploitHooks
|
||||||
|
|
||||||
BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Modules, 'post_soft_load')
|
BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Modules, 'post_soft_load')
|
||||||
|
BeEF::API::Registrar.instance.register(BeEF::Extension::Metasploit::API::MetasploitHooks, BeEF::API::Server, 'mount_handler')
|
||||||
|
|
||||||
# Load modules from metasploit just after all other module config is loaded
|
# Load modules from metasploit just after all other module config is loaded
|
||||||
def self.post_soft_load
|
def self.post_soft_load
|
||||||
@@ -18,7 +19,7 @@ module BeEF
|
|||||||
if msf.login
|
if msf.login
|
||||||
msf_module_config = {}
|
msf_module_config = {}
|
||||||
path = BeEF::Core::Configuration.instance.get('beef.extension.metasploit.path')
|
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")
|
if !BeEF::Core::Console::CommandLine.parse[:resetdb] && File.exists?("#{path}msf-exploits.cache")
|
||||||
print_debug "Attempting to use Metasploit exploits cache file"
|
print_debug "Attempting to use Metasploit exploits cache file"
|
||||||
raw = File.read("#{path}msf-exploits.cache")
|
raw = File.read("#{path}msf-exploits.cache")
|
||||||
begin
|
begin
|
||||||
@@ -39,7 +40,7 @@ module BeEF
|
|||||||
msf_modules = msf.call('module.exploits')
|
msf_modules = msf.call('module.exploits')
|
||||||
count = 1
|
count = 1
|
||||||
msf_modules['modules'].each { |m|
|
msf_modules['modules'].each { |m|
|
||||||
next if not m.include? "/browser/"
|
next if !m.include? "/browser/"
|
||||||
m_details = msf.call('module.info', 'exploit', m)
|
m_details = msf.call('module.info', 'exploit', m)
|
||||||
if m_details
|
if m_details
|
||||||
key = 'msf_'+m.split('/').last
|
key = 'msf_'+m.split('/').last
|
||||||
@@ -70,16 +71,16 @@ module BeEF
|
|||||||
|
|
||||||
|
|
||||||
msf_module_config[key] = {
|
msf_module_config[key] = {
|
||||||
'enable'=> true,
|
'enable' => true,
|
||||||
'msf'=> true,
|
'msf' => true,
|
||||||
'msf_key' => m,
|
'msf_key' => m,
|
||||||
'name'=> m_details['name'],
|
'name' => m_details['name'],
|
||||||
'category' => 'Metasploit',
|
'category' => 'Metasploit',
|
||||||
'description'=> m_details['description'],
|
'description'=> m_details['description'],
|
||||||
'authors'=> m_details['references'],
|
'authors' => m_details['references'],
|
||||||
'path'=> path,
|
'path' => path,
|
||||||
'class'=> 'Msf_module',
|
'class' => 'Msf_module',
|
||||||
'target'=> target_browser
|
'target' => target_browser
|
||||||
}
|
}
|
||||||
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_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, 'get_payload_options', [key, nil])
|
||||||
@@ -102,12 +103,17 @@ module BeEF
|
|||||||
def self.get_options(mod)
|
def self.get_options(mod)
|
||||||
msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key")
|
msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key")
|
||||||
msf = BeEF::Extension::Metasploit::RpcClient.instance
|
msf = BeEF::Extension::Metasploit::RpcClient.instance
|
||||||
if msf_key != nil and msf.login
|
if msf_key != nil && msf.login
|
||||||
msf_module_options = msf.call('module.options', 'exploit', msf_key)
|
msf_module_options = msf.call('module.options', 'exploit', msf_key)
|
||||||
com = BeEF::Core::Models::CommandModule.first(:name => mod)
|
com = BeEF::Core::Models::CommandModule.first(:name => mod)
|
||||||
if msf_module_options
|
if msf_module_options
|
||||||
options = BeEF::Extension::Metasploit.translate_options(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)
|
msf_payload_options = msf.call('module.compatible_payloads', msf_key)
|
||||||
if msf_payload_options
|
if msf_payload_options
|
||||||
options << BeEF::Extension::Metasploit.translate_payload(msf_payload_options)
|
options << BeEF::Extension::Metasploit.translate_payload(msf_payload_options)
|
||||||
@@ -132,27 +138,26 @@ module BeEF
|
|||||||
msf_opts[opt["name"]] = opt["value"]
|
msf_opts[opt["name"]] = opt["value"]
|
||||||
}
|
}
|
||||||
|
|
||||||
if msf_key != nil and msf.login
|
if msf_key != nil && msf.login
|
||||||
# Are the options correctly formatted for msf?
|
# Are the options correctly formatted for msf?
|
||||||
# This call has not been tested
|
# This call has not been tested
|
||||||
msf.call('module.execute', 'exploit', msf_key, msf_opts)
|
msf.call('module.execute', 'exploit', msf_key, msf_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
hb = BeEF::HBManager.get_by_session(hbsession)
|
hb = BeEF::HBManager.get_by_session(hbsession)
|
||||||
if not hb
|
if !hb
|
||||||
print_error "Could not find hooked browser when attempting to execute module '#{mod}'"
|
print_error "Could not find hooked browser when attempting to execute module '#{mod}'"
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
bopts = []
|
bopts = []
|
||||||
uri = ""
|
|
||||||
if msf_opts['SSL']
|
if msf_opts['SSL']
|
||||||
uri += "https://"
|
proto = 'https'
|
||||||
else
|
else
|
||||||
uri += "http://"
|
proto = 'http'
|
||||||
end
|
end
|
||||||
config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit')
|
config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit')
|
||||||
uri += config['callback_host'] + ":" + msf_opts['SRVPORT'] + "/" + msf_opts['URIPATH']
|
uri = proto + '://' + config['callback_host'] + ":" + msf_opts['SRVPORT'] + "/" + msf_opts['URIPATH']
|
||||||
|
|
||||||
|
|
||||||
bopts << {:sploit_url => uri}
|
bopts << {:sploit_url => uri}
|
||||||
@@ -172,7 +177,7 @@ module BeEF
|
|||||||
msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key")
|
msf_key = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.msf_key")
|
||||||
|
|
||||||
msf = BeEF::Extension::Metasploit::RpcClient.instance
|
msf = BeEF::Extension::Metasploit::RpcClient.instance
|
||||||
if msf_key != nil and msf.login
|
if msf_key != nil && msf.login
|
||||||
msf_module_options = msf.call('module.options', 'payload', payload)
|
msf_module_options = msf.call('module.options', 'payload', payload)
|
||||||
|
|
||||||
com = BeEF::Core::Models::CommandModule.first(:name => mod)
|
com = BeEF::Core::Models::CommandModule.first(:name => mod)
|
||||||
@@ -184,6 +189,13 @@ module BeEF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Mounts the handler for processing Metasploit RESTful API requests.
|
||||||
|
#
|
||||||
|
# @param beef_server [BeEF::Core::Server] HTTP server instance
|
||||||
|
def self.mount_handler(beef_server)
|
||||||
|
beef_server.mount('/api/msf', BeEF::Extension::Metasploit::MsfRest.new)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ module Metasploit
|
|||||||
|
|
||||||
extend BeEF::API::Extension
|
extend BeEF::API::Extension
|
||||||
|
|
||||||
|
@short_name = 'msf'
|
||||||
|
@full_name = 'Metasploit'
|
||||||
|
@description = 'Metasploit integration'
|
||||||
|
|
||||||
# Translates msf exploit options to beef options array
|
# Translates msf exploit options to beef options array
|
||||||
def self.translate_options(msf_options)
|
def self.translate_options(msf_options)
|
||||||
options = []
|
options = []
|
||||||
@@ -89,3 +93,4 @@ require 'msfrpc-client'
|
|||||||
require 'extensions/metasploit/rpcclient'
|
require 'extensions/metasploit/rpcclient'
|
||||||
require 'extensions/metasploit/api'
|
require 'extensions/metasploit/api'
|
||||||
require 'extensions/metasploit/module'
|
require 'extensions/metasploit/module'
|
||||||
|
require 'extensions/metasploit/rest/msf'
|
||||||
|
|||||||
150
extensions/metasploit/rest/msf.rb
Normal file
150
extensions/metasploit/rest/msf.rb
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2006-2014 Wade Alcorn - wade@bindshell.net
|
||||||
|
# Browser Exploitation Framework (BeEF) - http://beefproject.com
|
||||||
|
# See the file 'doc/COPYING' for copying permission
|
||||||
|
#
|
||||||
|
module BeEF
|
||||||
|
module Extension
|
||||||
|
module Metasploit
|
||||||
|
|
||||||
|
# This class handles the routing of RESTful API requests for Metasploit integration
|
||||||
|
class MsfRest < BeEF::Core::Router::Router
|
||||||
|
|
||||||
|
# Filters out bad requests before performing any routing
|
||||||
|
before do
|
||||||
|
@msf ||= BeEF::Extension::Metasploit::RpcClient.instance
|
||||||
|
config = BeEF::Core::Configuration.instance
|
||||||
|
|
||||||
|
# Require a valid API token from a valid IP address
|
||||||
|
halt 401 unless params[:token] == config.get('beef.api_token')
|
||||||
|
halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip)
|
||||||
|
|
||||||
|
headers 'Content-Type' => 'application/json; charset=UTF-8',
|
||||||
|
'Pragma' => 'no-cache',
|
||||||
|
'Cache-Control' => 'no-cache',
|
||||||
|
'Expires' => '0'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns version of Metasploit
|
||||||
|
get '/version' do
|
||||||
|
begin
|
||||||
|
version = @msf.call('core.version')
|
||||||
|
result = {}
|
||||||
|
result[:version] = version
|
||||||
|
result.to_json
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving Metasploit version (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns all the jobs
|
||||||
|
get '/jobs' do
|
||||||
|
begin
|
||||||
|
jobs = @msf.call('job.list')
|
||||||
|
count = jobs.size
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result[:count] = count
|
||||||
|
result[:jobs] = jobs
|
||||||
|
result.to_json
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving Metasploit job list (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns information about a specific job given its id
|
||||||
|
get '/job/:id/info' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' if id !~ /\A\d+\Z/
|
||||||
|
job = @msf.call('job.info', id)
|
||||||
|
halt 404 if job.nil?
|
||||||
|
job.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving Metasploit job with ID #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stops a job given its id
|
||||||
|
get '/job/:id/stop' do
|
||||||
|
result = {}
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' if id !~ /\A\d+\Z/
|
||||||
|
|
||||||
|
removed = @msf.call('job.stop', id)
|
||||||
|
if !removed.nil?
|
||||||
|
result['success'] = removed
|
||||||
|
print_info "[Metasploit] Stopped job [id: #{id}]"
|
||||||
|
end
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while stopping job with ID #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
result.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
# Starts a new msf payload handler
|
||||||
|
post '/handler' do
|
||||||
|
begin
|
||||||
|
body = JSON.parse(request.body.read)
|
||||||
|
handler = @msf.call('module.execute', 'exploit', 'exploit/multi/handler', body)
|
||||||
|
result = {}
|
||||||
|
# example response: {"job_id"=>0, "uuid"=>"oye0kmpr"}
|
||||||
|
if handler.nil? || handler['job_id'].nil?
|
||||||
|
print_error "[Metasploit] Could not start payload handler"
|
||||||
|
result['success'] = false
|
||||||
|
else
|
||||||
|
print_info "[Metasploit] Started job [id: #{handler['job_id']}]"
|
||||||
|
print_debug "#{@msf.call('job.info', handler['job_id'])}"
|
||||||
|
result['success'] = true
|
||||||
|
result['id'] = handler['job_id']
|
||||||
|
end
|
||||||
|
result.to_json
|
||||||
|
rescue InvalidJsonError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while creating exploit handler (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raised when invalid JSON input is passed to an /api/msf handler.
|
||||||
|
class InvalidJsonError < StandardError
|
||||||
|
|
||||||
|
DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/msf handler'
|
||||||
|
|
||||||
|
def initialize(message = nil)
|
||||||
|
super(message || DEFAULT_MESSAGE)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raised when an invalid named parameter is passed to an /api/msf handler.
|
||||||
|
class InvalidParamError < StandardError
|
||||||
|
|
||||||
|
DEFAULT_MESSAGE = 'Invalid parameter passed to /api/msf handler'
|
||||||
|
|
||||||
|
def initialize(message = nil)
|
||||||
|
str = "Invalid \"%s\" parameter passed to /api/msf handler"
|
||||||
|
message = sprintf str, message unless message.nil?
|
||||||
|
super(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user