#
# Copyright (c) 2006-2016 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
  
	class RpcClient < ::Msf::RPC::Client
		
		include Singleton

        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'] || false)
				launch_msf = ''
				msf_os = ''
				@config['msf_path'].each do |path|
					if File.exist?(path['path'] + 'msfrpcd')
						launch_msf = path['path'] + 'msfrpcd'
						print_info 'Found msfrpcd: ' + launch_msf
						msf_os = path['os'] 
					end
				end
				if (launch_msf.length > 0)
					msf_url = ''
					argssl = ''
					if not opts[:ssl]
						argssl = '-S'
						msf_url = 'http://'
					else
						msf_url = 'https://'	
					end

					msf_url += opts[:host] + ':' + opts[:port].to_s() + opts[:uri]
					if msf_os.eql? "win"
						print_info 'Metasploit auto-launch is currently not supported in BeEF on MS Windows.'
					else	
						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 'Attempt to start msfrpcd, this may take a while. PID: ' + child.pid.to_s

						#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
						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 Exception
							retry if (retries -= 1) > 0
						end
					end
				else
					print_error '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
        end
    
        def release_lock()
            @lock = false
        end
	def call(meth, *args)
		ret = nil
		begin
			ret = super(meth,*args)
		rescue => e
			return nil
		end
		ret
	end
    
	def unit_test_init
		@unit_test = true
	end
        # login to metasploit
		def login
            		get_lock()

			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
      
            		release_lock()
			true
		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

		def browser_exploits()
                
      			get_lock()
			res = self.call('module.exploits')
			return [] if not res or not res['modules']

			mods = res['modules']
			ret = []
			
			mods.each do |m|
				ret << m if(m.include? '/browser/')
			end
			
      			release_lock()
			ret.sort
	  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()

			uri = ""
			if opts['SSL'] 
				uri += "https://"
			else
				uri += "http://"
			end

			uri += @config['callback_host'] + ":#{opts['SRVPORT']}/" + opts['URIPATH']

			res['uri'] = uri
			res
		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

		end
		
	end
	
end
end
end
