Update style
This commit is contained in:
@@ -6,85 +6,84 @@
|
||||
module BeEF
|
||||
module Extension
|
||||
module Metasploit
|
||||
|
||||
extend BeEF::API::Extension
|
||||
|
||||
@short_name = 'msf'
|
||||
@full_name = 'Metasploit'
|
||||
@description = 'Metasploit integration'
|
||||
@short_name = 'msf'
|
||||
@full_name = 'Metasploit'
|
||||
@description = 'Metasploit integration'
|
||||
|
||||
# Translates msf exploit options to beef options array
|
||||
def self.translate_options(msf_options)
|
||||
callback_host = BeEF::Core::Configuration.instance.get('beef.extension.metasploit.callback_host')
|
||||
|
||||
options = []
|
||||
msf_options.each{|k,v|
|
||||
next if v['advanced'] == true || v['evasion'] == true
|
||||
v['allowBlank'] = 'true' if v['required'] == false
|
||||
case v['type']
|
||||
when "string", "address", "port", "integer"
|
||||
v['type'] = 'text'
|
||||
v['value'] = rand(3**20).to_s(16) if k == 'URIPATH'
|
||||
v['value'] = v['default'] if k != "URIPATH"
|
||||
v['value'] = BeEF::Core::Configuration.instance.get('beef.extension.metasploit.callback_host') if k == "LHOST"
|
||||
msf_options.each do |k, v|
|
||||
next if v['advanced'] == true
|
||||
next if v['evasion'] == true
|
||||
|
||||
v['allowBlank'] = 'true' if v['required'] == false
|
||||
|
||||
when "bool"
|
||||
v['type'] = 'checkbox'
|
||||
when "enum"
|
||||
v['type'] = 'combobox'
|
||||
v['store_type'] = 'arraystore',
|
||||
v['store_fields'] = ['enum'],
|
||||
v['store_data'] = self.translate_enums(v['enums']),
|
||||
v['value'] = v['default']
|
||||
v['valueField'] = 'enum',
|
||||
v['displayField'] = 'enum',
|
||||
v['autoWidth'] = true,
|
||||
v['mode'] = 'local'
|
||||
case v['type']
|
||||
when 'string', 'address', 'port', 'integer'
|
||||
v['type'] = 'text'
|
||||
if k == 'URIPATH'
|
||||
v['value'] = rand(3**20).to_s(16)
|
||||
elsif k == 'LHOST'
|
||||
v['value'] = callback_host
|
||||
else
|
||||
v['value'] = v['default']
|
||||
end
|
||||
v['name'] = k
|
||||
v['label'] = k
|
||||
options << v
|
||||
}
|
||||
return options
|
||||
when 'bool'
|
||||
v['type'] = 'checkbox'
|
||||
when 'enum'
|
||||
v['type'] = 'combobox'
|
||||
v['store_type'] = 'arraystore',
|
||||
v['store_fields'] = ['enum'],
|
||||
v['store_data'] = translate_enums(v['enums']),
|
||||
v['value'] = v['default']
|
||||
v['valueField'] = 'enum',
|
||||
v['displayField'] = 'enum',
|
||||
v['autoWidth'] = true,
|
||||
v['mode'] = 'local'
|
||||
end
|
||||
v['name'] = k
|
||||
v['label'] = k
|
||||
options << v
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
# Translates msf payloads to a beef compatible drop down
|
||||
def self.translate_payload(payloads)
|
||||
if payloads.has_key?('payloads')
|
||||
values = self.translate_enums(payloads['payloads'])
|
||||
return unless payloads.key?('payloads')
|
||||
|
||||
defaultPayload = values[0]
|
||||
defaultPayload = 'generic/shell_bind_tcp' if values.include? 'generic/shell_bind_tcp'
|
||||
values = translate_enums(payloads['payloads'])
|
||||
|
||||
if values.length > 0
|
||||
return {
|
||||
'name' => 'PAYLOAD',
|
||||
'type' => 'combobox',
|
||||
'ui_label' => 'Payload',
|
||||
'store_type' => 'arraystore',
|
||||
'store_fields' => ['payload'],
|
||||
'store_data' => values,
|
||||
'valueField' => 'payload',
|
||||
'displayField' => 'payload',
|
||||
'mode' => 'local',
|
||||
'autoWidth' => true,
|
||||
'defaultPayload' => defaultPayload,
|
||||
'reloadOnChange' => true
|
||||
}
|
||||
end
|
||||
end
|
||||
return nil
|
||||
default_payload = values.include?('generic/shell_bind_tcp') ? 'generic/shell_bind_tcp' : values.first
|
||||
|
||||
return unless values.length.positive?
|
||||
|
||||
{
|
||||
'name' => 'PAYLOAD',
|
||||
'type' => 'combobox',
|
||||
'ui_label' => 'Payload',
|
||||
'store_type' => 'arraystore',
|
||||
'store_fields' => ['payload'],
|
||||
'store_data' => values,
|
||||
'valueField' => 'payload',
|
||||
'displayField' => 'payload',
|
||||
'mode' => 'local',
|
||||
'autoWidth' => true,
|
||||
'defaultPayload' => default_payload,
|
||||
'reloadOnChange' => true
|
||||
}
|
||||
end
|
||||
|
||||
# Translates metasploit enums to ExtJS combobox store_data
|
||||
def self.translate_enums(enums)
|
||||
values = []
|
||||
enums.each{|e|
|
||||
values << [e]
|
||||
}
|
||||
return values
|
||||
enums.map{|e| [e]}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,243 +6,258 @@
|
||||
module BeEF
|
||||
module Extension
|
||||
module Metasploit
|
||||
|
||||
class RpcClient < ::Msf::RPC::Client
|
||||
require 'net/http'
|
||||
|
||||
include Singleton
|
||||
class RpcClient < ::Msf::RPC::Client
|
||||
include Singleton
|
||||
|
||||
def initialize
|
||||
@config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit')
|
||||
def initialize
|
||||
@config = BeEF::Core::Configuration.instance.get('beef.extension.metasploit')
|
||||
|
||||
if not (@config.key?('host') or @config.key?('uri') or @config.key?('port') or @config.key?('user') or @config.key?('pass'))
|
||||
print_error 'There is not enough information to initalize Metasploit connectivity at this time'
|
||||
print_error 'Please check your options in config.yaml to verify that all information is present'
|
||||
BeEF::Core::Configuration.instance.set('beef.extension.metasploit.enabled', false)
|
||||
BeEF::Core::Configuration.instance.set('beef.extension.metasploit.loaded', false)
|
||||
return nil
|
||||
end
|
||||
@lock = false
|
||||
@lastauth = nil
|
||||
@unit_test = false
|
||||
opts = {
|
||||
:host => @config['host'] || '127.0.0.1',
|
||||
:port => @config['port'] || 55552,
|
||||
:uri => @config['uri'] || '/api/',
|
||||
:ssl => @config['ssl'] ,
|
||||
:ssl_version => @config['ssl_version'] ,
|
||||
:context => {}
|
||||
}
|
||||
if opts[:ssl_version] =~ /SSLv3/i
|
||||
print_warning("Warning: Connections to Metasploit RPC over SSLv3 are insecure. Use TLSv1 instead.")
|
||||
end
|
||||
#auto start msfrpcd
|
||||
if (@config['auto_msfrpcd'])
|
||||
launch_msf = ''
|
||||
@config['msf_path'].each do |path|
|
||||
if File.exist?(path['path'] + 'msfrpcd')
|
||||
launch_msf = path['path'] + 'msfrpcd'
|
||||
print_info '[Metasploit] Found msfrpcd: ' + launch_msf
|
||||
end
|
||||
end
|
||||
unless @config.key?('host') || @config.key?('uri') || @config.key?('port') ||
|
||||
@config.key?('user') || @config.key?('pass')
|
||||
print_error 'There is not enough information to initalize Metasploit connectivity at this time'
|
||||
print_error 'Please check your options in config.yaml to verify that all information is present'
|
||||
BeEF::Core::Configuration.instance.set('beef.extension.metasploit.enabled', false)
|
||||
BeEF::Core::Configuration.instance.set('beef.extension.metasploit.loaded', false)
|
||||
return
|
||||
end
|
||||
|
||||
if (launch_msf.length > 0)
|
||||
msf_url = 'https://'
|
||||
argssl = ''
|
||||
unless opts[:ssl]
|
||||
argssl = '-S'
|
||||
msf_url = 'http://'
|
||||
end
|
||||
@lock = false
|
||||
@lastauth = nil
|
||||
@unit_test = false
|
||||
@msf_path = nil
|
||||
|
||||
msf_url += opts[:host] + ':' + opts[:port].to_s() + opts[:uri]
|
||||
child = IO.popen([launch_msf, "-f", argssl, "-P" , @config['pass'], "-U" , @config['user'], "-u" , opts[:uri], "-a" , opts[:host], "-p" , opts[:port].to_s()], 'r+')
|
||||
|
||||
print_info '[Metasploit] Attempt to start msfrpcd, this may take a while. PID: ' + child.pid.to_s
|
||||
opts = {
|
||||
:host => @config['host'] || '127.0.0.1',
|
||||
:port => @config['port'] || 55552,
|
||||
:uri => @config['uri'] || '/api/',
|
||||
:ssl => @config['ssl'],
|
||||
:ssl_version => @config['ssl_version'],
|
||||
:context => {}
|
||||
}
|
||||
|
||||
# Give daemon time to launch
|
||||
# poll and giveup after timeout
|
||||
retries = @config['auto_msfrpcd_timeout']
|
||||
uri = URI(msf_url)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
if opts[:ssl_version].match?(/SSLv3/i)
|
||||
print_warning '[Metasploit] Warning: Connections to Metasploit RPC over SSLv3 are insecure. Use TLSv1 instead.'
|
||||
end
|
||||
|
||||
if opts[:ssl]
|
||||
http.use_ssl = true
|
||||
http.ssl_version = opts[:ssl_version]
|
||||
end
|
||||
if not @config['ssl_verify']
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
end
|
||||
headers = { 'Content-Type' => "binary/message-pack" }
|
||||
path = uri.path.empty? ? "/" : uri.path
|
||||
begin
|
||||
sleep 1
|
||||
code = http.head(path, headers).code.to_i
|
||||
rescue => e
|
||||
retry if (retries -= 1) > 0
|
||||
end
|
||||
else
|
||||
print_error '[Metasploit] Please add a custom path for msfrpcd to the config-file.'
|
||||
end
|
||||
end
|
||||
super(opts)
|
||||
end
|
||||
|
||||
def get_lock()
|
||||
sleep 0.2 while @lock
|
||||
@lock = true
|
||||
if @config['auto_msfrpcd']
|
||||
@config['msf_path'].each do |path|
|
||||
if File.exist? "#{path['path']}/msfrpcd"
|
||||
@msf_path = "#{path['path']}/msfrpcd"
|
||||
end
|
||||
end
|
||||
|
||||
def release_lock()
|
||||
@lock = false
|
||||
|
||||
if @msf_path.nil?
|
||||
print_error '[Metasploit] Please add a custom path for msfrpcd to the config file.'
|
||||
return
|
||||
end
|
||||
def call(meth, *args)
|
||||
ret = nil
|
||||
begin
|
||||
ret = super(meth,*args)
|
||||
rescue => e
|
||||
print_error "Metasploit: #{e}"
|
||||
return nil
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
print_info "[Metasploit] Found msfrpcd: #{@msf_path}"
|
||||
|
||||
return unless launch_msfrpcd(opts)
|
||||
end
|
||||
|
||||
super(opts)
|
||||
end
|
||||
|
||||
#
|
||||
# @note auto start msfrpcd
|
||||
#
|
||||
def launch_msfrpcd(opts)
|
||||
if opts[:ssl]
|
||||
argssl = '-S'
|
||||
proto = 'http'
|
||||
else
|
||||
argssl = ''
|
||||
proto = 'https'
|
||||
end
|
||||
|
||||
msf_url = "#{proto}://#{opts[:host]}:#{opts[:port]}#{opts[:uri]}"
|
||||
|
||||
child = IO.popen([
|
||||
@msf_path,
|
||||
'-f',
|
||||
argssl,
|
||||
'-P' , @config['pass'],
|
||||
'-U' , @config['user'],
|
||||
'-u' , opts[:uri],
|
||||
'-a' , opts[:host],
|
||||
'-p' , opts[:port].to_s
|
||||
], 'r+')
|
||||
|
||||
print_info "[Metasploit] Attempt to start msfrpcd, this may take a while. PID: #{child.pid}"
|
||||
|
||||
# Give daemon time to launch
|
||||
# poll and giveup after timeout
|
||||
retries = @config['auto_msfrpcd_timeout']
|
||||
uri = URI(msf_url)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
|
||||
if opts[:ssl]
|
||||
http.use_ssl = true
|
||||
http.ssl_version = opts[:ssl_version]
|
||||
end
|
||||
|
||||
unless @config['ssl_verify']
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
end
|
||||
|
||||
headers = { 'Content-Type' => 'binary/message-pack' }
|
||||
path = uri.path.empty? ? '/' : uri.path
|
||||
|
||||
begin
|
||||
sleep 1
|
||||
code = http.head(path, headers).code.to_i
|
||||
print_debug "[Metasploit] Success - HTTP response: #{code}"
|
||||
rescue => e
|
||||
retry if (retries -= 1).positive?
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def get_lock
|
||||
sleep 0.2 while @lock
|
||||
@lock = true
|
||||
end
|
||||
|
||||
def unit_test_init
|
||||
@unit_test = true
|
||||
end
|
||||
# login to metasploit
|
||||
def login
|
||||
get_lock()
|
||||
def release_lock
|
||||
@lock = false
|
||||
end
|
||||
|
||||
res = super(@config['user'] , @config['pass'])
|
||||
if not res
|
||||
release_lock()
|
||||
print_error 'Could not authenticate to Metasploit MSGRPC.'
|
||||
return false
|
||||
end
|
||||
if (!@lastauth)
|
||||
print_info 'Successful connection with Metasploit.' if (!@unit_test)
|
||||
print_debug "Metasploit: Received temporary token: #{self.token}"
|
||||
# Generate permanent token
|
||||
new_token = token_generate
|
||||
if new_token.nil?
|
||||
print_warning "Metasploit: Could not retrieve permanent Metasploit token. Connection to Metasploit will time out in 5 minutes."
|
||||
else
|
||||
self.token = new_token
|
||||
print_debug "Metasploit: Received permanent token: #{self.token}"
|
||||
end
|
||||
end
|
||||
@lastauth = Time.now
|
||||
def call(meth, *args)
|
||||
super(meth, *args)
|
||||
rescue => e
|
||||
print_error "[Metasploit] RPC call to '#{meth}' failed: #{e}"
|
||||
puts e.backtrace
|
||||
return
|
||||
end
|
||||
|
||||
def unit_test_init
|
||||
@unit_test = true
|
||||
end
|
||||
|
||||
# login to metasploit
|
||||
def login
|
||||
get_lock
|
||||
|
||||
res = super(@config['user'], @config['pass'])
|
||||
|
||||
unless res
|
||||
print_error '[Metasploit] Could not authenticate to Metasploit RPC sevrice.'
|
||||
return false
|
||||
end
|
||||
|
||||
unless @lastauth
|
||||
print_info '[Metasploit] Successful connection with Metasploit.' unless @unit_test
|
||||
print_debug "[Metasploit] Received temporary token: #{token}"
|
||||
|
||||
# Generate permanent token
|
||||
new_token = token_generate
|
||||
if new_token.nil?
|
||||
print_warning "[Metasploit] Could not retrieve permanent Metasploit token. Connection to Metasploit will time out in 5 minutes."
|
||||
else
|
||||
self.token = new_token
|
||||
print_debug "[Metasploit] Received permanent token: #{token}"
|
||||
end
|
||||
end
|
||||
@lastauth = Time.now
|
||||
|
||||
release_lock()
|
||||
true
|
||||
end
|
||||
true
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
# generate a permanent auth token
|
||||
def token_generate
|
||||
res = self.call('auth.token_generate')
|
||||
return if not res or not res['token']
|
||||
res['token']
|
||||
end
|
||||
# generate a permanent auth token
|
||||
def token_generate
|
||||
res = call('auth.token_generate')
|
||||
|
||||
def browser_exploits()
|
||||
|
||||
get_lock()
|
||||
res = self.call('module.exploits')
|
||||
return [] if not res or not res['modules']
|
||||
return unless res || res['token']
|
||||
|
||||
mods = res['modules']
|
||||
ret = []
|
||||
|
||||
mods.each do |m|
|
||||
ret << m if(m.include? '/browser/')
|
||||
end
|
||||
|
||||
release_lock()
|
||||
ret.sort
|
||||
end
|
||||
res['token']
|
||||
end
|
||||
|
||||
def get_exploit_info(name)
|
||||
get_lock()
|
||||
res = self.call('module.info','exploit',name)
|
||||
release_lock()
|
||||
res || {}
|
||||
end
|
||||
|
||||
def get_payloads(name)
|
||||
get_lock()
|
||||
res = self.call('module.compatible_payloads',name)
|
||||
release_lock()
|
||||
res || {}
|
||||
end
|
||||
|
||||
def get_options(name)
|
||||
get_lock()
|
||||
res = self.call('module.options','exploit',name)
|
||||
release_lock()
|
||||
res || {}
|
||||
end
|
||||
|
||||
def payloads()
|
||||
get_lock()
|
||||
res = self.call('module.payloads')
|
||||
release_lock()
|
||||
return {} if not res or not res['modules']
|
||||
res['modules']
|
||||
end
|
||||
|
||||
def payload_options(name)
|
||||
get_lock()
|
||||
res = self.call('module.options','payload',name)
|
||||
release_lock
|
||||
return {} if not res
|
||||
res
|
||||
end
|
||||
|
||||
def launch_exploit(exploit,opts)
|
||||
get_lock()
|
||||
begin
|
||||
res = self.call('module.execute','exploit',exploit,opts)
|
||||
rescue => e
|
||||
print_error "Exploit failed for #{exploit} \n"
|
||||
release_lock()
|
||||
return false
|
||||
end
|
||||
|
||||
release_lock()
|
||||
def browser_exploits
|
||||
get_lock
|
||||
res = call('module.exploits')
|
||||
|
||||
uri = ""
|
||||
if opts['SSL']
|
||||
uri += "https://"
|
||||
else
|
||||
uri += "http://"
|
||||
end
|
||||
return [] unless res || res['modules']
|
||||
|
||||
uri += @config['callback_host'] + ":#{opts['SRVPORT']}/" + opts['URIPATH']
|
||||
res['modules'].select{|m| m.include?('/browser/') }.sort
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
res['uri'] = uri
|
||||
res
|
||||
end
|
||||
def get_exploit_info(name)
|
||||
get_lock
|
||||
res = call('module.info', 'exploit', name)
|
||||
res || {}
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
def launch_autopwn
|
||||
opts = {
|
||||
'LHOST' => @config['callback_host'] ,
|
||||
'URIPATH' => @apurl
|
||||
}
|
||||
get_lock()
|
||||
begin
|
||||
res = self.call('module.execute','auxiliary','server/browser_autopwn',opts)
|
||||
rescue => e
|
||||
print_error "Failed to launch autopwn\n"
|
||||
release_lock()
|
||||
return false
|
||||
end
|
||||
release_lock()
|
||||
return res
|
||||
def get_payloads(name)
|
||||
get_lock
|
||||
res = call('module.compatible_payloads', name)
|
||||
res || {}
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def get_options(name)
|
||||
get_lock
|
||||
res = call('module.options', 'exploit', name)
|
||||
res || {}
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
def payloads
|
||||
get_lock
|
||||
res = call('module.payloads')
|
||||
return {} unless res || res['modules']
|
||||
res['modules']
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
def payload_options(name)
|
||||
get_lock
|
||||
res = call('module.options', 'payload', name)
|
||||
return {} unless res
|
||||
res
|
||||
rescue => e
|
||||
return {}
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
def launch_exploit(exploit, opts)
|
||||
get_lock
|
||||
res = call('module.execute', 'exploit', exploit, opts)
|
||||
proto = opts['SSL'] ? 'https' : 'http'
|
||||
res['uri'] = "#{proto}://#{@config['callback_host']}:#{opts['SRVPORT']}/#{opts['URIPATH']}"
|
||||
res
|
||||
rescue => e
|
||||
print_error "Exploit failed for #{exploit} \n"
|
||||
return false
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
|
||||
def launch_autopwn
|
||||
opts = {
|
||||
'LHOST' => @config['callback_host'],
|
||||
'URIPATH' => @apurl
|
||||
}
|
||||
get_lock
|
||||
call('module.execute', 'auxiliary', 'server/browser_autopwn', opts)
|
||||
rescue => e
|
||||
print_error "Failed to launch autopwn"
|
||||
return false
|
||||
ensure
|
||||
release_lock
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user