Exemplo n.º 1
0
	def _respond_file_check_id(self):
		if re.match(r'^[._]metadata\.(json|yaml|yml)$', os.path.basename(self.request_path)):
			self.server.logger.warning('received request for template metadata file')
			raise errors.KingPhisherAbortRequestError()
		if re.match(r'^/\.well-known/acme-challenge/[a-zA-Z0-9\-_]{40,50}$', self.request_path):
			self.server.logger.info('received request for .well-known/acme-challenge')
			return
		if not self.config.get('server.require_id'):
			return

		if self.message_id == self.config.get('server.secret_id'):
			self.server.logger.debug('request received with the correct secret id')
			return
		# a valid campaign_id requires a valid message_id
		if not self.campaign_id:
			self.server.logger.warning('denying request due to lack of a valid id')
			raise errors.KingPhisherAbortRequestError()

		campaign = db_manager.get_row_by_id(self._session, db_models.Campaign, self.campaign_id)
		query = self._session.query(db_models.LandingPage)
		query = query.filter_by(campaign_id=self.campaign_id, hostname=self.vhost)
		if query.count() == 0:
			self.server.logger.warning('denying request with not found due to invalid hostname')
			raise errors.KingPhisherAbortRequestError()
		if campaign.has_expired:
			self.server.logger.warning('denying request because the campaign has expired')
			raise errors.KingPhisherAbortRequestError()
		if campaign.max_credentials is not None and self.visit_id is None:
			query = self._session.query(db_models.Credential)
			query = query.filter_by(message_id=self.message_id)
			if query.count() >= campaign.max_credentials:
				self.server.logger.warning('denying request because the maximum number of credentials have already been harvested')
				raise errors.KingPhisherAbortRequestError()
		return
Exemplo n.º 2
0
    def _respond_file_check_id(self):
        if not self.config.get('server.require_id'):
            return
        if self.message_id == self.config.get('server.secret_id'):
            return
        # a valid campaign_id requires a valid message_id
        if not self.campaign_id:
            self.server.logger.warning(
                'denying request due to lack of a valid id')
            raise errors.KingPhisherAbortRequestError()

        session = db_manager.Session()
        campaign = db_manager.get_row_by_id(session, db_models.Campaign,
                                            self.campaign_id)
        query = session.query(db_models.LandingPage)
        query = query.filter_by(campaign_id=self.campaign_id,
                                hostname=self.vhost)
        if query.count() == 0:
            self.server.logger.warning(
                'denying request with not found due to invalid hostname')
            session.close()
            raise errors.KingPhisherAbortRequestError()
        if campaign.reject_after_credentials and self.visit_id == None:
            query = session.query(db_models.Credential)
            query = query.filter_by(message_id=self.message_id)
            if query.count():
                self.server.logger.warning(
                    'denying request because credentials were already harvested'
                )
                session.close()
                raise errors.KingPhisherAbortRequestError()
        session.close()
        return
Exemplo n.º 3
0
	def adjust_path(self):
		"""Adjust the :py:attr:`~.KingPhisherRequestHandler.path` attribute based on multiple factors."""
		self.request_path = self.path.split('?', 1)[0]
		if not self.config.get('server.vhost_directories'):
			return
		if not self.vhost:
			raise errors.KingPhisherAbortRequestError()
		if self.vhost in ('localhost', '127.0.0.1') and self.client_address[0] != '127.0.0.1':
			raise errors.KingPhisherAbortRequestError()
		self.path = '/' + self.vhost + self.path
