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', '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
示例#3
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
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(plugins.ClientPlugin):
    authors = ['Spencer McIntyre']
    title = 'Campaign Message Configuration Manager'
    description = """
	Store campaign message configurations for their respective campaigns. This
	allows users to switch between campaigns while keeping each of the message
	configurations and restoring them when the user returns to the original
	campaign. New campaigns can either be created with customizable default
	settings or from the existing configuration (see the "Transfer Settings"
	option).
	"""
    homepage = 'https://github.com/securestate/king-phisher-plugins'
    options = [
        plugins.ClientOptionBoolean(
            'transfer_options',
            'Whether or not to keep the current settings for new campaigns.',
            default=True,
            display_name='Transfer Settings')
    ]
    req_min_version = '1.10.0b1'
    version = '1.0.1'

    def initialize(self):
        self.signal_connect('campaign-set', self.signal_kpc_campaign_set)
        self.storage = self.load_storage()

        # create the submenu for setting and clearing the default config
        submenu = 'Tools > Message Configuration'
        self.add_submenu(submenu)
        self.menu_items = {
            'set_defaults':
            self.add_menu_item(submenu + ' > Set Defaults',
                               self.menu_item_set_defaults),
            'clear_defaults':
            self.add_menu_item(submenu + ' > Clear Defaults',
                               self.menu_item_clear_defaults)
        }
        return True

    def finalize(self):
        self.set_campaign_config(self.get_current_config(),
                                 self.application.config['campaign_id'])
        self.save_storage()

    def menu_item_clear_defaults(self, _):
        proceed = gui_utilities.show_dialog_yes_no(
         'Clear Default Campaign Configuration?',
         self.application.get_active_window(),
         'Are you sure you want to clear the default\n'\
          + 'message configuration for new campaigns?'
        )
        if not proceed:
            return
        self.storage['default'] = {}

    def menu_item_set_defaults(self, _):
        proceed = gui_utilities.show_dialog_yes_no(
         'Set The Default Campaign Configuration?',
         self.application.get_active_window(),
         'Are you sure you want to set the default\n'\
          + 'message configuration for new campaigns?'
        )
        if not proceed:
            return
        self.storage['default'] = self.get_current_config()

    @property
    def storage_file_path(self):
        """The path on disk of where to store the plugin's data."""
        return os.path.join(self.application.user_data_path,
                            'campaign_message_config.json')

    def load_default_config(self):
        """
		Load the default configuration to use when settings are missing. This
		will load the user's configured defaults and fail back to the core ones
		distributed with the application.

		:return: The default configuration.
		:rtype: dict
		"""
        default_client_config = find.data_file('client_config.json')
        with open(default_client_config, 'r') as tmp_file:
            default_client_config = serializers.JSON.load(tmp_file)

        users_defaults = self.storage['default']
        for key, value in users_defaults.items():
            if not is_managed_key(key):
                continue
            if key not in default_client_config:
                continue
            default_client_config[key] = value
        return default_client_config

    def load_storage(self):
        """
		Load this plugin's stored data from disk.

		:return: The plugin's stored data.
		:rtype: dict
		"""
        storage = {'campaigns': {}, 'default': {}}
        file_path = self.storage_file_path
        if os.path.isfile(file_path) and os.access(file_path, os.R_OK):
            self.logger.debug(
                'loading campaign messages configuration file: ' + file_path)
            with open(file_path, 'r') as file_h:
                storage = serializers.JSON.load(file_h)
        else:
            self.logger.debug('campaigns configuration file not found')
        return storage

    def save_storage(self):
        """Save this plugin's stored data to disk."""
        file_path = self.storage_file_path
        self.logger.debug('writing campaign messages configuration file: ' +
                          file_path)
        with open(file_path, 'w') as file_h:
            serializers.JSON.dump(self.storage, file_h, pretty=True)

    def get_campaign_config(self, campaign_id=None):
        """
		Get the message configuration for a specific campaign. If *campaign_id*
		is not specified, then the current campaign is used. If not settings
		are available for the specified campaign, an empty dictionary is
		returned.

		:param str campaign_id: The ID of the campaign.
		:return: The campaign's message configuration or an empty dictionary.
		:rtype: dict
		"""
        if campaign_id is None:
            campaign_id = self.application.config['campaign_id']
        campaign_id = str(campaign_id)
        if not self.storage.get('campaigns'):
            return {}
        return self.storage['campaigns'].get(campaign_id,
                                             {}).get('configuration')

    def set_campaign_config(self, config, campaign_id=None):
        """
		Add the message configuration into the plugin's storage data and
		associate it with the specified campaign. If *campaign_id* is not
		specified, then the current campaign is used.

		:param dict config:
		:param str campaign_id: The ID of the campaign.
		"""
        if campaign_id is None:
            campaign_id = self.application.config['campaign_id']
        campaign_id = str(campaign_id)
        config = {
            'created': datetime.datetime.utcnow(),
            'configuration': config
        }
        self.storage['campaigns'][campaign_id] = config

    def get_message_config_tab(self):
        main_window = self.application.main_window
        mailer_tab = main_window.tabs['mailer']
        return mailer_tab.tabs['config']
        mailer_config_tab.objects_save_to_config()

    def get_current_config(self):
        """
		Get the current configuration options that are managed. This saves the
		settings from the message configuration tab to the standard
		configuration then returns a new dictionary with all of the managed
		settings.

		:return: The current configuration options.
		:rtype: dict
		"""
        app_config = self.application.config
        mailer_config_tab = self.get_message_config_tab()
        mailer_config_tab.objects_save_to_config()
        current_config = dict((key, value)
                              for key, value in app_config.items()
                              if is_managed_key(key))
        return current_config

    def signal_kpc_campaign_set(self, app, old_campaign_id, new_campaign_id):
        dft_config = self.load_default_config()
        app_config = self.application.config
        mailer_config_tab = self.get_message_config_tab()

        if old_campaign_id is not None:
            # switching campaigns
            self.set_campaign_config(self.get_current_config(),
                                     old_campaign_id)
            self.save_storage()

        new_campaign_config = self.get_campaign_config(new_campaign_id)
        if new_campaign_config:
            for key in app_config.keys():
                if not is_managed_key(key):
                    continue
                if key in new_campaign_config:
                    app_config[key] = new_campaign_config[key]
                elif key in dft_config:
                    app_config[key] = dft_config[key]
        elif not self.config['transfer_options']:
            for key in app_config.keys():
                if not is_managed_key(key):
                    continue
                app_config[key] = dft_config.get(key)

        mailer_config_tab.objects_load_from_config()
