Beispiel #1
0
	def import_message_data(self):
		"""
		Process a previously exported message archive file and restore the
		message data, settings, and applicable files from it. This function
		wraps the emission of the ``message-data-import`` signal.

		:return: Whether or not the message archive file was loaded from disk.
		:rtype: bool
		"""
		config_tab = self.tabs.get('config')
		if not config_tab:
			self.logger.warning('attempted to import message data while the config tab was unavailable')
			return False
		config_tab.objects_save_to_config()
		dialog = extras.FileChooserDialog('Import Message Configuration', self.parent)
		dialog.quick_add_filter('King Phisher Message Files', '*.kpm')
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return False
		target_file = response['target_path']

		dialog = extras.FileChooserDialog('Destination Directory', self.parent)
		response = dialog.run_quick_select_directory()
		dialog.destroy()
		if not response:
			return False
		dest_dir = response['target_path']
		if not self.emit('message-data-import', target_file, dest_dir):
			return False
		gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.')
		return True
Beispiel #2
0
	def import_message_data(self):
		"""
		Process a previously exported message archive file and restore the
		message data, settings, and applicable files from it.
		"""
		config_tab = self.tabs.get('config')
		if not config_tab:
			self.logger.warning('attempted to import message data while the config tab was unavailable')
			return
		config_prefix = config_tab.config_prefix
		config_tab.objects_save_to_config()
		dialog = extras.FileChooserDialog('Import Message Configuration', self.parent)
		dialog.quick_add_filter('King Phisher Message Files', '*.kpm')
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return
		target_file = response['target_path']

		dialog = extras.FileChooserDialog('Destination Directory', self.parent)
		response = dialog.run_quick_select_directory()
		dialog.destroy()
		if not response:
			return
		dest_dir = response['target_path']
		try:
			message_data = export.message_data_from_kpm(target_file, dest_dir)
		except KingPhisherInputValidationError as error:
			gui_utilities.show_dialog_error('Import Error', self.parent, error.message.capitalize() + '.')
			return

		config_keys = set(key for key in self.config.keys() if key.startswith(config_prefix))
		config_types = dict(zip(config_keys, [type(self.config[key]) for key in config_keys]))
		for key, value in message_data.items():
			key = config_prefix + key
			if not key in config_keys:
				continue
			self.config[key] = value
			config_keys.remove(key)
		for unset_key in config_keys:
			config_type = config_types[unset_key]
			if not config_type in (bool, dict, int, list, str, tuple):
				continue
			self.config[unset_key] = config_type()

		# set missing defaults for backwards compatibility
		if not self.config.get('mailer.message_type'):
			self.config['mailer.message_type'] = 'email'
		if not self.config.get('mailer.target_type'):
			self.config['mailer.target_type'] = 'file'

		config_tab.objects_load_from_config()
		gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.')
Beispiel #3
0
	def save_html_file(self, force_prompt=False):
		"""
		Save the contents from the editor into an HTML file if one is configured
		otherwise prompt to user to select a file to save as. The user may abort
		the operation by declining to select a file to save as if they are
		prompted to do so.

		:param force_prompt: Force prompting the user to select the file to save as.
		:rtype: bool
		:return: Whether the contents were saved or not.
		"""
		html_file = self.config.get('mailer.html_file')
		if not html_file or force_prompt:
			if html_file:
				current_name = os.path.basename(html_file)
			else:
				current_name = 'message.html'
			dialog = extras.FileChooserDialog('Save HTML File', self.parent)
			response = dialog.run_quick_save(current_name=current_name)
			dialog.destroy()
			if not response:
				return False
			html_file = response['target_path']
			self.config['mailer.html_file'] = html_file
		text = self.textbuffer.get_text(self.textbuffer.get_start_iter(), self.textbuffer.get_end_iter(), False)
		with open(html_file, 'w') as file_h:
			file_h.write(text)
		self.toolbutton_save_html_file.set_sensitive(True)
		return True
