# -*- coding: binary -*-
module Msf
module Ui
module Console
module CommandDispatcher

###
#
# Exploit module command dispatcher.
#
###
class Exploit

	include Msf::Ui::Console::ModuleCommandDispatcher

	@@exploit_opts = Rex::Parser::Arguments.new(
		"-e" => [ true,  "The payload encoder to use.  If none is specified, ENCODER is used." ],
		"-f" => [ false, "Force the exploit to run regardless of the value of MinimumRank."    ],
		"-h" => [ false, "Help banner."                                                        ],
		"-j" => [ false, "Run in the context of a job."                                        ],
		"-n" => [ true,  "The NOP generator to use.  If none is specified, NOP is used."       ],
		"-o" => [ true,  "A comma separated list of options in VAR=VAL format."                ],
		"-p" => [ true,  "The payload to use.  If none is specified, PAYLOAD is used."         ],
		"-t" => [ true,  "The target index to use.  If none is specified, TARGET is used."     ],
		"-z" => [ false, "Do not interact with the session after successful exploitation."     ])

	#
	# Returns the hash of exploit module specific commands.
	#
	def commands
		super.update({
			"exploit"  => "Launch an exploit attempt",
			"rcheck"   => "Reloads the module and checks if the target is vulnerable",
			"rexploit" => "Reloads the module and launches an exploit attempt",
			"reload"   => "Just reloads the module",
			"run"      => "Alias for exploit",
			"rerun"    => "Alias for rexploit",
		})
	end

	#
	# Returns the name of the command dispatcher.
	#
	def name
		"Exploit"
	end

	#
	# Launches an exploitation attempt.
	#
	def cmd_exploit(*args)
		defanged?

		opt_str = nil
		payload = mod.datastore['PAYLOAD']
		encoder = mod.datastore['ENCODER']
		target  = mod.datastore['TARGET']
		nop     = mod.datastore['NOP']
		bg      = false
		jobify  = false
		force   = false

		# Always run passive exploits in the background
		if (mod.passive?)
			jobify = true
		end

		@@exploit_opts.parse(args) { |opt, idx, val|
			case opt
				when '-e'
					encoder = val
				when '-f'
					force = true
				when '-j'
					jobify = true
				when '-n'
					nop = val
				when '-o'
					opt_str = val
				when '-p'
					payload = val
				when '-t'
					target = val.to_i
				when '-z'
					bg = true
				when '-h'
					cmd_exploit_help
					return false
			end
		}

		minrank = RankingName.invert[framework.datastore['MinimumRank']] || 0
		if minrank > mod.rank
			if force
				print_status("Forcing #{mod.refname} to run despite MinimumRank '#{framework.datastore['MinimumRank']}'")
				ilog("Forcing #{mod.refname} to run despite MinimumRank '#{framework.datastore['MinimumRank']}'", 'core')
			else
				print_error("This exploit is below the minimum rank, '#{framework.datastore['MinimumRank']}'.")
				print_error("If you really want to run it, do 'exploit -f' or")
				print_error("setg MinimumRank to something lower ('manual' is")
				print_error("the lowest and would allow running all exploits).")
				return
			end
		end

		if not payload
			payload = Exploit.choose_payload(mod, target)
		end

		begin
			session = mod.exploit_simple(
				'Encoder'        => encoder,
				'Payload'        => payload,
				'Target'         => target,
				'Nop'            => nop,
				'OptionStr'      => opt_str,
				'LocalInput'     => driver.input,
				'LocalOutput'    => driver.output,
				'RunAsJob'       => jobify)
		rescue ::Interrupt
			raise $!
		rescue ::Exception => e
			print_error("Exploit exception (#{mod.refname}): #{e.class} #{e}")
			if(e.class.to_s != 'Msf::OptionValidateError')
				print_error("Call stack:")
				e.backtrace.each do |line|
					break if line =~ /lib.msf.base.simple/
					print_error("  #{line}")
				end
			end
		end

		# If we were given a session, let's see what we can do with it
		if (session)

			# If we aren't told to run in the background and the session can be
			# interacted with, start interacting with it by issuing the session
			# interaction command.
			if (bg == false and session.interactive?)
				print_line

				driver.run_single("sessions -q -i #{session.sid}")
			# Otherwise, log that we created a session
			else
				print_status("Session #{session.sid} created in the background.")
			end
		# If we ran the exploit as a job, indicate such so the user doesn't
		# wonder what's up.
		elsif (jobify)
			if mod.job_id
				print_status("Exploit running as background job.")
			end
		# Worst case, the exploit ran but we got no session, bummer.
		else
			# If we didn't run a payload handler for this exploit it doesn't
			# make sense to complain to the user that we didn't get a session
			unless (mod.datastore["DisablePayloadHandler"])
				print_status("Exploit completed, but no session was created.")
			end
		end
	end

	alias cmd_run cmd_exploit

	def cmd_exploit_help
		print_line "Usage: exploit [options]"
		print_line
		print_line "Launches an exploitation attempt."
		print @@exploit_opts.usage
	end

	alias cmd_run_help cmd_exploit_help

	#
	# Reloads an exploit module and checks the target to see if it's
	# vulnerable.
	#
	def cmd_rcheck(*args)
		reload()

		cmd_check(*args)
	end

	#
	# Reloads an exploit module and launches an exploit.
	#
	def cmd_rexploit(*args)
		return cmd_rexploit_help if args.include? "-h"

		# Stop existing job and reload the module
		if reload(true)
			# Delegate to the exploit command unless the reload failed
			cmd_exploit(*args)
		end
	end

	alias cmd_rerun cmd_rexploit

	def cmd_rexploit_help
		print_line "Usage: rexploit [options]"
		print_line
		print_line "Reloads a module, stopping any associated job, and launches an exploitation attempt."
		print @@exploit_opts.usage
	end

	alias cmd_rerun_help cmd_rexploit_help

	#
	# Picks a reasonable payload and minimally configures it
	#
	def self.choose_payload(mod, target)

		# Choose either the real target or an invalid address
		# This is used to determine the LHOST value
		rhost = mod.datastore['RHOST'] || '50.50.50.50'

		# A list of preferred payloads in the best-first order
		pref = [
			'windows/meterpreter/reverse_tcp',
			'java/meterpreter/reverse_tcp',
			'php/meterpreter/reverse_tcp',
			'php/meterpreter_reverse_tcp',
			'ruby/shell_reverse_tcp',
			'cmd/unix/interact',
			'cmd/unix/reverse',
			'cmd/unix/reverse_perl',
			'cmd/unix/reverse_netcat_gaping',
			'windows/meterpreter/reverse_nonx_tcp',
			'windows/meterpreter/reverse_ord_tcp',
			'windows/shell/reverse_tcp',
			'generic/shell_reverse_tcp'
		]
		pset = mod.compatible_payloads.map{|x| x[0] }
		pref.each do |n|
			if(pset.include?(n))
				mod.datastore['PAYLOAD'] = n
				mod.datastore['LHOST']   = Rex::Socket.source_address(rhost)
				return n
			end
		end
		return
	end

end

end end end end
