module Msf
module Ui
module Gtk2
###
#
# This class provides an assistant to configure module
#
###
class MsfAssistant
###
#
# This class perform an assistant to configure and launch exploit
#
###
class Exploit < Msf::Ui::Gtk2::Assistant
# to stock our values
WIZARD = {}
WizardStruct = Struct.new('Wizard',
:description, :page,
:target_state, :payload_state, :options_state, :review_state)
ARRAY = [
['Target',
["Select your target", "intro", true, false, false, false],
],
['Payload',
["Select your payload", "payload", true, true, false, false],
],
['Options',
["Select your options", "option", true, true, true, false],
],
['Review',
["Confirm settings", "end", true, true, true, true],
],
].collect do |item, state|
WIZARD[item] = WizardStruct.new(
state[0],
state[1],
state[2],
state[3],
state[4],
state[5]
)
end
include Msf::Ui::Gtk2::MyControls
def initialize(active_module)
@active_module = active_module
@session_tree = $gtk2driver.session_tree
@job_tree = $gtk2driver.job_tree
@hash = {}
# Call the parent
super(@active_module.name)
# Initialize exploit driver's exploit instance
@mydriver = Msf::ExploitDriver.new(framework)
@mydriver.exploit = framework.exploits.create(@active_module.refname)
# Populate the datastore if possible
@mydriver.exploit.load_config
initialize_options()
# Begin the wizard
target_completion()
# Build the left frame
populate_frame(
[
@label_target = create_label( WIZARD['Target'].target_state,
WIZARD['Target'].description
),
@label_payload = create_label( WIZARD['Target'].payload_state,
WIZARD['Payload'].description
),
@label_options = create_label( WIZARD['Target'].options_state,
WIZARD['Options'].description
),
@label_review = create_label( WIZARD['Target'].review_state,
WIZARD['Review'].description
)
]
)
self.show_all
end
#
# Save configuration for MsfAssistant
#
def save
dump_to_hash()
@mydriver.exploit.datastore.import_options_from_hash(@hash, imported = false)
# If payload
begin
@mydriver.payload.share_datastore(@mydriver.exploit.datastore)
rescue
nil
end
# TODO: choose the $gtk2driver or @mydriver.exploit ?
$gtk2driver.active_module = @mydriver.exploit
$gtk2driver.save_config
# Save the framework's datastore
framework.save_config
@mydriver.exploit.datastore.to_file(Msf::Config.config_file, @mydriver.exploit.refname)
MsfDialog::Information.new(self,
"Configuration Saved",
"Settings for exploit module #{@mydriver.exploit.refname} have been saved to #{Msf::Config.config_file}. " +
"These settings will be loaded the next time this module is accessed."
)
$gtk2driver.append_log_view("Saved configuration to: #{Msf::Config.config_file}\n")
end
#
# Action when Forward button was clicked
#
def next_page
if (self.page == "intro")
self.page = "payload"
refresh_label( [@label_target], # historic
[@label_payload], # actual
[@label_options, @label_review] # next
)
display()
payload_completion()
elsif (self.page == "payload")
self.page = "options"
refresh_label( [@label_target, @label_payload], # historic
[@label_options], # actual
[@label_review] # next
)
display()
initialize_options()
options_completion()
elsif (self.page == "options")
if not validate()
self.page = "options"
refresh_label( [@label_target, @label_payload], # historic
[@label_options], # actual
[@label_review] # next
)
display()
initialize_options()
options_completion()
else
self.page = "end"
refresh_label( [@label_target, @label_payload, @label_options],
[@label_review],
nil
)
display()
review_completion()
end
end
end
#
# Action when Back button was clicked
#
def back_page
if (self.page == "payload")
self.page = "intro"
refresh_label( nil,
[@label_target],
[@label_payload, @label_options, @label_review]
)
display()
target_completion()
elsif (self.page == "options")
self.page = "payload"
refresh_label( [@label_target], # historic
[@label_payload], # actual
[@label_options, @label_review] # next
)
display()
payload_completion()
elsif (self.page == "end")
self.page = "options"
refresh_label( [@label_target, @label_payload],
[@label_options],
[@label_review]
)
display()
initialize_options()
options_completion()
end
end
#
# Display the target view
#
def target_completion
# Model for Gtk::Combo
model_target = Gtk::ListStore.new(String, Object)
# Add iter to Gtk::Combo
@active_module.targets.each_with_index do |target, idx|
iter = model_target.append
iter[0] = target.name
iter[1] = idx
end
# Gtk::ComboBox
combo_target = Gtk::ComboBox.new(model_target)
begin
combo_target.active = @mydriver.exploit.datastore['TARGET'].to_i
rescue
combo_target.active = 0
end
# Pack & renderer combo_target
renderer = Gtk::CellRendererText.new
combo_target.pack_start(renderer, true)
combo_target.set_attributes(renderer, :text => 0)
# Define default value
@hash["TARGET"] = combo_target.active_iter[1]
# Signal for combo payload
combo_target.signal_connect('changed') do ||
@hash["TARGET"] = combo_target.active_iter[1]
end
self.main.pack_start(combo_target, true, false, 0)
self.main.show_all
end
#
# Display the payload view
#
def payload_completion
# Model for Gtk::Combox
model_all = Gtk::ListStore.new(String, Object)
index = ""
# Add iter to Model
@active_module.compatible_payloads.each_with_index do |key, idx|
iter = model_all.append
# refname
iter[0] = key[0]
# payload
iter[1] = key[1]
if ( @mydriver.exploit.datastore['PAYLOAD'] == key[0])
index = idx
end
end
# Gtk::ComboBox
combo_all = Gtk::ComboBox.new(model_all)
begin
combo_all.active = index
rescue
combo_all.active = 0
end
# Pack & renderer combo_all
renderer = Gtk::CellRendererText.new
combo_all.pack_start(renderer, true)
combo_all.set_attributes(renderer, :text => 0)
# Pack combo
self.main.pack_start(combo_all, true, false, 0)
# Stuff for description payload
textscroll = Gtk::ScrolledWindow.new
textscroll.shadow_type = Gtk::SHADOW_IN
textscroll.hscrollbar_policy = Gtk::POLICY_AUTOMATIC
textscroll.vscrollbar_policy = Gtk::POLICY_AUTOMATIC
buffer = Gtk::TextBuffer.new
textview = Gtk::TextView.new(buffer)
textview.set_editable(false)
textview.set_cursor_visible(false)
textscroll.add(textview)
# Pack description
self.main.pack_start(textscroll, true, false, 0)
# Define default value
buffer.set_text(combo_all.active_iter[1].new.description)
@hash["PAYLOAD"] = combo_all.active_iter[0]
# Signal for combo payload
combo_all.signal_connect('changed') do
buffer.set_text(combo_all.active_iter[1].new.description)
@hash["PAYLOAD"] = combo_all.active_iter[0]
end
self.main.show_all
end
#
# Instantiate the options page controls
#
def initialize_options
@frame_standard = Gtk::Expander.new("Standard")
@frame_advanced = Gtk::Expander.new("Advanced")
@frame_evasion = Gtk::Expander.new("Evasion")
@options_standard = Gtk::VBox.new(false, 0)
@options_advanced = Gtk::VBox.new(false, 0)
@options_evasion = Gtk::VBox.new(false, 0)
@framer = Gtk::VBox.new(false, 10)
@scroller = Gtk::ScrolledWindow.new
@scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
@scroller.set_size_request(580, 420)
@viewport = Gtk::Viewport.new(@scroller.hadjustment, @scroller.vadjustment)
@frame_standard.expanded = true
end
#
# Display options view
#
def options_completion
self.page = "options"
# Title and three sets of options
title = Gtk::Label.new
title.set_markup("#{@mydriver.exploit.name}")
@framer.pack_start(title, false, true, 5)
@framer.pack_start(@frame_standard, false, false, 10)
@framer.pack_start(@frame_advanced, false, false, 10)
@framer.pack_start(@frame_evasion, false, false, 10)
uniq = {}
# Exploit options
@mydriver.exploit.options.sorted.each do |key, opt|
uniq[key] ||= true
if(opt.evasion?)
@options_evasion.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
elsif(opt.advanced?)
@options_advanced.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
else
@options_standard.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
end
end
# Payload options
@mydriver.payload = framework.payloads.create(@hash["PAYLOAD"])
@mydriver.payload.options.each do |key, opt|
next if uniq[key]
if(opt.evasion?)
@options_evasion.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
elsif(opt.advanced?)
@options_advanced.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
else
@options_standard.pack_start(add_option(key, opt, @mydriver.exploit.datastore[key]), false, false, 10)
end
end
# Display
indent = Gtk::HBox.new(false, 5)
indent.pack_start(Gtk::Label.new(""), false, false, 5)
indent.pack_start(@options_standard, false, false, 0)
indent.pack_start(Gtk::Label.new(""),true, true, 5)
@frame_standard.add(indent)
indent = Gtk::HBox.new(false, 5)
indent.pack_start(Gtk::Label.new(""), false, false, 5)
indent.pack_start(@options_advanced, false, false, 0)
indent.pack_start(Gtk::Label.new(""),true, true, 5)
@frame_advanced.add(indent)
indent = Gtk::HBox.new(false, 5)
indent.pack_start(Gtk::Label.new(""), false, false, 5)
indent.pack_start(@options_evasion, false, false, 0)
indent.pack_start(Gtk::Label.new(""),true, true, 5)
@frame_evasion.add(indent)
labels = ["Standard", "Advanced", "Evasion"]
[@frame_standard, @frame_advanced, @frame_evasion].each do |obj|
txt = labels.shift
obj.spacing = 10
obj.use_markup = true
obj.label = "#{txt}"
end
# Stuff it into a viewport
@viewport.add(@framer)
# Stuff the viewport into a scrolledwindow
@scroller.add(@viewport)
# Stuff this into main and call it done
self.main.pack_start(@scroller, true, true, 10)
self.main.show_all
end
#
# Put all values in a hash
#
def dump_to_hash
@options_standard.each do |widget|
name, value = widget.get_pair
begin
@hash[name] = value
rescue
nil
end
end
@options_advanced.each do |widget|
name, value = widget.get_pair
begin
if (@mydriver.exploit.options[name].default.to_s == value)
nil
else
@hash[name] = value
end
rescue
nil
end
end
@options_evasion.each do |widget|
name, value = widget.get_pair
begin
if (@mydriver.exploit.options[name].default.to_s == value)
nil
else
@hash[name] = value
end
rescue
nil
end
end
end
#
# Validate options in datastore
#
def validate
dump_to_hash()
errors = []
@mydriver.exploit.datastore.import_options_from_hash(@hash)
@mydriver.exploit.options.each_pair do |name, option|
if (!option.valid?(@mydriver.exploit.datastore[name]))
errors << name
# If the option is valid, normalize its format to the correct type.
elsif ((val = option.normalize(@mydriver.exploit.datastore[name])) != nil)
@mydriver.exploit.datastore.update_value(name, val)
end
end
if (errors.empty? == false)
MsfDialog::Error.new(self, "Failed to validate: #{errors.join(', ')}")
false
else
true
end
end
#
# Display the review page
#
def review_completion
warning = Gtk::Label.new
warning.set_markup("Review your configuration before clicking the apply button")
self.main.pack_start(warning, false, false, 0)
label = Gtk::Label.new
review = "\n\n"
@hash.each do |key, value|
review << "#{key} : #{value}\n"
end
label.set_markup(review)
self.main.pack_start(label, false, false, 0)
self.main.show_all
end
#
# Fire !!
#
def apply
# Import options from the supplied assistant
@mydriver.exploit.datastore.import_options_from_hash(@hash)
# Share the exploit's datastore with the payload
@mydriver.payload.share_datastore(@mydriver.exploit.datastore)
@mydriver.target_idx = (@mydriver.exploit.datastore['TARGET']).to_i
@pipe = Msf::Ui::Gtk2::GtkConsolePipe.new
@mydriver.exploit.init_ui(@pipe, @pipe)
@mydriver.payload.init_ui(@pipe, @pipe)
@mydriver.use_job = true
@pipe.create_subscriber_proc() do |msg|
$gtk2driver.append_log_view(@mydriver.exploit.refname.split("/")[-1] + " " + msg)
end
@pipe.print_status("Launching exploit #{@mydriver.exploit.refname}...")
begin
@mydriver.run
rescue ::Exception => e
@mydriver.exploit.print_error("Exploit failed: #{$!}")
elog("Exploit failed: #{$!}", 'core', LEV_0)
dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_3)
end
select(nil, nil, nil, 0.01)
@pipe.print_status("Exploit #{@mydriver.exploit.refname} completed.") if not @mydriver.use_job
end
end # MsfAssistant::Exploit
end # Exploit
end
end
end