Beispiel #4
0
    def signal_kpm_select_clicked(self, _):
        dialog = extras.FileChooserDialog('Import Message Configuration',
                                          self.parent)
        dialog.quick_add_filter('King Phisher Message Files', '*.kpm')
        dialog.quick_add_filter('All Files', '*')
        response = dialog.run_quick_open()
        dialog.destroy()
        if not response:
            return False
        target_path = response['target_path']
        self.gobjects['entry_kpm_file'].set_text(target_path)
        self._set_page_complete(self._get_kpm_path().is_valid)

        if not _kpm_file_path_is_valid(target_path):
            return
        # open the KPM for reading to extract the target URL for the assistant,
        # ignore the directory to allow the user to optionally only import the URL
        kpm = archive.ArchiveFile(target_path, 'r')
        if not kpm.has_file('message_config.json'):
            self.logger.warning(
                'the kpm archive is missing the message_config.json file')
            return
        message_config = kpm.get_json('message_config.json')
        webserver_url = message_config.get('webserver_url')
        if not webserver_url:
            return
        self._set_webserver_url(webserver_url)
Beispiel #5
0
	def export_message_data(self, path=None):
		"""
		Gather and prepare the components of the mailer tab to be exported into
		a King Phisher message (KPM) archive file suitable for restoring at a
		later point in time. If *path* is not specified, the user will be
		prompted to select one and failure to do so will prevent the message
		data from being exported. This function wraps the emission of the
		``message-data-export`` signal.

		:param str path: An optional path of where to save the archive file to.
		:return: Whether or not the message archive file was written to disk.
		:rtype: bool
		"""
		config_tab = self.tabs.get('config')
		if not config_tab:
			self.logger.warning('attempted to export message data while the config tab was unavailable')
			return False
		if path is None:
			dialog = extras.FileChooserDialog('Export Message Configuration', self.parent)
			response = dialog.run_quick_save('message.kpm')
			dialog.destroy()
			if not response:
				return False
			path = response['target_path']
		if not self.emit('message-data-export', path):
			return False
		gui_utilities.show_dialog_info('Success', self.parent, 'Successfully exported the message.')
		return True
	def make_preview(self, _):
		mailer_tab = self.application.main_tabs['mailer']
		config_tab = mailer_tab.tabs['config']
		config_tab.objects_save_to_config()
		input_path = self.application.config['mailer.attachment_file']
		if not (os.path.isfile(input_path) and os.access(input_path, os.R_OK)):
			gui_utilities.show_dialog_error(
				'PDF Build Error',
				self.application.get_active_window(),
				'Attachment path is invalid or is not readable.'
			)
			return

		dialog = extras.FileChooserDialog('Save Generated PDF File', self.application.get_active_window())
		response = dialog.run_quick_save('PDF Preview.pdf')
		dialog.destroy()
		if response is None:
			return

		output_path = response['target_path']
		if not self.process_attachment_file(input_path, output_path):
			return
		gui_utilities.show_dialog_info(
			'PDF Created',
			self.application.get_active_window(),
			'Successfully created the PDF file.'
		)
Beispiel #7
0
    def prompt_and_generate(self):
        config = self.config
        dialog_txt = 'Would you like to generate research data to submit to SecureState?'
        if not gui_utilities.show_dialog_yes_no(
                'Phishing Data Collection',
                self.application.get_active_window(), dialog_txt):
            return

        get_stats = StatsGenerator(self.application.rpc)
        stats = get_stats.generate_stats()

        dialog = extras.FileChooserDialog('Save Anonymized Data',
                                          self.application.get_active_window())
        file_name = 'anonymized_phishing_data.txt'
        response = dialog.run_quick_save(file_name)
        dialog.destroy()
        if not response['target_path']:
            return
        with open(response['target_path'], 'w') as file_h:
            file_h.write(stats)
        gui_utilities.show_dialog_info(
            'Successfully Generated Data',
            self.application.get_active_window(),
            "Please review and email:\n{}\nto [email protected]".
            format(response['target_path']))
        config['last_date'] = datetime.datetime.utcnow()
Beispiel #8
0
	def signal_kpm_dest_folder_clicked(self, _):
		dialog = extras.FileChooserDialog('Destination Directory', self.parent)
		response = dialog.run_quick_select_directory()
		dialog.destroy()
		if not response:
			return False
		self.gobjects['entry_kpm_dest_folder'].set_text(response['target_path'])
		self._update_completion_status()
Beispiel #9
0
	def signal_activate_export_credentials_msf_txt(self, _):
		dialog = extras.FileChooserDialog('Export Credentials', self.application.get_active_window())
		file_name = self.config['campaign_name'] + '.txt'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		export.campaign_credentials_to_msf_txt(self.application.rpc, self.config['campaign_id'], destination_file)
