2007-05-13 09:10:26 +00:00
|
|
|
module Msf
|
|
|
|
module Ui
|
|
|
|
module Gtk2
|
|
|
|
|
|
|
|
##
|
|
|
|
# This class perform an assistant to configure module
|
|
|
|
##
|
|
|
|
class MsfAssistant
|
|
|
|
|
|
|
|
KEY, DEFAULT, VALUE, DESC = *(0..5).to_a
|
|
|
|
|
|
|
|
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',
|
|
|
|
["Check your review", "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
|
|
|
|
|
2007-05-13 19:46:26 +00:00
|
|
|
include Msf::Ui::Gtk2::MyControls
|
|
|
|
|
2007-05-13 09:10:26 +00:00
|
|
|
#
|
|
|
|
# Init
|
|
|
|
#
|
|
|
|
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.refname)
|
|
|
|
|
|
|
|
# Initialize exploit driver's exploit instance
|
|
|
|
@mydriver = Msf::ExploitDriver.new(framework)
|
|
|
|
@mydriver.exploit = framework.exploits.create(@active_module.refname)
|
2007-05-13 19:46:26 +00:00
|
|
|
|
2007-05-13 11:32:55 +00:00
|
|
|
# Populate the datastore if possible
|
|
|
|
@mydriver.exploit.load_config
|
|
|
|
# p @mydriver.exploit.datastore
|
2007-05-13 19:46:26 +00:00
|
|
|
|
2007-05-13 09:10:26 +00:00
|
|
|
|
|
|
|
# Main interface
|
|
|
|
@model_required = Gtk::ListStore.new(String, String, String, String)
|
|
|
|
@model_advanced = Gtk::ListStore.new(String, String, String, String)
|
|
|
|
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
|
2007-05-13 19:46:26 +00:00
|
|
|
@mydriver.exploit.datastore.import_options_from_hash(@hash, imported = false)
|
|
|
|
@mydriver.payload.share_datastore(@mydriver.exploit.datastore)
|
2007-05-13 09:10:26 +00:00
|
|
|
|
2007-05-13 19:46:26 +00:00
|
|
|
# TODO: choose the $gtk2driver or @mydriver.exploit ?
|
|
|
|
$gtk2driver.active_module = @mydriver.exploit
|
|
|
|
$gtk2driver.save_config
|
2007-05-13 09:10:26 +00:00
|
|
|
|
2007-05-13 19:46:26 +00:00
|
|
|
# Save the framework's datastore
|
|
|
|
framework.save_config
|
|
|
|
@mydriver.exploit.datastore.to_file(Msf::Config.config_file, @mydriver.exploit.refname)
|
2007-05-13 09:10:26 +00:00
|
|
|
|
|
|
|
$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
|
|
|
|
)
|
|
|
|
button_forward.set_sensitive(false)
|
|
|
|
display()
|
|
|
|
options_completion()
|
|
|
|
elsif (self.page == "options")
|
|
|
|
self.page = "end"
|
|
|
|
refresh_label( [@label_target, @label_payload, @label_options],
|
|
|
|
[@label_review],
|
|
|
|
nil
|
|
|
|
)
|
|
|
|
display()
|
|
|
|
review_completion()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Validate options in datastore
|
|
|
|
#
|
|
|
|
def validate
|
|
|
|
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)
|
|
|
|
button_forward.set_sensitive(false)
|
|
|
|
# MsfDialog::Error.new(self, "Failed to validate : #{errors.join(', ')}")
|
|
|
|
else
|
|
|
|
button_forward.set_sensitive(true)
|
|
|
|
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()
|
|
|
|
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)
|
2007-05-13 11:32:55 +00:00
|
|
|
begin
|
|
|
|
combo_target.active = @mydriver.exploit.datastore['TARGET'].to_i
|
|
|
|
rescue
|
|
|
|
combo_target.active = 0
|
|
|
|
end
|
2007-05-13 09:10:26 +00:00
|
|
|
|
|
|
|
# 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)
|
2007-05-13 19:46:26 +00:00
|
|
|
|
2007-05-13 11:32:55 +00:00
|
|
|
index = ""
|
2007-05-13 09:10:26 +00:00
|
|
|
|
|
|
|
# Add iter to Model
|
2007-05-13 11:32:55 +00:00
|
|
|
@active_module.compatible_payloads.each_with_index do |key, idx|
|
2007-05-13 09:10:26 +00:00
|
|
|
iter = model_all.append
|
2007-05-13 19:46:26 +00:00
|
|
|
|
2007-05-13 11:32:55 +00:00
|
|
|
# refname
|
|
|
|
iter[0] = key[0]
|
|
|
|
# payload
|
|
|
|
iter[1] = key[1]
|
2007-05-13 19:46:26 +00:00
|
|
|
|
2007-05-13 11:32:55 +00:00
|
|
|
if ( @mydriver.exploit.datastore['PAYLOAD'] == key[0])
|
|
|
|
index = idx
|
|
|
|
end
|
2007-05-13 09:10:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Gtk::ComboBox
|
|
|
|
combo_all = Gtk::ComboBox.new(model_all)
|
2007-05-13 11:32:55 +00:00
|
|
|
begin
|
|
|
|
combo_all.active = index
|
|
|
|
rescue
|
|
|
|
combo_all.active = 0
|
|
|
|
end
|
2007-05-13 09:10:26 +00:00
|
|
|
# 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
|
|
|
|
|
|
|
|
#
|
|
|
|
# Display options view
|
|
|
|
#
|
|
|
|
def options_completion
|
|
|
|
|
|
|
|
#
|
|
|
|
@button_forward.set_sensitive(false)
|
|
|
|
|
|
|
|
# Clear treeview
|
|
|
|
@model_required.clear
|
|
|
|
@model_advanced.clear
|
|
|
|
|
|
|
|
# An expander view for advanced options
|
|
|
|
frame_advanced = Gtk::Expander.new('Advanced')
|
|
|
|
|
|
|
|
# TreeView
|
|
|
|
@treeview_required = Gtk::TreeView.new(@model_required)
|
|
|
|
@treeview_advanced = Gtk::TreeView.new(@model_advanced)
|
|
|
|
|
|
|
|
# Column
|
|
|
|
add_columns(@treeview_required, @model_required)
|
|
|
|
add_columns(@treeview_advanced, @model_advanced)
|
|
|
|
|
|
|
|
# Payload options
|
|
|
|
@mydriver.payload = framework.payloads.create(@hash["PAYLOAD"])
|
|
|
|
@mydriver.payload.options.each do |key, opt|
|
|
|
|
if (opt.required?)
|
2007-05-13 11:32:55 +00:00
|
|
|
pack(@model_required, key, opt, @mydriver.exploit.datastore[key])
|
2007-05-13 09:10:26 +00:00
|
|
|
else
|
2007-05-13 11:32:55 +00:00
|
|
|
pack(@model_advanced, key, opt, @mydriver.exploit.datastore[key])
|
2007-05-13 09:10:26 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Exploits options
|
|
|
|
@mydriver.exploit.options.sorted.each do |key, opt|
|
|
|
|
next if (opt.evasion?)
|
|
|
|
if (opt.required?)
|
2007-05-13 11:32:55 +00:00
|
|
|
pack(@model_required, key, opt, @mydriver.exploit.datastore[key])
|
2007-05-13 09:10:26 +00:00
|
|
|
else
|
2007-05-13 11:32:55 +00:00
|
|
|
pack(@model_advanced, key, opt, @mydriver.exploit.datastore[key])
|
2007-05-13 09:10:26 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# add treeview to frame
|
|
|
|
self.main.pack_start(@treeview_required, false, false, 0)
|
|
|
|
self.main.pack_start(frame_advanced, false, false, 10)
|
|
|
|
frame_advanced.add(@treeview_advanced)
|
|
|
|
|
|
|
|
self.main.show_all
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Display the review page
|
|
|
|
#
|
|
|
|
def review_completion
|
|
|
|
warning = Gtk::Label.new
|
|
|
|
warning.set_markup("Review your configuration before clicking the <b>apply</b> button")
|
|
|
|
self.main.pack_start(warning, false, false, 0)
|
|
|
|
|
|
|
|
label = Gtk::Label.new
|
|
|
|
review = "\n\n"
|
|
|
|
@hash.each do |key, value|
|
|
|
|
review << "<b>#{key}</b> : #{value}\n"
|
|
|
|
end
|
|
|
|
label.set_markup(review)
|
|
|
|
|
|
|
|
self.main.pack_start(label, false, false, 0)
|
|
|
|
|
|
|
|
self.main.show_all
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Add column and tips
|
|
|
|
#
|
|
|
|
def add_columns(treeview, model)
|
|
|
|
# column for KEY
|
|
|
|
renderer = Gtk::CellRendererText.new
|
|
|
|
column = Gtk::TreeViewColumn.new('Name',
|
2007-05-17 20:07:14 +00:00
|
|
|
renderer,
|
|
|
|
'text' => KEY)
|
2007-05-13 09:10:26 +00:00
|
|
|
column.set_sort_column_id(KEY)
|
|
|
|
treeview.append_column(column)
|
|
|
|
|
|
|
|
# column for DEFAULT
|
|
|
|
renderer = Gtk::CellRendererText.new
|
|
|
|
column = Gtk::TreeViewColumn.new('Default',
|
2007-05-17 20:07:14 +00:00
|
|
|
renderer,
|
|
|
|
'text' => DEFAULT)
|
2007-05-13 09:10:26 +00:00
|
|
|
column.set_sort_column_id(DEFAULT)
|
|
|
|
treeview.append_column(column)
|
|
|
|
|
|
|
|
# column for VALUE
|
|
|
|
renderer = Gtk::CellRendererText.new
|
|
|
|
renderer.background = "#f6ffd6"
|
|
|
|
renderer.editable = true
|
|
|
|
renderer.signal_connect("edited") do |rend, path, text|
|
|
|
|
updatevalue(model, VALUE, path, text)
|
|
|
|
end
|
|
|
|
column = Gtk::TreeViewColumn.new('Value',
|
2007-05-17 20:07:14 +00:00
|
|
|
renderer,
|
|
|
|
'text' => VALUE)
|
2007-05-13 09:10:26 +00:00
|
|
|
column.set_sort_column_id(VALUE)
|
|
|
|
column.set_resizable(true)
|
2007-05-17 20:07:14 +00:00
|
|
|
column.set_min_width(100)
|
2007-05-13 09:10:26 +00:00
|
|
|
treeview.append_column(column)
|
|
|
|
|
|
|
|
# Init tips on the treeview
|
|
|
|
tips = AssistantTips.new(column)
|
|
|
|
tips.add_view(@treeview_required)
|
|
|
|
tips.add_view(@treeview_advanced)
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Display values on the treeview
|
|
|
|
#
|
2007-05-13 11:32:55 +00:00
|
|
|
def pack(model, key, opt, value = "")
|
2007-05-19 22:18:44 +00:00
|
|
|
default = ""
|
|
|
|
if (opt.default.to_s.length > 50)
|
|
|
|
size = opt.default.to_s.length
|
|
|
|
default = "[...]" + opt.default.to_s[size - 27, size]
|
|
|
|
else
|
|
|
|
default = opt.default.to_s
|
|
|
|
end
|
2007-05-13 09:10:26 +00:00
|
|
|
iter = model.append
|
|
|
|
iter[KEY] = key
|
2007-05-19 22:18:44 +00:00
|
|
|
iter[DEFAULT] = default
|
2007-05-13 11:32:55 +00:00
|
|
|
iter[DESC] = opt.desc.to_s
|
|
|
|
if (key == "LHOST" and value == "")
|
2007-05-13 09:10:26 +00:00
|
|
|
iter[VALUE] = Rex::Socket.source_address
|
|
|
|
@hash['LHOST'] = Rex::Socket.source_address
|
2007-05-13 11:32:55 +00:00
|
|
|
else
|
|
|
|
iter[VALUE] = value.to_s
|
|
|
|
@hash['LHOST'] = value
|
2007-05-13 09:10:26 +00:00
|
|
|
end
|
2007-05-13 11:32:55 +00:00
|
|
|
validate()
|
2007-05-13 09:10:26 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
|
|
|
# Update changing value
|
|
|
|
#
|
|
|
|
def updatevalue(model, column, path, text)
|
|
|
|
iter = model.get_iter(path)
|
|
|
|
iter[column] = text
|
|
|
|
@hash[iter.get_value(KEY)] = text
|
|
|
|
validate()
|
|
|
|
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)
|
|
|
|
|
|
|
|
# Session registration is done by event handler
|
|
|
|
# XXX: No output from the exploit when this is set!
|
|
|
|
@mydriver.use_job = true
|
|
|
|
|
|
|
|
@pipe.create_subscriber_proc() do |msg|
|
|
|
|
$stderr.puts "MSG: #{msg}"
|
|
|
|
$gtk2driver.append_log_view(msg)
|
|
|
|
end
|
|
|
|
|
|
|
|
Thread.new do
|
|
|
|
0.upto(20) do |i|
|
|
|
|
$pipe.print_status("I am alive at #{i}")
|
|
|
|
select(nil, nil, nil, 1.0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@pipe.print_status("Launching exploit #{@mydriver.exploit.refname}...")
|
|
|
|
|
|
|
|
begin
|
|
|
|
@mydriver.run
|
|
|
|
@job_tree.add_oneshot(@active_module, @hash["RHOST"])
|
|
|
|
rescue ::Exception => e
|
|
|
|
@pipe.print_error("Exploit failed: #{e}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|