Example #1
0
 def setUp(self):
     find.data_path_append('data/server')
     web_root = os.path.join(os.getcwd(), 'data', 'server', 'king_phisher')
     config = smoke_zephyr.configuration.Configuration(
         find.find_data_file('server_config.yml'))
     config.set('server.address.host', '127.0.0.1')
     config.set('server.address.port', 0)
     config.set('server.addresses', [])
     config.set('server.database', 'sqlite://')
     config.set(
         'server.geoip.database',
         os.environ.get('KING_PHISHER_TEST_GEOIP_DB',
                        './GeoLite2-City.mmdb'))
     config.set('server.web_root', web_root)
     config.set('server.rest_api.enabled', True)
     config.set('server.rest_api.token', rest_api.generate_token())
     self.config = config
     self.plugin_manager = plugins.ServerPluginManager(config)
     self.server = build.server_from_config(
         config,
         handler_klass=KingPhisherRequestHandlerTest,
         plugin_manager=self.plugin_manager)
     config.set('server.address.port',
                self.server.sub_servers[0].server_port)
     self.assertIsInstance(self.server, server.KingPhisherServer)
     self.server_thread = threading.Thread(target=self.server.serve_forever)
     self.server_thread.daemon = True
     self.server_thread.start()
     self.assertTrue(self.server_thread.is_alive())
     self.shutdown_requested = False
     self.rpc = client_rpc.KingPhisherRPCClient(
         ('localhost', self.config.get('server.address.port')))
     self.rpc.login(username='******', password='******')
Example #2
0
def build_king_phisher_server(config, ServerClass=None, HandlerClass=None):
    """
	Build a server from a provided configuration instance. If *ServerClass* or
	*HandlerClass* is specified, then the object must inherit from the
	corresponding KingPhisherServer base class.

	:param config: Configuration to retrieve settings from.
	:type config: :py:class:`smoke_zephyr.configuration.Configuration`
	:param ServerClass: Alternative server class to use.
	:type ServerClass: :py:class:`.KingPhisherServer`
	:param HandlerClass: Alternative handler class to use.
	:type HandlerClass: :py:class:`.KingPhisherRequestHandler`
	:return: A configured server instance.
	:rtype: :py:class:`.KingPhisherServer`
	"""
    logger = logging.getLogger('KingPhisher.Server.build')
    ServerClass = (ServerClass or KingPhisherServer)
    HandlerClass = (HandlerClass or KingPhisherRequestHandler)
    # set config defaults
    if not config.has_option('server.secret_id'):
        config.set('server.secret_id', make_uid())
    address = (config.get('server.address.host'),
               config.get('server.address.port'))
    ssl_certfile = None
    ssl_keyfile = None
    if config.has_option('server.ssl_cert'):
        ssl_certfile = config.get('server.ssl_cert')
        ssl_keyfile = config.get_if_exists('server.ssl_key')
    try:
        server = ServerClass(config,
                             HandlerClass,
                             address=address,
                             ssl_certfile=ssl_certfile,
                             ssl_keyfile=ssl_keyfile)
    except socket.error as error:
        error_number, error_message = error.args
        if error_number == 98:
            logger.critical(
                "failed to bind server to address {0}:{1} (socket error #98)".
                format(*address))
        raise errors.KingPhisherError("socket error #{0} ({1})".format(
            (error_number or 'NOT-SET'), error_message))
    if config.has_option('server.server_header'):
        server.server_version = config.get('server.server_header')
    if not config.get_if_exists('server.rest_api.token'):
        config.set('server.rest_api.token', rest_api.generate_token())
    if config.get('server.rest_api.enabled'):
        logger.info('rest api initialized with token: ' +
                    config.get('server.rest_api.token'))
    return server
