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
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 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()
def signal_exit(self, app): if not self.config['validiction']: return gui_utilities.show_dialog_info( "Good bye {0}!".format(self.config['name']), app.get_active_window() )
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.' )
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()
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()
def enrollment_remove(self, _): rpc = self.application.rpc this_user = rpc.graphql_file(user_gql_query, {'name': rpc.username})['db']['user'] if this_user['otpSecret'] is None: gui_utilities.show_dialog_info( 'Not Enrolled', self.application.get_active_window(), 'This account is not currently enrolled in two factor\n'\ + 'authentication. There are no changes to make.' ) return remove = gui_utilities.show_dialog_yes_no( 'Already Enrolled', self.application.get_active_window(), 'Are you sure you want to unenroll in TOTP? This will remove\n'\ + 'two factor authentication on your account.' ) if not remove: return rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': None}) gui_utilities.show_dialog_info( 'TOTP Unenrollment', self.application.get_active_window(), 'Successfully removed the TOTP secret. Your account is now unenrolled\n'\ + 'in two factor authentication. You will no longer be prompted to enter\n'\ + 'the value when you login.' )
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())
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.' )
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() try: test_webserver_url( target_url, self.config['server_config']['server.secret_id']) except Exception as error: error_description = None if isinstance(error, urllib.request.URLError) and hasattr( error, 'reason') and isinstance(error.reason, Exception): error = error.reason if isinstance(error, urllib.request.HTTPError) and error.getcode(): self.logger.warning("verify url HTTPError: {0} {1}".format( error.getcode(), error.reason)) error_description = "HTTP status {0} {1}".format( error.getcode(), error.reason) elif isinstance(error, socket.gaierror): self.logger.warning( 'verify url attempt failed, socket.gaierror') error_description = error.args[-1] elif isinstance(error, socket.timeout): self.logger.warning( 'verify url attempt failed, connection timeout occurred') error_description = 'Connection timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) gui_utilities.show_dialog_warning( 'Unable To Open The Web Server URL', self.parent, error_description) return gui_utilities.show_dialog_info( 'Successfully Opened The Web Server URL', self.parent) return
def signal_server_connected(self, _): release = get_latest_release() if release is None: self.logger.error('failed to find the latest release') return self.logger.info('found latest release: ' + release['tag_name']) client_version = StrictVersion(version.distutils_version) release_version = release_to_version(release) server_version = self.application.rpc('version')['version_info'] server_version = StrictVersion( "{major}.{minor}.{micro}".format(**server_version)) out_of_date = None if release_version > client_version: out_of_date = 'Client' elif release_version > server_version: out_of_date = 'Server' if out_of_date is None: return gui_utilities.show_dialog_info( 'New Version Available', self.application.main_window, "The King Phisher {part} is out of date,\n" "<a href=\"{release[html_url]}\">{release[tag_name]}</a> is now available." .format(part=out_of_date, release=release), secondary_use_markup=True)
def enrollment_remove(self, _): rpc = self.application.rpc this_user = rpc.remote_table_row('users', rpc.username) if this_user.otp_secret is None: gui_utilities.show_dialog_info( 'Not Enrolled', self.application.get_active_window(), 'This account is not currently enrolled in two factor\n'\ + 'authentication. There are no changes to make.' ) return remove = gui_utilities.show_dialog_yes_no( 'Already Enrolled', self.application.get_active_window(), 'Are you sure you want to unenroll in TOTP? This will remove\n'\ + 'two factor authentication on your account.' ) if not remove: return this_user.otp_secret = None this_user.commit() gui_utilities.show_dialog_info( 'TOTP Unenrollment', self.application.get_active_window(), 'Successfully removed the TOTP secret. Your account is now unenrolled\n'\ + 'in two factor authentication. You will no longer be prompted to enter\n'\ + 'the value when you login.' )
def signal_check_domain(self, _): email = str(self.application.config['mailer.source_email']) user, at, domain = email.partition('@') try: self.logger.debug('checking email domain') result = getaddrinfo(domain, None) if result: try: info = whois.whois(domain) self.logger.info('email domain valid') gui_utilities.show_dialog_info( 'Email Domain Valid', self.application.get_active_window(), 'Domain registered to: ' + info.name + '\n', 'Name Servers: ' + info.name_servers + '\n', 'Contact Email: ' + info.emails) except Exception as err: self.logger.info('whois lookup unavailable') gui_utilities.show_dialog_info( 'Email Domain Valid', self.application.get_active_window(), 'Your domain is valid, however whois lookup failed.') else: self.logger.info('email domain valid') except Exception as err: if not gui_utilities.show_dialog_yes_no( 'Spoofed Email Domain Doesn\'t exist, continue?', self.application.get_active_window()): return return True
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() error_description = None try: response = test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except requests.exceptions.RequestException as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning('verify url attempt failed, could not connect') error_description = 'Could not connect to the server' elif isinstance(error, requests.exceptions.Timeout): self.logger.warning('verify url attempt failed, a timeout occurred') error_description = 'The HTTP request timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) error_description = 'An unknown verify URL exception occurred' else: if response.status_code < 200 or response.status_code > 299: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) else: self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) else: gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def rename_campaign(self): campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id']) prompt = dialogs.TextEntryDialog.build_prompt(self.config, self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name) response = prompt.interact() if response == None or response == campaign.name: return self.rpc('campaigns/set', self.config['campaign_id'], ('name',), (response,)) gui_utilities.show_dialog_info('Campaign Name Updated', self, 'The campaign name was successfully changed')
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 = gui_utilities.UtilityFileChooser( '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 = gui_utilities.UtilityFileChooser('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( filter(lambda k: k.startswith(config_prefix), self.config.keys())) config_types = dict(zip(config_keys, map(type, 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() config_tab.objects_load_from_config() gui_utilities.show_dialog_info('Successfully imported the message', self.parent)
def make_preview(self, _): if self.missing_options(): return False if not self.build_pdf(): gui_utilities.show_dialog_info( '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.')
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.")
def campaign_rename(self): """ Show a dialog prompting the user to for the a new name to assign to the currently selected campaign. """ campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id']) prompt = dialogs.TextEntryDialog.build_prompt(self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name) response = prompt.interact() if response is None or response == campaign.name: return self.rpc('db/table/set', 'campaigns', self.config['campaign_id'], 'name', response) gui_utilities.show_dialog_info('Campaign Name Updated', self.get_active_window(), 'The campaign name was successfully changed.')
def campaign_rename(self): """ Show a dialog prompting the user to for the a new name to assign to the currently selected campaign. """ campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id']) prompt = dialogs.TextEntryDialog.build_prompt(self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name) response = prompt.interact() if response == None or response == campaign.name: return self.rpc('db/table/set', 'campaigns', self.config['campaign_id'], 'name', response) gui_utilities.show_dialog_info('Campaign Name Updated', self.get_active_window(), 'The campaign name was successfully changed.')
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() try: parsed_url = urlparse.urlparse(target_url) query = urlparse.parse_qs(parsed_url.query) query['id'] = [self.config['server_config']['server.secret_id']] query = urllib.urlencode(query, True) target_url = urlparse.urlunparse((parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, query, parsed_url.fragment)) urllib2.urlopen(target_url, timeout=5) except: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent) return gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def rename_campaign(self): campaign = self.rpc.remote_table_row('campaigns', self.config['campaign_id']) prompt = dialogs.TextEntryDialog.build_prompt( self.config, self, 'Rename Campaign', 'Enter the new campaign name:', campaign.name) response = prompt.interact() if response == None or response == campaign.name: return self.rpc('campaigns/set', self.config['campaign_id'], ('name', ), (response, )) gui_utilities.show_dialog_info( 'Campaign Name Updated', self, 'The campaign name was successfully changed')
def make_preview(self, _): if self.missing_options(): return False if not self.build_pdf(): gui_utilities.show_dialog_info( '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.' )
def _finialize_settings_dashboard(self): dashboard_changed = False for dash_position in ['top_left', 'top_right', 'bottom']: combobox = self.gtk_builder_get('combobox_dashboard_' + dash_position) ti = combobox.get_active_iter() if not ti: continue graph_providers = combobox.get_model() graph_name = graph_providers[ti][1] if self.config.get('dashboard.' + dash_position) == graph_name: continue self.config['dashboard.' + dash_position] = graph_name dashboard_changed = True if dashboard_changed: gui_utilities.show_dialog_info('The dashboard layout has been updated.', self.parent, 'The new settings will be applied the next time the application starts.')
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 = gui_utilities.FileChooser('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 = gui_utilities.FileChooser('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, map(type, 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() config_tab.objects_load_from_config() gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message.')
def check_totp(self, _, window, entry, new_otp, this_user): if not new_otp.verify(entry.get_text().strip()): gui_utilities.show_dialog_warning( 'Incorrect TOTP', self.application.get_active_window(), 'The specified TOTP code is invalid. Make sure your time\n'\ + 'is correct, rescan the QR code and try again.' ) return self.application.rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': new_otp.secret}) gui_utilities.show_dialog_info( 'TOTP Enrollment', self.application.get_active_window(), 'Successfully set the TOTP secret. Your account is now enrolled\n'\ + 'in two factor authentication. You will be prompted to enter the\n' + 'value the next time you login.' ) window.destroy()
def check_totp(self, _, window, entry, new_otp, this_user): if not new_otp.verify(entry.get_text().strip()): gui_utilities.show_dialog_warning( 'Incorrect TOTP', self.application.get_active_window(), 'The specified TOTP code is invalid. Make sure your time\n'\ + 'is correct, rescan the QR code and try again.' ) return rpc.remote_table_row_set('users', this_user['id'], {'otp_secret': new_otp.secret}) gui_utilities.show_dialog_info( 'TOTP Enrollment', self.application.get_active_window(), 'Successfully set the TOTP secret. Your account is now enrolled\n'\ + 'in two factor authentication. You will be prompted to enter the\n' + 'value the next time you login.' ) window.destroy()
def signal_button_clicked_verify_spf(self, button): sender_email = self.gobjects['entry_source_email_smtp'].get_text() if not utilities.is_valid_email_address(sender_email): gui_utilities.show_dialog_warning('Warning', self.parent, 'Can not check SPF records for an invalid source email address.\n') return True spf_test_ip = mailer.guess_smtp_server_address(self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: gui_utilities.show_dialog_warning('Warning', self.parent, 'Skipping spf policy check because the smtp server address could not be reliably detected') return True spf_test_sender, spf_test_domain = sender_email.split('@') try: spf_test = spf.SenderPolicyFramework(spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: gui_utilities.show_dialog_warning('Warning', self.parent, "Done, encountered exception: {0}.\n".format(error.__class__.__name__)) return True if not spf_result: gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'No SPF records found.') else: if spf_result is 'fail': gui_utilities.show_dialog_info('SPF Check Results:', self.parent, 'SPF exists with a hard fail. Your messages will probably be blocked.') elif spf_result is 'softfail': gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'SPF Exists with a soft fail. Your messages have strong possiblity of being blocked. Check your logs.') return True return True
def signal_server_connected(self, _): release = get_latest_release() self.logger.info('found latest release: ' + release['tag_name']) client_version = StrictVersion(version.distutils_version) release_version = release_to_version(release) server_version = self.application.rpc('version')['version_info'] server_version = StrictVersion("{major}.{minor}.{micro}".format(**server_version)) out_of_date = None if release_version > client_version: out_of_date = 'Client' elif release_version > server_version: out_of_date = 'Server' if out_of_date is None: return gui_utilities.show_dialog_info( 'New Version Available', self.application.main_window, "The King Phisher {part} is out of date,\n" "<a href=\"{release[html_url]}\">{release[tag_name]}</a> is now available.".format(part=out_of_date, release=release), secondary_use_markup=True )
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()
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() try: test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except Exception as error: error_description = None if isinstance(error, urllib.request.URLError) and hasattr(error, 'reason') and isinstance(error.reason, Exception): error = error.reason if isinstance(error, urllib.request.HTTPError) and error.getcode(): self.logger.warning("verify url HTTPError: {0} {1}".format(error.getcode(), error.reason)) error_description = "HTTP status {0} {1}".format(error.getcode(), error.reason) elif isinstance(error, socket.gaierror): self.logger.warning('verify url attempt failed, socket.gaierror') error_description = error.args[-1] elif isinstance(error, socket.timeout): self.logger.warning('verify url attempt failed, connection timeout occurred') error_description = 'Connection timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) return gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
def signal_button_clicked_verify(self, button): target_url = self.gobjects["entry_webserver_url"].get_text() error_description = None if re.match(r"^\s+", target_url): target_url = target_url.strip() self.gobjects["entry_webserver_url"].set_text(target_url) for _ in range(1): if not target_url.strip().startswith("http"): error_description = "The web server URL is invalid" break try: response = test_webserver_url(target_url, self.config["server_config"]["server.secret_id"]) except (requests.exceptions.ConnectionError, requests.exceptions.RequestException) as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning("verify url attempt failed, could not connect") error_description = "Could not connect to the server" elif isinstance(error, requests.exceptions.Timeout): self.logger.warning("verify url attempt failed, a timeout occurred") error_description = "The HTTP request timed out" else: self.logger.warning("unknown verify url exception: " + repr(error)) error_description = "An unknown verify URL exception occurred" break if not response.ok: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) break self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning("Unable To Open The Web Server URL", self.parent, error_description) else: gui_utilities.show_dialog_info("Successfully Opened The Web Server URL", self.parent) return
def signal_button_clicked_verify(self, button): target_url = self.gobjects['entry_webserver_url'].get_text() error_description = None if re.match(r'^\s+', target_url): target_url = target_url.strip() self.gobjects['entry_webserver_url'].set_text(target_url) for _ in range(1): if not target_url.strip().startswith('http'): error_description = 'The web server URL is invalid' break try: response = test_webserver_url(target_url, self.config['server_config']['server.secret_id']) except (requests.exceptions.ConnectionError, requests.exceptions.RequestException) as error: if isinstance(error, requests.exceptions.ConnectionError): self.logger.warning('verify url attempt failed, could not connect') error_description = 'Could not connect to the server' elif isinstance(error, requests.exceptions.Timeout): self.logger.warning('verify url attempt failed, a timeout occurred') error_description = 'The HTTP request timed out' else: self.logger.warning('unknown verify url exception: ' + repr(error)) error_description = 'An unknown verify URL exception occurred' break if not response.ok: self.logger.warning("verify url HTTP error: {0} {1}".format(response.status_code, response.reason)) error_description = "HTTP status {0} {1}".format(response.status_code, response.reason) break self.logger.debug("verify url HTTP status: {0} {1}".format(response.status_code, response.reason)) if error_description: gui_utilities.show_dialog_warning('Unable To Open The Web Server URL', self.parent, error_description) else: gui_utilities.show_dialog_info('Successfully Opened The Web Server URL', self.parent) return
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.')
def signal_precheck(self, mailer_tab): email = str(self.application.config['mailer.source_email']) user, _, domain = email.partition('@') self.logger.debug("checking email domain: {0}".format(domain)) if not domain_has_mx_record(domain): response = gui_utilities.show_dialog_yes_no( 'Invalid Email Domain', self.application.get_active_window(), 'The source email domain does not exist. Continue?') if not response: return False text_insert = mailer_tab.tabs['send_messages'].text_insert text_insert( "Checking the WHOIS record for domain '{0}'... ".format(domain)) try: info = whois.whois(domain) except Exception as error: text_insert("done, encountered exception: {0}.\n".format( error.__class__.__name__)) self.logger.error( "whois lookup failed for domain: {0}".format(domain), exc_info=True) response = gui_utilities.show_dialog_info( 'Whois Lookup Failed', self.application.get_active_window(), 'The domain is valid, however the whois lookup failed. Continue?' ) return response if any(info.values()): text_insert('done, record found.\nWHOIS Record Overview:\n') text_insert(" Domain registered to: {0!r}\n".format(info.name)) if info.name_servers: text_insert(" Name Servers: {0}\n".format(', '.join( info.name_servers))) if info.emails: text_insert(" Contact Email: {0}\n".format( info.emails)) else: text_insert('done, no record found.\n') return True
def signal_precheck(self, mailer_tab): email = str(self.application.config['mailer.source_email']) user, _, domain = email.partition('@') self.logger.debug("checking email domain: {0}".format(domain)) if not domain_has_mx_record(domain): response = gui_utilities.show_dialog_yes_no( 'Invalid Email Domain', self.application.get_active_window(), 'The source email domain does not exist. Continue?' ) if not response: return False text_insert = mailer_tab.tabs['send_messages'].text_insert text_insert("Checking the WHOIS record for domain '{0}'... ".format(domain)) try: info = whois.whois(domain) except Exception as error: text_insert("done, encountered exception: {0}.\n".format(error.__class__.__name__)) self.logger.error("whois lookup failed for domain: {0}".format(domain), exc_info=True) response = gui_utilities.show_dialog_info( 'Whois Lookup Failed', self.application.get_active_window(), 'The domain is valid, however the whois lookup failed. Continue?' ) return response if any(info.values()): text_insert('done, record found.\nWHOIS Record Overview:\n') text_insert(" Domain registered to: {0!r}\n".format(info.name)) if info.name_servers: text_insert(" Name Servers: {0}\n".format(', '.join(info.name_servers))) if info.emails: text_insert(" Contact Email: {0}\n".format(info.emails)) else: text_insert('done, no record found.\n') return True
def signal_assistant_apply(self, _): self._close_ready = False # have to do it this way because the next page will be selected when the apply signal is complete set_current_page = lambda page_name: self.assistant.set_current_page( max(0, self._page_titles[page_name] - 1)) # get and validate the campaign name campaign_name = self.gobjects['entry_campaign_name'].get_text() campaign_name = campaign_name.strip() if not campaign_name: gui_utilities.show_dialog_error( 'Invalid Campaign Name', self.parent, 'A valid campaign name must be specified.') set_current_page('Basic Settings') return True campaign = self._get_campaign_by_name(campaign_name) if campaign and campaign['id'] != self.campaign_id: gui_utilities.show_dialog_error( 'Invalid Campaign Name', self.parent, 'A unique campaign name must be specified.') set_current_page('Basic Settings') return True properties = {} # validate the credential validation regular expressions for field in ('username', 'password', 'mfa_token'): regex = self.gobjects['entry_validation_regex_' + field].get_text() if regex: try: re.compile(regex) except re.error: label = self.gobjects['label_validation_regex_' + field].get_text() gui_utilities.show_dialog_error( 'Invalid Regex', self.parent, "The '{0}' regular expression is invalid.".format( label)) return True else: regex = None # keep empty strings out of the database properties['credential_regex_' + field] = regex # validate the company company_id = None if self.gobjects['radiobutton_company_existing'].get_active(): company_id = self._get_company_existing_id() if company_id is None: gui_utilities.show_dialog_error( 'Invalid Company', self.parent, 'A valid existing company must be specified.') set_current_page('Company') return True elif self.gobjects['radiobutton_company_new'].get_active(): company_id = self._get_company_new_id() if company_id is None: gui_utilities.show_dialog_error( 'Invalid Company', self.parent, 'The new company settings are invalid.') set_current_page('Company') return True # get and validate the campaign expiration expiration = None if self.gobjects['checkbutton_expire_campaign'].get_property('active'): expiration = datetime.datetime.combine( gui_utilities.gtk_calendar_get_pydate( self.gobjects['calendar_campaign_expiration']), self._expiration_time.time) expiration = utilities.datetime_local_to_utc(expiration) if self.is_new_campaign and expiration <= datetime.datetime.now(): gui_utilities.show_dialog_error( 'Invalid Campaign Expiration', self.parent, 'The expiration date is set in the past.') set_current_page('Expiration') return True # point of no return campaign_description = self.get_entry_value('campaign_description') if self.campaign_id: properties['name'] = self.campaign_name properties['description'] = campaign_description cid = self.campaign_id else: try: cid = self.application.rpc('campaign/new', campaign_name, description=campaign_description) properties['name'] = campaign_name except advancedhttpserver.RPCError as error: if not error.is_remote_exception: raise error if not error.remote_exception[ 'name'] == 'exceptions.ValueError': raise error error_message = error.remote_exception.get( 'message', 'an unknown error occurred').capitalize() + '.' gui_utilities.show_dialog_error('Failed To Create Campaign', self.parent, error_message) set_current_page('Basic Settings') return True self.application.emit('campaign-created', cid) properties['campaign_type_id'] = self._get_tag_from_combobox( self.gobjects['combobox_campaign_type'], 'campaign_types') properties['company_id'] = company_id properties['expiration'] = expiration properties['max_credentials'] = ( 1 if self.gobjects['checkbutton_reject_after_credentials']. get_property('active') else None) self.application.rpc('db/table/set', 'campaigns', cid, tuple(properties.keys()), tuple(properties.values())) should_subscribe = self.gobjects[ 'checkbutton_alert_subscribe'].get_property('active') if should_subscribe != self.application.rpc( 'campaign/alerts/is_subscribed', cid): if should_subscribe: self.application.rpc('campaign/alerts/subscribe', cid) else: self.application.rpc('campaign/alerts/unsubscribe', cid) self.application.emit('campaign-changed', cid) old_cid = self.config.get('campaign_id') self.config['campaign_id'] = cid self.config['campaign_name'] = properties['name'] _kpm_pathing = self._get_kpm_path() if all(_kpm_pathing): if not self.application.main_tabs['mailer'].emit( 'message-data-import', _kpm_pathing.kpm_file, _kpm_pathing.destination_folder): gui_utilities.show_dialog_info( 'Failure', self.parent, 'Failed to import the message configuration.') else: gui_utilities.show_dialog_info( 'Success', self.parent, 'Successfully imported the message configuration.') if self._ssl_status['hasSni']: combobox_url_scheme = self.gobjects['combobox_url_scheme'] active = combobox_url_scheme.get_active() if active != -1: url_scheme = _ModelURLScheme( *combobox_url_scheme.get_model()[active]) if url_scheme.name == 'https': hostname = gui_utilities.gtk_combobox_get_entry_text( self.gobjects['combobox_url_hostname']) if not self.application.rpc('ssl/sni_hostnames/load', hostname): gui_utilities.show_dialog_error( 'Failure', self.parent, 'Failed to load an SSL certificate for the specified hostname.' ) self.config['mailer.webserver_url'] = self._get_webserver_url() self.application.emit('campaign-set', old_cid, cid) self._close_ready = True return
def signal_assistant_apply(self, _): self._close_ready = False # have to do it this way because the next page will be selected when the apply signal is complete set_current_page = lambda page_name: self.assistant.set_current_page(max(0, self._page_titles[page_name] - 1)) # get and validate the campaign name campaign_name = self.gobjects['entry_campaign_name'].get_text() campaign_name = campaign_name.strip() if not campaign_name: gui_utilities.show_dialog_error('Invalid Campaign Name', self.parent, 'A unique and valid campaign name must be specified.') set_current_page('Basic Settings') return True properties = {} # validate the credential validation regular expressions for field in ('username', 'password', 'mfa_token'): regex = self.gobjects['entry_validation_regex_' + field].get_text() if regex: try: re.compile(regex) except re.error: label = self.gobjects['label_validation_regex_' + field].get_text() gui_utilities.show_dialog_error('Invalid Regex', self.parent, "The '{0}' regular expression is invalid.".format(label)) return True else: regex = None # keep empty strings out of the database properties['credential_regex_' + field] = regex # validate the company company_id = None if self.gobjects['radiobutton_company_existing'].get_active(): company_id = self._get_company_existing_id() if company_id is None: gui_utilities.show_dialog_error('Invalid Company', self.parent, 'A valid existing company must be specified.') set_current_page('Company') return True elif self.gobjects['radiobutton_company_new'].get_active(): company_id = self._get_company_new_id() if company_id is None: gui_utilities.show_dialog_error('Invalid Company', self.parent, 'The new company settings are invalid.') set_current_page('Company') return True # get and validate the campaign expiration expiration = None if self.gobjects['checkbutton_expire_campaign'].get_property('active'): expiration = datetime.datetime.combine( gui_utilities.gtk_calendar_get_pydate(self.gobjects['calendar_campaign_expiration']), self._expiration_time.time ) expiration = utilities.datetime_local_to_utc(expiration) if self.is_new_campaign and expiration <= datetime.datetime.now(): gui_utilities.show_dialog_error('Invalid Campaign Expiration', self.parent, 'The expiration date is set in the past.') set_current_page('Expiration') return True # point of no return campaign_description = self.get_entry_value('campaign_description') if self.campaign_id: properties['name'] = self.campaign_name properties['description'] = campaign_description cid = self.campaign_id else: try: cid = self.application.rpc('campaign/new', campaign_name, description=campaign_description) properties['name'] = campaign_name except advancedhttpserver.RPCError as error: if not error.is_remote_exception: raise error if not error.remote_exception['name'] == 'exceptions.ValueError': raise error error_message = error.remote_exception.get('message', 'an unknown error occurred').capitalize() + '.' gui_utilities.show_dialog_error('Failed To Create Campaign', self.parent, error_message) set_current_page('Basic Settings') return True self.application.emit('campaign-created', cid) properties['campaign_type_id'] = self._get_tag_from_combobox(self.gobjects['combobox_campaign_type'], 'campaign_types') properties['company_id'] = company_id properties['expiration'] = expiration properties['max_credentials'] = (1 if self.gobjects['checkbutton_reject_after_credentials'].get_property('active') else None) self.application.rpc('db/table/set', 'campaigns', cid, tuple(properties.keys()), tuple(properties.values())) should_subscribe = self.gobjects['checkbutton_alert_subscribe'].get_property('active') if should_subscribe != self.application.rpc('campaign/alerts/is_subscribed', cid): if should_subscribe: self.application.rpc('campaign/alerts/subscribe', cid) else: self.application.rpc('campaign/alerts/unsubscribe', cid) self.application.emit('campaign-changed', cid) old_cid = self.config.get('campaign_id') self.config['campaign_id'] = cid self.config['campaign_name'] = properties['name'] _kpm_pathing = self._get_kpm_path() if all(_kpm_pathing): if not self.application.main_tabs['mailer'].emit('message-data-import', _kpm_pathing.kpm_file, _kpm_pathing.destination_folder): gui_utilities.show_dialog_info('Failure', self.parent, 'Failed to import the message configuration.') else: gui_utilities.show_dialog_info('Success', self.parent, 'Successfully imported the message configuration.') url_model, url_iter = self.gobjects['treeselection_url_selector'].get_selected() if url_iter: selected_row = [] for column_n in range(0, url_model.get_n_columns()): selected_row.append(url_model.get_value(url_iter, column_n)) selected_row = _ModelNamedRow(*selected_row) self.config['mailer.webserver_url'] = selected_row.url self.application.emit('campaign-set', old_cid, cid) self._close_ready = True return