Beispiel #10
0
 def signal_menuitem_activate_config(self, _):
     dialog = extras.FileChooserDialog('Import Configuration File',
                                       self.dialog)
     response = dialog.run_quick_open()
     dialog.destroy()
     if response is None:
         return
     self.application.merge_config(response['target_path'], strict=False)
     self.objects_load_from_config()
Beispiel #11
0
	def signal_activate_popup_menu_export(self, action):
		dialog = extras.FileChooserDialog('Export Graph', self.application.get_active_window())
		file_name = self.config['campaign_name'] + '.png'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		self.figure.savefig(destination_file, format='png')
Beispiel #12
0
	def export_campaign_xml(self):
		"""Export the current campaign to an XML data file."""
		dialog = extras.FileChooserDialog('Export Campaign XML Data', self)
		file_name = self.config['campaign_name'] + '.xml'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		export.campaign_to_xml(self.rpc, self.config['campaign_id'], destination_file)
Beispiel #13
0
	def signal_kpm_select_clicked(self, _):
		dialog = extras.FileChooserDialog('Import Message Configuration', self.parent)
		dialog.quick_add_filter('King Phisher Message Files', '*.kpm')
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return False
		self.gobjects['entry_kpm_file'].set_text(response['target_path'])
		self._update_completion_status()
Beispiel #14
0
	def signal_toolbutton_open(self, button):
		dialog = extras.FileChooserDialog('Choose File', self.parent)
		dialog.quick_add_filter('HTML Files', ['*.htm', '*.html'])
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return False
		self.config['mailer.html_file'] = response['target_path']
		self.show_tab()
		return True
Beispiel #15
0
	def signal_activate_popup_menu_insert_image(self, widget):
		dialog = extras.FileChooserDialog('Choose Image', self.parent)
		dialog.quick_add_filter('Images', ['*.gif', '*.jpeg', '*.jpg', '*.png'])
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return
		target_path = response['target_path']
		target_path = escape_single_quote(target_path)
		text = "{{{{ inline_image('{0}') }}}}".format(target_path)
		return self.signal_activate_popup_menu_insert(widget, text)
Beispiel #16
0
	def export_campaign_visit_geojson(self):
		"""
		Export the current campaign visit information to a GeoJSON data file.
		"""
		dialog = extras.FileChooserDialog('Export Campaign Visit GeoJSON Data', self)
		file_name = self.config['campaign_name'] + '.geojson'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		export.campaign_visits_to_geojson(self.rpc, self.config['campaign_id'], destination_file)
Beispiel #17
0
	def signal_entry_activate_open_file(self, entry):
		dialog = extras.FileChooserDialog('Choose File', self.parent)
		if entry == self.gobjects.get('entry_html_file'):
			dialog.quick_add_filter('HTML Files', ['*.htm', '*.html'])
		elif entry == self.gobjects.get('entry_target_file'):
			dialog.quick_add_filter('CSV Files', '*.csv')
		dialog.quick_add_filter('All Files', '*')
		response = dialog.run_quick_open()
		dialog.destroy()
		if not response:
			return False
		entry.set_text(response['target_path'])
		return True
Beispiel #18
0
	def signal_menuitem_activate_import_config(self, _):
		dialog = extras.FileChooserDialog('Import Configuration File', self.dialog)
		response = dialog.run_quick_open()
		dialog.destroy()
		if response is None:
			return
		config_path = response['target_path']
		try:
			self.application.merge_config(config_path, strict=False)
		except Exception:
			self.logger.warning('failed to merge configuration file: ' + config_path, exc_info=True)
			gui_utilities.show_dialog_error('Invalid Configuration File', self.dialog, 'Could not import the configuration file.')
		else:
			self.objects_load_from_config()