Exemplo n.º 4
0
    def _respond_file_check_id(self):
        if re.match(r'^/\.well-known/acme-challenge/[a-zA-Z0-9\-_]{40,50}$',
                    self.request_path):
            self.server.logger.info(
                'received request for .well-known/acme-challenge')
            return
        if not self.config.get('server.require_id'):
            return

        self.semaphore_acquire()
        if self.message_id == self.config.get('server.secret_id'):
            self.semaphore_release()
            self.server.logger.debug(
                'request received with the correct secret id')
            return
        # a valid campaign_id requires a valid message_id
        if not self.campaign_id:
            self.semaphore_release()
            self.server.logger.warning(
                'denying request due to lack of a valid id')
            raise errors.KingPhisherAbortRequestError()

        session = db_manager.Session()
        campaign = db_manager.get_row_by_id(session, db_models.Campaign,
                                            self.campaign_id)
        query = session.query(db_models.LandingPage)
        query = query.filter_by(campaign_id=self.campaign_id,
                                hostname=self.vhost)
        if query.count() == 0:
            session.close()
            self.semaphore_release()
            self.server.logger.warning(
                'denying request with not found due to invalid hostname')
            raise errors.KingPhisherAbortRequestError()
        if campaign.has_expired:
            session.close()
            self.semaphore_release()
            self.server.logger.warning(
                'denying request because the campaign has expired')
            raise errors.KingPhisherAbortRequestError()
        if campaign.reject_after_credentials and self.visit_id is None:
            query = session.query(db_models.Credential)
            query = query.filter_by(message_id=self.message_id)
            if query.count():
                session.close()
                self.semaphore_release()
                self.server.logger.warning(
                    'denying request because credentials were already harvested'
                )
                raise errors.KingPhisherAbortRequestError()
        session.close()
        self.semaphore_release()
        return
Exemplo n.º 5
0
	def _respond_file_raw(self, file_path, attachment):
		try:
			file_obj = open(file_path, 'rb')
		except IOError:
			raise errors.KingPhisherAbortRequestError()
		fs = os.fstat(file_obj.fileno())
		headers = collections.deque([('Content-Type', self.guess_mime_type(file_path)), ('Content-Length', fs[6])])

		if attachment:
			file_name = os.path.basename(file_path)
			headers.append(('Content-Disposition', 'attachment; filename=' + file_name))
		headers.append(('Last-Modified', self.date_time_string(fs.st_mtime)))
		self.semaphore_acquire()
		try:
			headers.extend(self.handle_page_visit() or [])
		except Exception as error:
			self.server.logger.error('handle_page_visit raised error: {0}.{1}'.format(error.__class__.__module__, error.__class__.__name__), exc_info=True)
		finally:
			self.semaphore_release()

		self.send_response(200)
		for header in headers:
			self.send_header(*header)
		self.end_headers()
		shutil.copyfileobj(file_obj, self.wfile)
		file_obj.close()
		return
Exemplo n.º 6
0
	def on_request_handle(self, handler):
		if handler.command == 'RPC' or handler.path.startswith('/_/'):
			return
		client_ip = ipaddress.ip_address(handler.client_address[0])
		for rule in self.config.get('rules', []):
			if client_ip not in rule['source']:
				continue
			target = rule.get('target')
			if not target:
				self.logger.debug("request redirect rule for {0} matched exception".format(str(client_ip)))
				break
			self.logger.debug("request redirect rule for {0} matched target: {1}".format(str(client_ip), target))
			self.respond_redirect(handler, rule)
			raise errors.KingPhisherAbortRequestError(response_sent=True)
Exemplo n.º 7
0
	def _respond_file_raw(self, file_path, attachment):
		try:
			file_obj = open(file_path, 'rb')
		except IOError:
			raise errors.KingPhisherAbortRequestError()
		fs = os.fstat(file_obj.fileno())
		self.send_response(200)
		self.send_header('Content-Type', self.guess_mime_type(file_path))
		self.send_header('Content-Length', fs[6])
		if attachment:
			file_name = os.path.basename(file_path)
			self.send_header('Content-Disposition', 'attachment; filename=' + file_name)
		self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
		self.end_headers()
		shutil.copyfileobj(file_obj, self.wfile)
		file_obj.close()
		return
Exemplo n.º 8
0
 def on_request_handle(self, handler):
     if handler.command == 'RPC' or handler.path.startswith('/_/'):
         return
     client_ip = ipaddress.ip_address(handler.client_address[0])
     for entry in self.entries:
         if 'rule' in entry and not entry['rule'].matches(handler):
             continue
         if 'source' in entry and client_ip not in entry['source']:
             continue
         target = entry.get('target')
         if not target:
             self.logger.debug(
                 "request redirect rule for {0} matched exception".format(
                     str(client_ip)))
             break
         self.logger.debug(
             "request redirect rule for {0} matched target: {1}".format(
                 str(client_ip), target))
         self.respond_redirect(handler, entry)
         raise errors.KingPhisherAbortRequestError(response_sent=True)
