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