#!/usr/local/bin/ruby18
#
# $Id: msfcli 10719 2010-10-17 05:16:57Z hdm $
#
# This user interface allows users to interact with the framework through a
# command line interface (CLI) rather than having to use a prompting console
# or web-based interface.
#
# $Revision: 10719 $
#

msfbase = __FILE__
while File.symlink?(msfbase)
	msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end

$:.unshift(File.join(File.dirname(msfbase), 'lib'))
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']

require 'rex'
require 'msf/ui'
require 'msf/base'

Indent = '   '


def usage (str = nil, extra = nil)
	tbl = Rex::Ui::Text::Table.new(
		'Header'  => "Usage: #{$0} <exploit_name> <option=value> [mode]",
		'Indent'  => 4,
		'Columns' => ['Mode', 'Description']
	)

	tbl << ['(H)elp', "You're looking at it baby!"]
	tbl << ['(S)ummary', 'Show information about this module']
	tbl << ['(O)ptions', 'Show available options for this module']
	tbl << ['(A)dvanced', 'Show available advanced options for this module']
	tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module']
	tbl << ['(P)ayloads', 'Show available payloads for this module']
	tbl << ['(T)argets', 'Show available targets for this exploit module']
	tbl << ['(AC)tions', 'Show available actions for this auxiliary module']
	tbl << ['(C)heck', 'Run the check routine of the selected module']
	tbl << ['(E)xecute', 'Execute the selected module']

	$stdout.puts "Error: #{str}\n\n" if str
	$stdout.puts tbl.to_s + "\n"
	$stdout.puts extra + "\n" if extra

	exit
end

# Handle the help option before loading modules
exploit_name = ARGV.shift
exploit      = nil
module_class = "exploit"

if(exploit_name == "-h")
	usage()
end

# Initialize the simplified framework instance.
$stderr.puts "[*] Please wait while we load the module tree..."
$framework = Msf::Simple::Framework.create

if ($framework.modules.failed.length > 0)
  print("Warning: The following modules could not be loaded!\n\n")
  $framework.modules.failed.each_pair do |file, err|
    print("\t#{file}: #{err}\n\n")
  end
end

if (not exploit_name)
	ext = ''

	tbl = Rex::Ui::Text::Table.new(
		'Header'  => 'Exploits',
		'Indent'  => 4,
		'Columns' => [ 'Name', 'Description' ])

	$framework.exploits.each_module { |name, mod|
		tbl << [  'exploit/' + name, mod.new.name ]
	}
	ext << tbl.to_s + "\n"

	tbl = Rex::Ui::Text::Table.new(
		'Header'  => 'Auxiliary',
		'Indent'  => 4,
		'Columns' => [ 'Name', 'Description' ])

	$framework.auxiliary.each_module { |name, mod|
		tbl << [ 'auxiliary/' + name, mod.new.name ]
	}

	ext << tbl.to_s + "\n"

	usage(nil, ext)
end


# Process special var/val pairs...
Msf::Ui::Common.process_cli_arguments($framework, ARGV)

# Determine what type of module it is
case exploit_name
when /exploit\/(.*)/
	exploit = $framework.exploits.create($1)
	module_class = 'exploit'

when /auxiliary\/(.*)/
	exploit = $framework.auxiliary.create($1)
	module_class = 'auxiliary'

else
	exploit = $framework.exploits.create(exploit_name)
	if exploit == nil
		# Try falling back on aux modules
		exploit = $framework.auxiliary.create(exploit_name)
		module_class = 'auxiliary'
	end

end



if (exploit == nil)
	usage("Invalid module: #{exploit_name}")
end

exploit.init_ui(
	Rex::Ui::Text::Input::Stdio.new,
	Rex::Ui::Text::Output::Stdio.new
)

# Evalulate the command (default to "help")
mode = ARGV.pop || 'h'

# Import options
exploit.datastore.import_options_from_s(ARGV.join('_|_'), '_|_')


# Initialize associated modules
payload = nil
encoder = nil
nop     = nil

if (exploit.datastore['PAYLOAD'])
	payload = $framework.payloads.create(exploit.datastore['PAYLOAD'])
	if (payload != nil)
	payload.datastore.import_options_from_s(ARGV.join('_|_'), '_|_')
	end
end

if (exploit.datastore['ENCODER'])
	encoder = $framework.encoders.create(exploit.datastore['ENCODER'])
	if (encoder != nil)
	encoder.datastore.import_options_from_s(ARGV.join('_|_'), '_|_')
	end
end

if (exploit.datastore['NOP'])
	nop = $framework.nops.create(exploit.datastore['NOP'])
	if (nop != nil)
	nop.datastore.import_options_from_s(ARGV.join('_|_'), '_|_')
	end
end

case mode.downcase
	when 'h'
		usage
	when "s"
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_module(exploit, Indent))
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_module(payload, Indent)) if payload
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_module(encoder, Indent)) if encoder
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_module(nop, Indent)) if nop

	when "o"
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_options(exploit, Indent))
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_options(payload, Indent)) if payload
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_options(encoder, Indent)) if encoder
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_options(nop, Indent)) if nop
	when "a"
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_advanced_options(exploit, Indent))
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_advanced_options(payload, Indent)) if payload
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_advanced_options(encoder, Indent)) if encoder
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_advanced_options(nop, Indent)) if nop
	when "i"
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_evasion_options(exploit, Indent))
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_evasion_options(payload, Indent)) if payload
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_evasion_options(encoder, Indent)) if encoder
		$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_evasion_options(nop, Indent)) if nop
	when "p"
		if (module_class == 'exploit')
			$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_compatible_payloads(exploit, Indent, "Compatible payloads"))
		else
			$stdout.puts("\nError: This type of module does not support payloads")
		end
	when "t"
		if (module_class == 'exploit')
			$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_exploit_targets(exploit, Indent))
		else
			$stdout.puts("\nError: This type of module does not support targets")
		end
	when "ac"
		if (module_class == 'auxiliary')
			$stdout.puts("\n" + Msf::Serializer::ReadableText.dump_auxiliary_actions(exploit, Indent))
		else
			$stdout.puts("\nError: This type of module does not support actions")
		end
	when "c"
		if (module_class == 'exploit')
			begin
				if (code = exploit.check_simple(
					'LocalInput'    => Rex::Ui::Text::Input::Stdio.new,
					'LocalOutput'   => Rex::Ui::Text::Output::Stdio.new))
					stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]'

					$stdout.puts("#{stat} #{code[1]}")
				else
					$stderr.puts("Check failed: The state could not be determined.")
				end
			rescue
				$stderr.puts("Check failed: #{$!}")
			end
		else
			$stdout.puts("\nError: This type of module does not support the check feature")
		end
	when "e"
			con = Msf::Ui::Console::Driver.new(
				Msf::Ui::Console::Driver::DefaultPrompt,
				Msf::Ui::Console::Driver::DefaultPromptChar,
				{ 
					'Framework' => $framework
				}
			)
			con.run_single("use #{module_class}/#{exploit.refname}")
			
			ARGV.each do |arg|
				k,v = arg.split("=", 2)
				con.run_single("set #{k} #{v}")
			end

			con.run_single("exploit")
			
			# If we have sessions or jobs, keep running
			if $framework.sessions.length > 0 or $framework.jobs.length > 0
				con.run
			else
				con.run_single("quit")
			end

	else
		usage("Invalid mode #{mode}")
end

$stdout.puts