Exemplo n.º 9
0
    def respond_file(self, file_path, attachment=False, query=None):
        self._respond_file_check_id()
        file_path = os.path.abspath(file_path)
        mime_type = self.guess_mime_type(file_path)
        if attachment or (mime_type != 'text/html'
                          and mime_type != 'text/plain'):
            self._respond_file_raw(file_path, attachment)
            return
        try:
            template = self.server.template_env.get_template(
                os.path.relpath(file_path, self.config.get('server.web_root')))
        except jinja2.exceptions.TemplateSyntaxError as error:
            self.server.logger.error(
                "jinja2 syntax error in template {0}:{1} {2}".format(
                    error.filename, error.lineno, error.message))
            raise errors.KingPhisherAbortRequestError()
        except jinja2.exceptions.TemplateError:
            raise errors.KingPhisherAbortRequestError()
        except UnicodeDecodeError as error:
            self.server.logger.error(
                "unicode error {0} in template file: {1}:{2}-{3}".format(
                    error.reason, file_path, error.start, error.end))
            raise errors.KingPhisherAbortRequestError()

        template_data = ''
        headers = []
        template_vars = {
            'client': self.get_template_vars_client(),
            'request': {
                'command':
                self.command,
                'cookies':
                dict((c[0], c[1].value) for c in self.cookies.items()),
                'parameters':
                dict(
                    zip(self.query_data.keys(),
                        map(self.get_query, self.query_data.keys()))),
                'user_agent':
                self.headers.get('user-agent')
            },
            'server': {
                'hostname': self.vhost,
                'address': self.connection.getsockname()[0]
            }
        }
        template_vars.update(self.server.template_env.standard_variables)
        try:
            template_module = template.make_module(template_vars)
        except (TypeError, jinja2.TemplateError) as error:
            self.server.logger.error(
                "jinja2 template {0} render failed: {1} {2}".format(
                    template.filename, error.__class__.__name__,
                    error.message))
            raise errors.KingPhisherAbortRequestError()
        if getattr(template_module, 'require_basic_auth',
                   False) and not all(self.get_query_creds(check_query=False)):
            mime_type = 'text/html'
            self.send_response(401)
            headers.append(('WWW-Authenticate', "Basic realm=\"{0}\"".format(
                getattr(template_module, 'basic_auth_realm',
                        'Authentication Required'))))
        else:
            try:
                template_data = template.render(template_vars)
            except (TypeError, jinja2.TemplateError) as error:
                self.server.logger.error(
                    "jinja2 template {0} render failed: {1} {2}".format(
                        template.filename, error.__class__.__name__,
                        error.message))
                raise errors.KingPhisherAbortRequestError()
            self.send_response(200)
            headers.append(
                ('Last-Modified',
                 self.date_time_string(os.stat(template.filename).st_mtime)))

        if mime_type.startswith('text'):
            mime_type += '; charset=utf-8'
        self.send_header('Content-Type', mime_type)
        self.send_header('Content-Length', len(template_data))
        for header in headers:
            self.send_header(*header)

        try:
            self.handle_page_visit()
        except Exception as error:
            self.server.logger.error(
                'handle_page_visit raised error: {0}.{1}'.format(
                    error.__class__.__module__, error.__class__.__name__),
                exc_info=True)

        self.end_headers()
        self.wfile.write(template_data.encode('utf-8', 'ignore'))
        return