Example #3
0
def build_king_phisher_server(config, ServerClass=None, HandlerClass=None):
    """
	Build a server from a provided configuration instance. If *ServerClass* or
	*HandlerClass* is specified, then the object must inherit from the
	corresponding KingPhisherServer base class.

	:param config: Configuration to retrieve settings from.
	:type config: :py:class:`smoke_zephyr.configuration.Configuration`
	:param ServerClass: Alternative server class to use.
	:type ServerClass: :py:class:`.KingPhisherServer`
	:param HandlerClass: Alternative handler class to use.
	:type HandlerClass: :py:class:`.KingPhisherRequestHandler`
	:return: A configured server instance.
	:rtype: :py:class:`.KingPhisherServer`
	"""
    logger = logging.getLogger("KingPhisher.Server.build")
    ServerClass = ServerClass or KingPhisherServer
    HandlerClass = HandlerClass or KingPhisherRequestHandler
    # set config defaults
    if not config.has_option("server.secret_id"):
        config.set("server.secret_id", make_uid())
    address = (config.get("server.address.host"), config.get("server.address.port"))
    ssl_certfile = None
    ssl_keyfile = None
    if config.has_option("server.ssl_cert"):
        ssl_certfile = config.get("server.ssl_cert")
        if not os.access(ssl_certfile, os.R_OK):
            logger.critical("setting server.ssl_cert file '{0}' not found".format(ssl_certfile))
            raise errors.KingPhisherError("invalid ssl configuration, missing file")
        if config.has_option("server.ssl_key"):
            ssl_keyfile = config.get("server.ssl_key")
            if not os.access(ssl_keyfile, os.R_OK):
                logger.critical("setting server.ssl_key file '{0}' not found".format(ssl_keyfile))
                raise errors.KingPhisherError("invalid ssl configuration, missing file")
    try:
        server = ServerClass(config, HandlerClass, address=address, ssl_certfile=ssl_certfile, ssl_keyfile=ssl_keyfile)
    except socket.error as error:
        error_number, error_message = error.args
        if error_number == 98:
            logger.critical("failed to bind server to address {0}:{1} (socket error #98)".format(*address))
        raise errors.KingPhisherError("socket error #{0} ({1})".format((error_number or "NOT-SET"), error_message))
    if config.has_option("server.server_header"):
        server.server_version = config.get("server.server_header")

    if not config.get_if_exists("server.rest_api.token"):
        config.set("server.rest_api.token", rest_api.generate_token())
    if config.get("server.rest_api.enabled"):
        logger.info("rest api initialized with token: " + config.get("server.rest_api.token"))
    return server
Example #4
0
	def setUp(self):
		find.data_path_append('data/server')
		web_root = os.path.join(os.getcwd(), 'data', 'server', 'king_phisher')
		config = smoke_zephyr.configuration.Configuration(find.find_data_file('server_config.yml'))
		config.set('server.address.port', 0)
		config.set('server.database', 'sqlite://')
		config.set('server.geoip.database', os.environ.get('KING_PHISHER_TEST_GEOIP_DB', './GeoLite2-City.mmdb'))
		config.set('server.web_root', web_root)
		config.set('server.rest_api.enabled', True)
		config.set('server.rest_api.token', rest_api.generate_token())
		self.config = config
		self.server = build_king_phisher_server(config, HandlerClass=KingPhisherRequestHandlerTest)
		config.set('server.address.port', self.server.http_server.server_port)
		self.assertIsInstance(self.server, KingPhisherServer)
		self.server_thread = threading.Thread(target=self.server.serve_forever)
		self.server_thread.daemon = True
		self.server_thread.start()
		self.assertTrue(self.server_thread.is_alive())
		self.shutdown_requested = False
		self.rpc = client_rpc.KingPhisherRPCClient(('localhost', self.config.get('server.address.port')), username='******', password='******')
