class Plugin(plugins.ClientPlugin):
    authors = ['Spencer McIntyre']  # the plugins author
    classifiers = ['Plugin :: Client'
                   ]  # the list of classifiers for categorization
    title = 'Hello World!'  # the title of the plugin to be shown to users
    description = """
	A 'hello world' plugin to serve as a basic template and demonstration. This
	plugin will display a message box when King Phisher exits.
	"""                                 # a description of the plugin to be shown to users
    homepage = 'https://github.com/securestate/king-phisher-plugins'  # an optional home page
    options = [  # specify options which can be configured through the GUI
        plugins.ClientOptionString(
            'name',  # the name of the option as it will appear in the configuration
            'The name to which to say goodbye.',  # the description of the option as shown to users
            default='Alice Liddle',  # a default value for the option
            display_name='Your Name'  # a name of the option as shown to users
        ),
        plugins.ClientOptionBoolean('validiction',
                                    'Whether or not this plugin say good bye.',
                                    default=True,
                                    display_name='Say Good Bye'),
        plugins.ClientOptionInteger('some_number',
                                    'An example number option.',
                                    default=1337,
                                    display_name='A Number'),
        plugins.ClientOptionPort('tcp_port',
                                 'The TCP port to connect to.',
                                 default=80,
                                 display_name='Connection Port')
    ]
    req_min_py_version = '3.3.0'  # (optional) specify the required minimum version of python
    req_min_version = '1.4.0'  # (optional) specify the required minimum version of king phisher
    req_packages = {  # (optional) specify a dictionary of required package names
        'advancedhttpserver':
        has_ahs  # set from within the exception handler when importing
    }
    req_platforms = ('Linux', 'Windows'
                     )  # (optional) specify the supported platforms
    version = '1.0'  # (optional) specify this plugin's version

    # this is the primary plugin entry point which is executed when the plugin is enabled
    def initialize(self):
        print('Hello World!')
        self.signal_connect('exit', self.signal_exit)
        # it is necessary to return True to indicate that the initialization was successful
        # this allows the plugin to check its options and return false to indicate a failure
        return True

    # this is a cleanup method to allow the plugin to close any open resources
    def finalize(self):
        print('Good Bye World!')

    # the plugin connects this handler to the applications 'exit' signal
    def signal_exit(self, app):
        # check the 'validiction' option in the configuration
        if not self.config['validiction']:
            return
        gui_utilities.show_dialog_info(
            "Good bye {0}!".format(self.config['name']),
            app.get_active_window())
class Plugin(plugins.ClientPlugin):
    authors = ['Jeremy Schoeneman']
    title = 'Sample Set Generator'
    classifiers = ['Plugin :: Client :: Tool']

    description = """
	Brings in a master list and generates a sample set from said list. 
	"""
    homepage = 'https://github.com/securestate/king-phisher-plugins'
    options = [
        plugins.ClientOptionString('master_csv',
                                   'Master list of targets to sample',
                                   display_name='Master CSV File'),
        plugins.ClientOptionString('sample_file',
                                   'CSV file to write the sample set to',
                                   display_name='Sample CSV File',
                                   default='~/sampled.csv'),
        plugins.ClientOptionInteger(
            'sample_size',
            'How many targets to sample',
            display_name='Sample Size',
        )
    ]
    version = '1.0'

    def initialize(self):
        self.add_menu_item('Tools > Create Sample Set', self.sample_setup)
        return True

    def sample_setup(self, _):
        self.logger.info('sample_setup reached')
        if not self.config['master_csv']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Master CSV File" option.')
            return
        if not self.config['sample_file']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Sample CSV File" option.')
            return
        if not self.config['sample_size']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Sample Size" option.')
            return
        self.logger.info('Config passed')

        outfile = self.expand_path(self.config['sample_file'])
        infile = self.expand_path(self.config['master_csv'])

        try:
            # Reads line locations into memory. Takes less memory than reading the whole file at once
            s = [0]
            linelocs = [
                s.append(s[0] + len(n)) or s.pop(0)
                for n in open(self.config['master_csv'])
            ]

            # Pulls the random samples based off the line locations and random
            with open(self.config['master_csv']) as f:
                file_len = sum(1 for l in f)
                chosen = sorted(
                    random.sample(linelocs, self.config['sample_size']))
                sample_set = []
                for offset in chosen:
                    f.seek(offset)
                    sample_set.append(f.readline().strip('\n'))
                    random.shuffle(sample_set)
            f.close()
        except IOError as e:
            self.logger.error('outputting file error', exc_info=True)

        try:
            # Writes to file
            with open(outfile, 'w', newline="\n") as of:
                for sample in sample_set:
                    of.write(sample + '\n')
            of.close()
            self.logger.info('Sample set exported successfully')

        except IOError as e:
            self.logger.error('outputting file error', exc_info=True)
        return

    def expand_path(self, output_file, *args, **kwargs):
        expanded_path = _expand_path(output_file, *args, **kwargs)
        try:
            expanded_path = mailer.render_message_template(
                expanded_path, self.application.config)
        except jinja2.exceptions.TemplateSyntaxError as error:
            self.logger.error(
                "jinja2 syntax error ({0}) in directory: {1}".format(
                    error.message, output_file))
            gui_utilities.show_dialog_error(
                'Error', self.application.get_active_window(),
                'Error creating the CSV file.')
            return None
        except ValueError as error:
            self.logger.error("value error ({0}) in directory: {1}".format(
                error, output_file))
            gui_utilities.show_dialog_error(
                'Error', self.application.get_active_window(),
                'Error creating the CSV file.')
            return None
        return expanded_path
