Beispiel #1
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 #2
0
def message_data_from_kpm(target_file, dest_dir):
	"""
	Retrieve the stored details describing a message from a previously exported
	file.

	:param str target_file: The file to load as a message archive.
	:param str dest_dir: The directory to extract data and attachment files to.
	:return: The restored details from the message config.
	:rtype: dict
	"""
	if not archive.is_archive(target_file):
		logger.warning('the file is not recognized as a valid archive')
		raise KingPhisherInputValidationError('file is not in the correct format')
	kpm = archive.ArchiveFile(target_file, 'r')

	attachment_member_names = [n for n in kpm.file_names if n.startswith('attachments' + os.path.sep)]
	attachments = []

	if not kpm.has_file('message_config.json'):
		logger.warning('the kpm archive is missing the message_config.json file')
		raise KingPhisherInputValidationError('data is missing from the message archive')
	message_config = kpm.get_data('message_config.json')
	message_config = json_ex.loads(message_config)

	if attachment_member_names:
		attachment_dir = os.path.join(dest_dir, 'attachments')
		if not os.path.isdir(attachment_dir):
			os.mkdir(attachment_dir)
		for file_name in attachment_member_names:
			arcfile_h = kpm.get_file(file_name)
			file_name = os.path.basename(file_name)
			file_path = os.path.join(attachment_dir, file_name)
			with open(file_path, 'wb') as file_h:
				shutil.copyfileobj(arcfile_h, file_h)
			attachments.append(file_path)
		logger.debug("extracted {0} attachment file{1} from the archive".format(len(attachments), 's' if len(attachments) > 1 else ''))

	for config_name, file_name in KPM_ARCHIVE_FILES.items():
		if not file_name in kpm.file_names:
			if config_name in message_config:
				logger.warning("the kpm archive is missing the {0} file".format(file_name))
				raise KingPhisherInputValidationError('data is missing from the message archive')
			continue
		if not message_config.get(config_name):
			logger.warning("the kpm message configuration is missing the {0} setting".format(config_name))
			raise KingPhisherInputValidationError('data is missing from the message archive')
		arcfile_h = kpm.get_file(file_name)
		file_path = os.path.join(dest_dir, message_config[config_name])
		with open(file_path, 'wb') as file_h:
			shutil.copyfileobj(arcfile_h, file_h)
		message_config[config_name] = file_path

	if 'message_content.html' in kpm.file_names:
		if not 'html_file' in message_config:
			logger.warning('the kpm message configuration is missing the html_file setting')
			raise KingPhisherInputValidationError('data is missing from the message archive')
		arcfile_h = kpm.get_file('message_content.html')
		file_path = os.path.join(dest_dir, message_config['html_file'])
		with open(file_path, 'wb') as file_h:
			file_h.write(message_template_from_kpm(arcfile_h.read(), attachments))
		message_config['html_file'] = file_path
	elif 'html_file' in message_config:
		logger.warning('the kpm archive is missing the message_content.html file')
		raise KingPhisherInputValidationError('data is missing from the message archive')
	kpm.close()
	return message_config
Beispiel #3
0
def message_data_from_kpm(target_file, dest_dir):
	"""
	Retrieve the stored details describing a message from a previously exported
	file.

	:param str target_file: The file to load as a message archive.
	:param str dest_dir: The directory to extract data and attachment files to.
	:return: The restored details from the message config.
	:rtype: dict
	"""
	if not tarfile.is_tarfile(target_file):
		logger.warning('the file is not recognized as a valid tar archive')
		raise KingPhisherInputValidationError('file is not in the correct format')
	tar_h = tarfile.open(target_file)
	member_names = tar_h.getnames()
	attachment_member_names = filter(lambda n: n.startswith('attachments' + os.path.sep), member_names)
	tar_get_file = lambda name: tar_h.extractfile(tar_h.getmember(name))
	attachments = []

	if not 'message_config.json' in member_names:
		logger.warning('the kpm archive is missing the message_config.json file')
		raise KingPhisherInputValidationError('data is missing from the message archive')
	message_config = tar_get_file('message_config.json').read()
	message_config = json.loads(message_config)

	if attachment_member_names:
		attachment_dir = os.path.join(dest_dir, 'attachments')
		if not os.path.isdir(attachment_dir):
			os.mkdir(attachment_dir)
		for file_name in attachment_member_names:
			tarfile_h = tar_get_file(file_name)
			file_name = os.path.basename(file_name)
			file_path = os.path.join(attachment_dir, file_name)
			with open(file_path, 'wb') as file_h:
				shutil.copyfileobj(tarfile_h, file_h)
			attachments.append(file_path)
		logger.debug("extracted {0} attachment file{1} from the archive".format(len(attachments), 's' if len(attachments) > 1 else ''))

	for config_name, file_name in KPM_ARCHIVE_FILES.items():
		if not file_name in member_names:
			if config_name in message_config:
				logger.warning("the kpm archive is missing the {0} file".format(file_name))
				raise KingPhisherInputValidationError('data is missing from the message archive')
			continue
		if not message_config.get(config_name):
			logger.warning("the kpm message configuration is missing the {0} setting".format(config_name))
			raise KingPhisherInputValidationError('data is missing from the message archive')
		tarfile_h = tar_get_file(file_name)
		file_path = os.path.join(dest_dir, message_config[config_name])
		with open(file_path, 'wb') as file_h:
			shutil.copyfileobj(tarfile_h, file_h)
		message_config[config_name] = file_path

	if 'message_content.html' in member_names:
		if not 'html_file' in message_config:
			logger.warning('the kpm message configuration is missing the html_file setting')
			raise KingPhisherInputValidationError('data is missing from the message archive')
		tarfile_h = tar_get_file('message_content.html')
		file_path = os.path.join(dest_dir, message_config['html_file'])
		template = tarfile_h.read()
		template = message_template_from_kpm(template, attachments)
		template_strio = io.BytesIO()
		template_strio.write(template)
		template_strio.seek(os.SEEK_SET)
		with open(file_path, 'wb') as file_h:
			shutil.copyfileobj(template_strio, file_h)
		message_config['html_file'] = file_path
	elif 'html_file' in message_config:
		logger.warning('the kpm archive is missing the message_content.html file')
		raise KingPhisherInputValidationError('data is missing from the message archive')

	return message_config