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