Example #3
0
class Plugin(plugins.ClientPlugin):
    version = __version__
    authors = ['Spencer McIntyre', 'Erik Daguerre']
    title = 'Anonymous Data Collection'
    description = """
	Generate anonymized data regarding the campaigns that have been run to
	submit to SecureState for collection and aggregation. Your submissions will
	help to fuel research into the effectiveness of phishing campaigns and user
	awareness. No identifying information regarding your campaigns will be
	collected.

	This plugin will generate and submit anonymous data containing statistics
	regarding the size and success of campaigns. If you would like credit when
	the research results are published, please include either your or your
	companies attribution information in an email to
	[email protected].

	We greatly appreciate your data contributions to the King Phisher project!
	"""
    homepage = 'https://github.com/securestate/king-phisher'
    options = [
        plugins.ClientOptionInteger(
            'cycle_days',
            'Number of days between reminders to submit data.',
            default=90,
            display_name='Cycle in Days',
            adjustment=Gtk.Adjustment(0, 1, 365, 1, 10, 0))
    ]

    def initialize(self):
        config = self.config
        self.signal_connect('server-connected', self.signal_server_connected)
        if 'last_date' not in config and self.application.rpc:
            self.prompt_and_generate()
            config['last_date'] = datetime.datetime.utcnow()
        return True

    def signal_server_connected(self, _):
        config = self.config
        if 'last_date' in config and datetime.datetime.utcnow() - config[
                'last_date'] < datetime.timedelta(days=config['cycle_days']):
            return
        self.prompt_and_generate()

    def prompt_and_generate(self):
        active_window = self.application.get_active_window()
        dialog_txt = 'Would you like to submit anonymized data to SecureState for research purposes?'
        if not gui_utilities.show_dialog_yes_no('Submit Phishing Data',
                                                active_window, dialog_txt):
            return

        get_stats = StatsGenerator(self.application.rpc)
        stats = get_stats.generate_stats()
        stats = stats.encode('utf-8')
        stats = bz2.compress(stats)
        stats = base64.b64encode(stats)
        stats = stats.decode('utf-8')
        stats = '\n'.join(textwrap.wrap(stats, width=80))

        try:
            response = requests.post(
                'https://forms.hubspot.com/uploads/form/v2/147369/f374545b-987f-44ce-82e5-889293a0e6b3',
                data={
                    'email': '*****@*****.**',
                    'statistics': stats
                })
            assert response.ok
        except (AssertionError, requests.exceptions.RequestException):
            self.logger.error('failed to submit data', exc_info=True)
            gui_utilities.show_dialog_error(
                'Error Submitting Data', active_window,
                'An Error occurred while submitting the data.')
            return
        gui_utilities.show_dialog_info(
            'Submitted Data', active_window,
            'Successfully submitted anonymized phishing data.\nThank you for your support!'
        )
        self.config['last_date'] = datetime.datetime.utcnow()