Example #5
0
	def setUp(self):
		find.data_path_append('data/server')
		web_root = os.path.join(os.getcwd(), 'data', 'server', 'king_phisher')
		server_address = {'host': '127.0.0.1', 'port': 0, 'ssl': False}
		config = configuration.Configuration.from_file(find.data_file('server_config.yml'))
		config.set('server.addresses', [server_address])
		config.set('server.database', 'sqlite://')
		config.set('server.geoip.database', os.environ.get('KING_PHISHER_TEST_GEOIP_DB', './GeoLite2-City.mmdb'))
		config.set('server.web_root', web_root)
		config.set('server.rest_api.enabled', True)
		config.set('server.rest_api.token', rest_api.generate_token())
		self.config = config
		self.plugin_manager = plugins.ServerPluginManager(config)
		self.server = build.server_from_config(config, handler_klass=KingPhisherRequestHandlerTest, plugin_manager=self.plugin_manager)
		server_address['port'] = self.server.sub_servers[0].server_port
		self.assertIsInstance(self.server, server.KingPhisherServer)
		self.server_thread = threading.Thread(target=self.server.serve_forever)
		self.server_thread.daemon = True
		self.server_thread.start()
		self.assertTrue(self.server_thread.is_alive())
		self.shutdown_requested = False
		self.rpc = client_rpc.KingPhisherRPCClient(('localhost', server_address['port']))
		self.rpc.login(username='******', password='******')
Example #6
0
    def __init__(self, config, plugin_manager, handler_klass, *args, **kwargs):
        """
		:param config: Configuration to retrieve settings from.
		:type config: :py:class:`smoke_zephyr.configuration.Configuration`
		"""
        # additional mime types to be treated as html because they're probably cloned pages
        handler_klass.extensions_map.update({
            '': 'text/html',
            '.asp': 'text/html',
            '.aspx': 'text/html',
            '.cfm': 'text/html',
            '.cgi': 'text/html',
            '.do': 'text/html',
            '.jsp': 'text/html',
            '.nsf': 'text/html',
            '.php': 'text/html',
            '.srf': 'text/html'
        })
        super(KingPhisherServer, self).__init__(handler_klass, *args, **kwargs)
        self.logger = logging.getLogger('KingPhisher.Server')
        self.config = config
        """A :py:class:`~smoke_zephyr.configuration.Configuration` instance used as the main King Phisher server configuration."""
        self.plugin_manager = plugin_manager
        self.serve_files = True
        self.serve_files_root = config.get('server.web_root')
        self.serve_files_list_directories = False
        self.serve_robots_txt = True
        self.database_engine = db_manager.init_database(
            config.get('server.database'), extra_init=True)

        self.throttle_semaphore = threading.Semaphore()
        self.session_manager = aaa.AuthenticatedSessionManager(
            timeout=config.get_if_exists('server.authentication.cache_timeout',
                                         '30m'))
        self.forked_authenticator = aaa.ForkedAuthenticator(
            cache_timeout=config.get_if_exists(
                'server.authentication.cache_timeout', '10m'),
            required_group=config.get_if_exists('server.authentication.group'),
            pam_service=config.get_if_exists(
                'server.authentication.pam_service', 'sshd'))
        self.job_manager = job.JobManager()
        """A :py:class:`~smoke_zephyr.job.JobManager` instance for scheduling tasks."""
        self.job_manager.start()
        loader = jinja2.FileSystemLoader(config.get('server.web_root'))
        global_vars = {}
        if config.has_section('server.page_variables'):
            global_vars = config.get('server.page_variables')
        global_vars['embed_youtube_video'] = pages.embed_youtube_video
        global_vars['make_csrf_page'] = pages.make_csrf_page
        global_vars['make_redirect_page'] = pages.make_redirect_page
        self.template_env = templates.TemplateEnvironmentBase(
            loader=loader, global_vars=global_vars)

        for http_server in self.sub_servers:
            http_server.config = config
            http_server.plugin_manager = plugin_manager
            http_server.throttle_semaphore = self.throttle_semaphore
            http_server.session_manager = self.session_manager
            http_server.forked_authenticator = self.forked_authenticator
            http_server.job_manager = self.job_manager
            http_server.template_env = self.template_env
            http_server.kp_shutdown = self.shutdown

        if not config.has_option('server.secret_id'):
            config.set('server.secret_id', rest_api.generate_token())
        if not config.get_if_exists('server.rest_api.token'):
            config.set('server.rest_api.token', rest_api.generate_token())
        if config.get('server.rest_api.enabled'):
            self.logger.info('rest api initialized with token: ' +
                             config.get('server.rest_api.token'))

        self.__geoip_db = geoip.init_database(
            config.get('server.geoip.database'))
        self.__is_shutdown = threading.Event()
        self.__is_shutdown.clear()
        self.__shutdown_lock = threading.Lock()
        plugin_manager.server = self