示例#6
0
class Plugin(plugins.ClientPlugin):
    authors = ['Spencer McIntyre']
    classifiers = ['Plugin :: Client :: Tool']
    title = 'Blink(1) Notifications'
    description = """
	A plugin which will flash a Blink(1) peripheral based on campaign events
	such as when a new visit is received or new credentials have been submitted.
	"""
    homepage = 'https://github.com/securestate/king-phisher-plugins'
    options = [
        plugins.ClientOptionBoolean(
            'filter_campaigns',
            'Only show events for the current campaign.',
            default=True,
            display_name='Current Campaign Only'),
        plugins.ClientOptionEnum(
            'color_visits',
            'The color to flash the Blink(1) for new visits.',
            choices=COLORS,
            default='yellow',
            display_name='Visits Flash Color'),
        plugins.ClientOptionEnum(
            'color_credentials',
            'The color to flash the Blink(1) for new credentials.',
            choices=COLORS,
            default='red',
            display_name='Credentials Flash Color'),
    ]
    reference_urls = ['https://blink1.thingm.com/']
    req_min_version = '1.6.0'
    req_packages = {'blink1': has_blink1}
    req_platforms = ('Linux', )
    version = '1.1'

    def initialize(self):
        self._color = None
        try:
            self._blink1 = blink1.Blink1()
            self._blink1_off()
        except usb.core.USBError as error:
            gui_utilities.show_dialog_error(
                'Connection Error', self.application.get_active_window(),
                'Unable to connect to the Blink(1) device.')
            return False
        except blink1.BlinkConnectionFailed:
            gui_utilities.show_dialog_error(
                'Connection Error', self.application.get_active_window(),
                'Unable to find the Blink(1) device.')
            return False
        self._gsrc_id = None
        if self.application.server_events is None:
            self.signal_connect('server-connected',
                                lambda app: self._connect_server_events())
        else:
            self._connect_server_events()
        return True

    def finalize(self):
        self._blink1_off()
        self._blink1.close()
        self._blink1 = None

    def _blink1_set_color(self, color):
        try:
            self._blink1.fade_to_color(375, color)
        except usb.core.USBError as error:
            self.logger.warning(
                "encountered a USB error '{0}' while setting the color of the blink(1)"
                .format(error.strerror))
            return
        if color != 'black':
            if self._gsrc_id is not None:
                GLib.source_remove(self._gsrc_id)
            self._gsrc_id = GLib.timeout_add(1625, self._blink1_off)
        self._color = color

    def _blink1_off(self):
        self._blink1_set_color('black')
        self._color = None

    def _blink1_off_timeout(self):
        self._gsrc_id = None
        self._blink1_off()
        return False

    def _connect_server_events(self):
        self.signal_connect_server_event('db-credentials',
                                         self.signal_db_credentials,
                                         ('inserted', ), ('id', 'campaign_id'))
        self.signal_connect_server_event('db-visits', self.signal_db_visits,
                                         ('inserted', ), ('id', 'campaign_id'))
        return True

    def _signal_db(self, color, rows):
        if self.config['filter_campaigns']:
            if all(
                    str(row.campaign_id) !=
                    self.application.config['campaign_id'] for row in rows):
                return
        self._blink1_set_color(color)

    @server_events.event_type_filter('inserted', is_method=True)
    def signal_db_credentials(self, _, event_type, rows):
        self._signal_db(self.config['color_credentials'], rows)

    @server_events.event_type_filter('inserted', is_method=True)
    def signal_db_visits(self, _, event_type, rows):
        self._signal_db(self.config['color_visits'], rows)