class Plugin(plugins.ClientPlugin):
    authors = ['Jeremy Schoeneman']
    title = 'Sample Set Generator'
    classifiers = ['Plugin :: Client :: Tool']
    description = """
	Brings in a master list and generates a sample set from said list.
	"""
    homepage = 'https://github.com/securestate/king-phisher-plugins'
    options = [
        plugins.ClientOptionPath('master_csv',
                                 'Master list of targets to sample',
                                 display_name='Master CSV File',
                                 path_type='file-open'),
        plugins.ClientOptionPath('sample_file',
                                 'CSV file to write the sample set to',
                                 display_name='Sample CSV File',
                                 default='~/sampled.csv',
                                 path_type='file-save'),
        plugins.ClientOptionInteger('sample_size',
                                    'How many targets to sample',
                                    display_name='Sample Size',
                                    default=1)
    ]
    version = '1.0'

    def initialize(self):
        self.add_menu_item('Tools > Create Sample Set', self.sample_setup)
        return True

    def sample_setup(self, _):
        self.logger.info('sample_setup reached')
        if not self.config['master_csv']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Master CSV File" option.')
            return
        if not self.config['sample_file']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Sample CSV File" option.')
            return
        if not self.config['sample_size']:
            gui_utilities.show_dialog_error(
                'Missing Option', self.application.get_active_window(),
                'Please configure the "Sample Size" option.')
            return
        self.logger.info('configuration check passed')

        outfile = self.expand_path(self.config['sample_file'])

        try:
            # Reads line locations into memory. Takes less memory than reading the whole file at once
            linelocs = collections.deque([0])
            with open(self.config['master_csv'],
                      'r') as in_file_h, open(outfile, 'w') as out_file_h:
                # Reads line locations into memory. Takes less memory than reading the whole file at once
                for line in in_file_h:
                    linelocs.append(linelocs[-1] + len(line))

                # Pulls the random samples based off the line locations and random
                chosen = random.sample(linelocs, self.config['sample_size'])
                random.shuffle(chosen)
                for offset in chosen:
                    in_file_h.seek(offset)
                    out_file_h.write(in_file_h.readline())
        except IOError:
            self.logger.error(
                'encountered io error while generating the sample set',
                exc_info=True)
        return

    def expand_path(self, output_file, *args, **kwargs):
        expanded_path = _expand_path(output_file, *args, **kwargs)
        try:
            expanded_path = mailer.render_message_template(
                expanded_path, self.application.config)
        except jinja2.exceptions.TemplateSyntaxError as error:
            self.logger.error(
                "jinja2 syntax error ({0}) in directory: {1}".format(
                    error.message, output_file))
            gui_utilities.show_dialog_error(
                'Error', self.application.get_active_window(),
                'Error creating the CSV file.')
            return None
        except ValueError as error:
            self.logger.error("value error ({0}) in directory: {1}".format(
                error, output_file))
            gui_utilities.show_dialog_error(
                'Error', self.application.get_active_window(),
                'Error creating the CSV file.')
            return None
        return expanded_path
Example #5
0
class Plugin(plugins.ClientPlugin):
    version = __version__
    authors = ['Spencer McIntyre', 'Erik Daguerre']
    title = 'Anonymous Data Collection'
    description = """
	Generate anonymized data regarding the campaigns that have been run to
	submit to SecureState for collection and aggregation. Your submissions will
	help to fuel research into the effectiveness of phishing campaigns and user
	awareness. No identifying information regarding your campaigns will be
	collected.

	This plugin will generate a text file with the anonymous data. After the
	data file has been saved, please email it to [email protected].
	If you would like credit when the research results are published, please
	include you or your companies attribution information.

	We greatly appreciate your data contributions to the King Phisher project!
	"""
    homepage = 'https://github.com/securestate/king-phisher'
    options = [
        plugins.ClientOptionInteger(
            'cycle_days',
            'Number of days between reminders to submit data.',
            default=90,
            display_name='Cycle in Days',
            adjustment=Gtk.Adjustment(0, 1, 365, 1, 10, 0))
    ]

    def initialize(self):
        config = self.config
        self.signal_connect('server-connected', self.signal_server_connected)
        if 'last_date' not in config and self.application.rpc:
            self.prompt_and_generate()
            config['last_date'] = datetime.datetime.utcnow()
        return True

    def signal_server_connected(self, _):
        config = self.config
        if 'last_date' in config and datetime.datetime.utcnow() - config[
                'last_date'] < datetime.timedelta(days=config['cycle_days']):
            return
        self.prompt_and_generate()

    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()