Exemplo n.º 10
0
	def respond_file(self, file_path, attachment=False, query=None):
		self.semaphore_acquire()
		try:
			self._set_ids()
			self._respond_file_check_id()
		finally:
			self.semaphore_release()

		file_path = os.path.abspath(file_path)
		mime_type = self.guess_mime_type(file_path)
		if attachment or (mime_type != 'text/html' and mime_type != 'text/plain'):
			self._respond_file_raw(file_path, attachment)
			return
		try:
			template = self.server.template_env.get_template(os.path.relpath(file_path, self.config.get('server.web_root')))
		except jinja2.exceptions.TemplateSyntaxError as error:
			self.server.logger.error("jinja2 syntax error in template {0}:{1} {2}".format(error.filename, error.lineno, error.message))
			raise errors.KingPhisherAbortRequestError()
		except jinja2.exceptions.TemplateError:
			raise errors.KingPhisherAbortRequestError()
		except UnicodeDecodeError as error:
			self.server.logger.error("unicode error {0} in template file: {1}:{2}-{3}".format(error.reason, file_path, error.start, error.end))
			raise errors.KingPhisherAbortRequestError()

		self.semaphore_acquire()
		headers = collections.deque()
		try:
			headers.extend(self.handle_page_visit() or [])
		except Exception as error:
			self.server.logger.error('handle_page_visit raised error: {0}.{1}'.format(error.__class__.__module__, error.__class__.__name__), exc_info=True)

		template_vars = self.get_template_vars()
		try:
			template_module = template.make_module(template_vars)
		except (TypeError, jinja2.TemplateError) as error:
			self.semaphore_release()
			self.server.logger.error("jinja2 template {0} render failed: {1} {2}".format(template.filename, error.__class__.__name__, getattr(error, 'message', '')))
			raise errors.KingPhisherAbortRequestError()

		query_creds = self.get_query_creds(check_query=False)
		require_basic_auth = getattr(template_module, 'require_basic_auth', False)
		require_basic_auth &= not (query_creds.username and query_creds.password)
		require_basic_auth &= self.message_id != self.config.get('server.secret_id')
		template_data = b''
		if require_basic_auth:
			mime_type = 'text/html'
			self.send_response(401)
			headers.append(('WWW-Authenticate', "Basic realm=\"{0}\"".format(getattr(template_module, 'basic_auth_realm', 'Authentication Required'))))
		else:
			self.send_response(200)
			headers.append(('Last-Modified', self.date_time_string(os.stat(template.filename).st_mtime)))
			template_data = str(template_module).encode('utf-8', 'ignore')

		if mime_type.startswith('text'):
			mime_type += '; charset=utf-8'
		headers.extendleft([('Content-Type', mime_type), ('Content-Length', len(template_data))])
		for header in headers:
			self.send_header(*header)

		self.semaphore_release()
		self.end_headers()
		self.wfile.write(template_data)
		return
Exemplo n.º 11
0
    def respond_file(self, file_path, attachment=False, query={}):
        self._respond_file_check_id()
        file_path = os.path.abspath(file_path)
        mime_type = self.guess_mime_type(file_path)
        if attachment or mime_type != 'text/html':
            self._respond_file_raw(file_path, attachment)
            return
        try:
            template = self.server.template_env.get_template(
                os.path.relpath(file_path, self.server.serve_files_root))
        except jinja2.exceptions.TemplateSyntaxError as error:
            self.server.logger.error(
                "jinja2 syntax error in template {0}:{1} {2}".format(
                    error.filename, error.lineno, error.message))
            raise errors.KingPhisherAbortRequestError()
        except jinja2.exceptions.TemplateError:
            raise errors.KingPhisherAbortRequestError()

        template_vars = {
            'client': {
                'address': self.client_address[0]
            },
            'request': {
                'command':
                self.command,
                'cookies':
                dict((c[0], c[1].value) for c in self.cookies.items()),
                'parameters':
                dict(
                    zip(self.query_data.keys(),
                        map(self.get_query, self.query_data.keys())))
            },
            'server': {
                'hostname': self.vhost,
                'address': self.connection.getsockname()[0]
            }
        }
        template_vars.update(self.server.template_env.standard_variables)
        template_vars['client'].update(self.get_template_vars_client() or {})
        try:
            template_data = template.render(template_vars)
        except jinja2.TemplateError as error:
            self.server.logger.error(
                "jinja2 template {0} render failed: {1} {2}".format(
                    template.filename, error.__class__.__name__,
                    error.message))
            raise errors.KingPhisherAbortRequestError()

        fs = os.stat(template.filename)
        if mime_type.startswith('text'):
            mime_type = mime_type + '; charset=utf-8'
        self.send_response(200)
        self.send_header('Content-Type', mime_type)
        self.send_header('Content-Length', str(len(template_data)))
        self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))

        try:
            self.handle_page_visit()
        except Exception as error:
            self.server.logger.error(
                'handle_page_visit raised error: {0}.{1}'.format(
                    error.__class__.__module__, error.__class__.__name__),
                exc_info=True)

        self.end_headers()
        self.wfile.write(template_data.encode('utf-8', 'ignore'))
        return