Beispiel #19
0
    def select_xml_campaign(self):
        """
		Prompts the user with a file dialog window to select the King Phisher
		Campaign XML file to import. Validates the file to make sure it is a
		Campaign exported from King Phisher and is the correct version to import.
		"""
        dialog = extras.FileChooserDialog('Import Campaign from XML',
                                          self.window)
        dialog.quick_add_filter('King Phisher XML Campaign', '*.xml')
        dialog.quick_add_filter('All Files', '*')
        response = dialog.run_quick_open()
        dialog.destroy()
        if not response:
            return
        target_file = response['target_path']
        self.entry_path.set_text(target_file)

        try:
            campaign_xml = ET.parse(target_file)
        except ET.ParseError as error:
            self.logger.error(
                "cannot import campaign: {0} is not a valid XML file".format(
                    target_file), error)
            raise KingPhisherInputValidationError(
                "{0} is not a valid xml file".format(target_file))

        root = campaign_xml.getroot()
        if root.tag != 'king_phisher':
            raise KingPhisherInputValidationError(
                'File not a King Phisher Campaign XML Export')
        meta_data = root.find('metadata')
        if meta_data.find('version').text < '1.3':
            raise KingPhisherInputValidationError(
                'Can only import XML Campaign data version 1.3 or higher')
        self.campaign_info = root.find('campaign')
        if not self.campaign_info:
            raise KingPhisherInputValidationError(
                'XML file does not contain any campaign information')

        self.db_campaigns = self.rpc.graphql(
            "{ db { campaigns { edges { node { id, name } } } } }"
        )['db']['campaigns']['edges']
        self.entry_campaign_name.set_text(self.campaign_info.find('name').text)
        self.thread_import_campaign = None
        if not self._check_campaign_name(self.campaign_info.find('name').text,
                                         verbose=True):
            self.button_import_campaign.set_sensitive(False)
            return
        self.button_import_campaign.set_sensitive(True)
Beispiel #20
0
 def export_table_to_csv(self):
     """Export the data represented by the view to a CSV file."""
     if not self._export_lock():
         return
     dialog = extras.FileChooserDialog('Export Data', self.parent)
     file_name = self.config['campaign_name'] + '.csv'
     response = dialog.run_quick_save(file_name)
     dialog.destroy()
     if not response:
         self.loader_thread_lock.release()
         return
     destination_file = response['target_path']
     store = self.gobjects['treeview_campaign'].get_model()
     columns = dict(enumerate(('UID', ) + self.view_columns))
     export.liststore_to_csv(store, destination_file, columns)
     self.loader_thread_lock.release()
Beispiel #21
0
	def export_campaign_xlsx(self):
		"""Export the current campaign to an Excel compatible XLSX workbook."""
		dialog = extras.FileChooserDialog('Export Campaign To Excel', self)
		file_name = self.config['campaign_name'] + '.xlsx'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		campaign_tab = self.tabs['campaign']
		workbook = xlsxwriter.Workbook(destination_file)
		title_format = workbook.add_format({'bold': True, 'size': 18})
		for tab_name, tab in campaign_tab.tabs.items():
			if not isinstance(tab, CampaignViewGenericTableTab):
				continue
			tab.export_table_to_xlsx_worksheet(workbook.add_worksheet(tab_name), title_format)
		workbook.close()
	def export_table_to_csv(self):
		"""Export the data represented by the view to a CSV file."""
		if not self.loader_thread_lock.acquire(False) or (isinstance(self.loader_thread, threading.Thread) and self.loader_thread.is_alive()):
			gui_utilities.show_dialog_warning('Can Not Export Rows While Loading', self.parent)
			return
		dialog = extras.FileChooserDialog('Export Data', self.parent)
		file_name = self.config['campaign_name'] + '.csv'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			self.loader_thread_lock.release()
			return
		destination_file = response['target_path']
		store = self.gobjects['treeview_campaign'].get_model()
		columns = dict(enumerate(('UID',) + self.view_columns))
		export.liststore_to_csv(store, destination_file, columns)
		self.loader_thread_lock.release()
Beispiel #23
0
	def select_path(self, application, entry_widget):
		dialog = extras.FileChooserDialog('Select ' + self.path_type.capitalize(), application.get_active_window())
		if self.path_type.startswith('file-') and self.file_filters:
			for name, patterns in self.file_filters:
				dialog.quick_add_filter(name, patterns)
			dialog.quick_add_filter('All Files', '*')

		if self.path_type == 'directory':
			result = dialog.run_quick_select_directory()
		elif self.path_type == 'file-open':
			result = dialog.run_quick_open()
		elif self.path_type == 'file-save':
			result = dialog.run_quick_save()
		else:
			dialog.destroy()
			raise ValueError('path_type must be either \'directory\', \'file-open\', or \'file-save\'')
		dialog.destroy()
		if result is None:
			return
		entry_widget.set_text(result['target_path'])