Example #7
0
	def __init__(self, config, plugin_manager, handler_klass, *args, **kwargs):
		"""
		:param config: Configuration to retrieve settings from.
		:type config: :py:class:`smoke_zephyr.configuration.Configuration`
		"""
		# additional mime types to be treated as html because they're probably cloned pages
		handler_klass.extensions_map.update({
			'': 'text/html',
			'.asp': 'text/html',
			'.aspx': 'text/html',
			'.cfm': 'text/html',
			'.cgi': 'text/html',
			'.do': 'text/html',
			'.jsp': 'text/html',
			'.nsf': 'text/html',
			'.php': 'text/html',
			'.srf': 'text/html'
		})
		super(KingPhisherServer, self).__init__(handler_klass, *args, **kwargs)
		self.logger = logging.getLogger('KingPhisher.Server')
		self.config = config
		"""A :py:class:`~smoke_zephyr.configuration.Configuration` instance used as the main King Phisher server configuration."""
		self.headers = collections.OrderedDict()
		"""A :py:class:`~collections.OrderedDict` containing additional headers specified from the server configuration to include in responses."""
		self.plugin_manager = plugin_manager
		self.serve_files = True
		self.serve_files_root = config.get('server.web_root')
		self.serve_files_list_directories = False
		self.serve_robots_txt = True
		self.database_engine = db_manager.init_database(config.get('server.database'), extra_init=True)

		self.throttle_semaphore = threading.BoundedSemaphore()
		self.session_manager = aaa.AuthenticatedSessionManager(
			timeout=config.get_if_exists('server.authentication.session_timeout', '30m')
		)
		self.forked_authenticator = aaa.ForkedAuthenticator(
			cache_timeout=config.get_if_exists('server.authentication.cache_timeout', '10m'),
			required_group=config.get_if_exists('server.authentication.group'),
			pam_service=config.get_if_exists('server.authentication.pam_service', 'sshd')
		)
		self.job_manager = smoke_zephyr.job.JobManager(logger_name='KingPhisher.Server.JobManager')
		"""A :py:class:`~smoke_zephyr.job.JobManager` instance for scheduling tasks."""
		self.job_manager.start()
		maintenance_interval = 900  # 15 minutes
		self._maintenance_job = self.job_manager.job_add(self._maintenance, parameters=(maintenance_interval,), seconds=maintenance_interval)

		loader = jinja2.FileSystemLoader(config.get('server.web_root'))
		global_vars = {}
		if config.has_section('server.page_variables'):
			global_vars = config.get('server.page_variables')
		global_vars.update(template_extras.functions)
		self.template_env = templates.TemplateEnvironmentBase(loader=loader, global_vars=global_vars)
		self.ws_manager = web_sockets.WebSocketsManager(config, self.job_manager)

		self.tables_api = {}
		self._init_tables_api()

		for http_server in self.sub_servers:
			http_server.add_sni_cert = self.add_sni_cert
			http_server.config = config
			http_server.forked_authenticator = self.forked_authenticator
			http_server.get_sni_certs = lambda: self.sni_certs
			http_server.headers = self.headers
			http_server.job_manager = self.job_manager
			http_server.kp_shutdown = self.shutdown
			http_server.plugin_manager = plugin_manager
			http_server.remove_sni_cert = self.remove_sni_cert
			http_server.session_manager = self.session_manager
			http_server.tables_api = self.tables_api
			http_server.template_env = self.template_env
			http_server.throttle_semaphore = self.throttle_semaphore
			http_server.ws_manager = self.ws_manager

		if not config.has_option('server.secret_id'):
			test_id = rest_api.generate_token()
			config.set('server.secret_id', test_id)
			self.logger.debug('server request test id initialized with value: ' + test_id)
		if not config.get_if_exists('server.rest_api.token'):
			config.set('server.rest_api.token', rest_api.generate_token())
		if config.get('server.rest_api.enabled'):
			self.logger.info('rest api token initialized with value: ' + config.get('server.rest_api.token'))

		self.__geoip_db = geoip.init_database(config.get('server.geoip.database'))
		self.__is_shutdown = threading.Event()
		self.__is_shutdown.clear()
		self.__shutdown_lock = threading.Lock()
		plugin_manager.server = weakref.proxy(self)

		headers = self.config.get_if_exists('server.headers', [])
		for header in headers:
			if ': ' not in header:
				self.logger.warning("header '{0}' is invalid and will not be included".format(header))
				continue
			header, value = header.split(': ', 1)
			header = header.strip()
			self.headers[header] = value
		self.logger.info("including {0} custom http headers".format(len(self.headers)))