Beispiel #24
0
	def export_message_data(self):
		"""
		Gather and prepare the components of the mailer tab to be exported into
		a message archive file suitable for restoring at a later point in time.
		"""
		config_tab = self.tabs.get('config')
		if not config_tab:
			self.logger.warning('attempted to export message data while the config tab was unavailable')
			return
		config_prefix = config_tab.config_prefix
		config_tab.objects_save_to_config()
		dialog = extras.FileChooserDialog('Export Message Configuration', self.parent)
		response = dialog.run_quick_save('message.kpm')
		dialog.destroy()
		if not response:
			return
		message_config = {}
		config_keys = (key for key in self.config.keys() if key.startswith(config_prefix))
		for config_key in config_keys:
			message_config[config_key[7:]] = self.config[config_key]
		export.message_data_to_kpm(message_config, response['target_path'])
    def make_preview(self, _):
        input_path = self.application.config['mailer.attachment_file']
        if not os.path.isfile(input_path) and os.access(input_path, os.R_OK):
            gui_utilities.show_dialog_error(
                'PDF Build Error', self.application.get_active_window(),
                'An invalid attachment file is specified.')
            return

        dialog = extras.FileChooserDialog('Save Generated PDF File',
                                          self.application.get_active_window())
        response = dialog.run_quick_save('preview.pdf')
        dialog.destroy()
        if response is None:
            return

        output_path = response['target_path']
        if not self.process_attachment_file(input_path, output_path):
            gui_utilities.show_dialog_error(
                'PDF Build Error', self.application.get_active_window(),
                'Failed to create the PDF file.')
            return
        gui_utilities.show_dialog_info('PDF Created',
                                       self.application.get_active_window(),
                                       'Successfully created the PDF file.')
Beispiel #26
0
 def signal_multi_set_directory(self, _):
     dialog = extras.FileChooserDialog('Destination Directory', self.dialog)
     response = dialog.run_quick_select_directory()
     dialog.destroy()
     if response:
         self.entry_directory.set_text(response['target_path'])
    def select_xml_campaign(self):
        """
		Prompts the user with a file dialog window to select the King Phisher
		Campaign XML file to import. Validates the file to make sure it is a
		Campaign exported from King Phisher and is the correct version to import.
		"""
        dialog = extras.FileChooserDialog('Import Campaign from XML',
                                          self.window)
        dialog.quick_add_filter('King Phisher XML Campaign', '*.xml')
        dialog.quick_add_filter('All Files', '*')
        response = dialog.run_quick_open()
        dialog.destroy()
        if not response:
            return
        target_file = response['target_path']
        self.entry_path.set_text(target_file)

        try:
            campaign_xml = ET.parse(target_file)
        except ET.ParseError as error:
            self.logger.error(
                "cannot import campaign file: {0} (not a valid xml file)".
                format(target_file))
            gui_utilities.show_dialog_error('Improper Format', self.window,
                                            'File is not valid XML')
            return

        root = campaign_xml.getroot()
        if root.tag != 'king_phisher':
            self.logger.error(
                "cannot import campaign file: {0} (invalid root xml tag)".
                format(target_file))
            gui_utilities.show_dialog_error(
                'Improper Format', self.window,
                'File is not a valid King Phisher XML campaign file')
            return

        meta_data = root.find('metadata')
        if meta_data.find('version').text < '1.3':
            self.logger.error(
                "cannot import campaign file: {0} (incompatible version)".
                format(target_file))
            gui_utilities.show_dialog_error(
                'Invalid Version', self.window,
                'Cannot import XML campaign data less then version 1.3')
            return

        self.campaign_info = root.find('campaign')
        if not self.campaign_info:
            self.logger.error(
                "cannot import campaign file: {0} (no campaign data found)".
                format(target_file))
            gui_utilities.show_dialog_error('No Campaign Data', self.window,
                                            'No campaign data to import')
            return

        self.db_campaigns = self.rpc.graphql(
            "{ db { campaigns { edges { node { id, name } } } } }"
        )['db']['campaigns']['edges']
        self.entry_campaign_name.set_text(self.campaign_info.find('name').text)
        self.thread_import_campaign = None
        if not self._check_campaign_name(self.campaign_info.find('name').text,
                                         verbose=True):
            self.button_import_campaign.set_sensitive(False)
            return
        self.button_import_campaign.set_sensitive(True)