Example #8
0
	def __init__(self, config, plugin_manager, handler_klass, *args, **kwargs):
		"""
		:param config: Configuration to retrieve settings from.
		:type config: :py:class:`smoke_zephyr.configuration.Configuration`
		"""
		# additional mime types to be treated as html because they're probably cloned pages
		handler_klass.extensions_map.update({
			'': 'text/html',
			'.asp': 'text/html',
			'.aspx': 'text/html',
			'.cfm': 'text/html',
			'.cgi': 'text/html',
			'.do': 'text/html',
			'.jsp': 'text/html',
			'.nsf': 'text/html',
			'.php': 'text/html',
			'.srf': 'text/html'
		})
		super(KingPhisherServer, self).__init__(handler_klass, *args, **kwargs)
		self.logger = logging.getLogger('KingPhisher.Server')
		self.config = config
		"""A :py:class:`~smoke_zephyr.configuration.Configuration` instance used as the main King Phisher server configuration."""
		self.plugin_manager = plugin_manager
		self.serve_files = True
		self.serve_files_root = config.get('server.web_root')
		self.serve_files_list_directories = False
		self.serve_robots_txt = True
		self.database_engine = db_manager.init_database(config.get('server.database'), extra_init=True)

		self.throttle_semaphore = threading.Semaphore()
		self.session_manager = aaa.AuthenticatedSessionManager(
			timeout=config.get_if_exists('server.authentication.cache_timeout', '30m')
		)
		self.forked_authenticator = aaa.ForkedAuthenticator(
			cache_timeout=config.get_if_exists('server.authentication.cache_timeout', '10m'),
			required_group=config.get_if_exists('server.authentication.group'),
			pam_service=config.get_if_exists('server.authentication.pam_service', 'sshd')
		)
		self.job_manager = job.JobManager()
		"""A :py:class:`~smoke_zephyr.job.JobManager` instance for scheduling tasks."""
		self.job_manager.start()
		loader = jinja2.FileSystemLoader(config.get('server.web_root'))
		global_vars = {}
		if config.has_section('server.page_variables'):
			global_vars = config.get('server.page_variables')
		global_vars['embed_youtube_video'] = pages.embed_youtube_video
		global_vars['make_csrf_page'] = pages.make_csrf_page
		global_vars['make_redirect_page'] = pages.make_redirect_page
		self.template_env = templates.TemplateEnvironmentBase(loader=loader, global_vars=global_vars)

		for http_server in self.sub_servers:
			http_server.config = config
			http_server.plugin_manager = plugin_manager
			http_server.throttle_semaphore = self.throttle_semaphore
			http_server.session_manager = self.session_manager
			http_server.forked_authenticator = self.forked_authenticator
			http_server.job_manager = self.job_manager
			http_server.template_env = self.template_env
			http_server.kp_shutdown = self.shutdown

		if not config.has_option('server.secret_id'):
			config.set('server.secret_id', rest_api.generate_token())
		if not config.get_if_exists('server.rest_api.token'):
			config.set('server.rest_api.token', rest_api.generate_token())
		if config.get('server.rest_api.enabled'):
			self.logger.info('rest api initialized with token: ' + config.get('server.rest_api.token'))

		self.__geoip_db = geoip.init_database(config.get('server.geoip.database'))
		self.__is_shutdown = threading.Event()
		self.__is_shutdown.clear()
		self.__shutdown_lock = threading.Lock()
		plugin_manager.server = self
Example #9
0
	def __init__(self, config, plugin_manager, handler_klass, *args, **kwargs):
		"""
		:param config: Configuration to retrieve settings from.
		:type config: :py:class:`smoke_zephyr.configuration.Configuration`
		"""
		# additional mime types to be treated as html because they're probably cloned pages
		handler_klass.extensions_map.update({
			'': 'text/html',
			'.asp': 'text/html',
			'.aspx': 'text/html',
			'.cfm': 'text/html',
			'.cgi': 'text/html',
			'.do': 'text/html',
			'.jsp': 'text/html',
			'.nsf': 'text/html',
			'.php': 'text/html',
			'.srf': 'text/html'
		})
		super(KingPhisherServer, self).__init__(handler_klass, *args, **kwargs)
		self.logger = logging.getLogger('KingPhisher.Server')
		self.config = config
		"""A :py:class:`~smoke_zephyr.configuration.Configuration` instance used as the main King Phisher server configuration."""
		self.headers = collections.OrderedDict()
		"""A :py:class:`~collections.OrderedDict` containing additional headers specified from the server configuration to include in responses."""
		self.plugin_manager = plugin_manager
		self.serve_files = True
		self.serve_files_root = config.get('server.web_root')
		self.serve_files_list_directories = False
		self.serve_robots_txt = True
		self.database_engine = db_manager.init_database(config.get('server.database'), extra_init=True)

		self.throttle_semaphore = threading.BoundedSemaphore()
		self.session_manager = aaa.AuthenticatedSessionManager(
			timeout=config.get_if_exists('server.authentication.cache_timeout', '30m')
		)
		self.forked_authenticator = aaa.ForkedAuthenticator(
			cache_timeout=config.get_if_exists('server.authentication.cache_timeout', '10m'),
			required_group=config.get_if_exists('server.authentication.group'),
			pam_service=config.get_if_exists('server.authentication.pam_service', 'sshd')
		)
		self.job_manager = smoke_zephyr.job.JobManager(logger_name='KingPhisher.Server.JobManager')
		"""A :py:class:`~smoke_zephyr.job.JobManager` instance for scheduling tasks."""
		self.job_manager.start()
		loader = jinja2.FileSystemLoader(config.get('server.web_root'))
		global_vars = {}
		if config.has_section('server.page_variables'):
			global_vars = config.get('server.page_variables')
		global_vars.update(pages.EXPORTED_FUNCTIONS)
		self.template_env = templates.TemplateEnvironmentBase(loader=loader, global_vars=global_vars)
		self.ws_manager = web_sockets.WebSocketsManager(config, self.job_manager)

		for http_server in self.sub_servers:
			http_server.config = config
			http_server.plugin_manager = plugin_manager
			http_server.throttle_semaphore = self.throttle_semaphore
			http_server.session_manager = self.session_manager
			http_server.forked_authenticator = self.forked_authenticator
			http_server.job_manager = self.job_manager
			http_server.template_env = self.template_env
			http_server.kp_shutdown = self.shutdown
			http_server.ws_manager = self.ws_manager
			http_server.headers = self.headers

		if not config.has_option('server.secret_id'):
			config.set('server.secret_id', rest_api.generate_token())
		if not config.get_if_exists('server.rest_api.token'):
			config.set('server.rest_api.token', rest_api.generate_token())
		if config.get('server.rest_api.enabled'):
			self.logger.info('rest api initialized with token: ' + config.get('server.rest_api.token'))

		self.__geoip_db = geoip.init_database(config.get('server.geoip.database'))
		self.__is_shutdown = threading.Event()
		self.__is_shutdown.clear()
		self.__shutdown_lock = threading.Lock()
		plugin_manager.server = weakref.proxy(self)

		headers = self.config.get_if_exists('server.headers', [])
		for header in headers:
			if ': ' not in header:
				self.logger.warning("header '{0}' is invalid and will not be included".format(header))
				continue
			header, value = header.split(': ', 1)
			header = header.strip()
			self.headers[header] = value
		self.logger.info("including {0} custom http headers".format(len(self.headers)))