Example #1
0
 def testExcectpion(self):
     """
     
     :return: 
     """
     logger2 = log.get_default_logger()
     logger2.exception("11111")
Example #2
0
class MysqlManage(object):
    _instance_lock = threading.Lock()

    __database = None

    mysql_logger = get_default_logger()

    @classmethod
    def get_database(cls, refresh=False):
        """
        单例多线程模式获取db对象
        :param refresh: 
        :return: 
        """
        with MysqlManage._instance_lock:
            mysql_config = get_system_config()['mysql']
            if refresh or MysqlManage.__database is None:  # or MysqlManage.__database.is_closed():
                # 老方法
                """MysqlManage.__database = MySQLDatabase(database=mysql_config["database"], host=mysql_config['host'],
                                                       port=int(mysql_config['port']),
                                                       user=mysql_config['user'],
                                                       passwd=mysql_config['password'])
                """
                MysqlManage.__database = PooledMySQLDatabase(database=mysql_config["database"],
                                                             host=mysql_config['host'],
                                                             port=int(mysql_config['port']), user=mysql_config['user'],
                                                             passwd=mysql_config['password'],
                                                             max_connections=mysql_config["max_connections"],
                                                             stale_timeout=mysql_config["stale_timeout"])
            return MysqlManage.__database

    @classmethod
    def close_database(cls, func):
        """关闭连接
        :param cls: 
        :param func: 
        :return: 
        """

        # logger = logging.getLogger('peewee')
        # logger.addHandler(logging.StreamHandler())
        # logger.setLevel(logging.DEBUG)

        def wapper(*args, **kwargs):
            try:
                MysqlManage.get_database().connect()

            except Exception as e:
                pass
            finally:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    MysqlManage.mysql_logger.exception("close_database error")
                    raise e
                finally:
                    MysqlManage.get_database().close()

        return wapper
Example #3
0
 def testFileAutoCreate(self):
     """
     测试log文件是否自动创建
     :return: 
     """
     from common import log
     logger1 = log.get_default_logger()
     logger1.info("1222")
     logger2 = log.getLogger("334555")
     logger2.info("4555")
Example #4
0
 def testColorLog(self):
     """
     测试非windows系统下log颜色是否为正常显示
     :return: 
     """
     from common import log
     logger2 = log.get_default_logger()
     logger2.info("info")
     logger2.error("error")
     logger2.warning("warning")
     logger2.critical("critical")
Example #5
0
 def testExceptionLog(self):
     """
     测试是否能够捕获异常
     :return: 
     """
     from common import log
     logger2 = log.get_default_logger()
     try:
         number = 1 / 0
     except ZeroDivisionError as e:
         logger2.exception("11111")
         logger2.info("info")
Example #6
0
def check_is_alive(url, method, data, headers):
    """
    检测接口是否能正常访问
    :param package: 
    :return: 
    """
    import requests
    from common import log
    logger = log.get_default_logger()
    headers = json.loads(headers) if isinstance(headers, str) else headers
    try:
        logger.info("check if the target URL content is stable")
        requests.request(method=method, url=url, data=data, headers=headers, timeout=5)
        logger.info("the target URL content is stable")
        return True
    except Exception as e:
        logger.warn("the target URL content is not stable")
        return False
Example #7
0
class BaseChecker(object):
    """
    抽象插件父类
    """
    __metaclass__ = ABCMeta
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {
        BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
        BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser
    }

    def __init__(self):
        """
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整
        
        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None
        try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

    def init_check_result(self):
        """
        初始化检测结果
        :return: 
        """
        self.result = {
            'status': False,
            'info': '没有漏洞(默认的描述信息)',
            'error': [],
            'details': self.info,
            'payload': ''
        }

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return: 
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑,各个插件自己实现,首先判断插件是否为禁用状态
        :param request_raw: 
        :return: 
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw: 
        :return: 
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package: 
        :return: 
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):
        """
        解析参数
        :param headers: 
        :return: 
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw: 
        :return: 
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型,其类型为{type}".format(
                    type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info: 
        :return: 
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = [
            "tag", "author", "name", "imp_version", "description", "repair",
            "type"
        ]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data 
        data为空 
        data 为XML,data为合法str,data为非法str
        :param data: 
        :return: 
        """
        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

    def generate_uuid(self):
        """
        生成uuid,根据时间戳生成
        :return: 
        """
        current_time = str(time.time())
        return encode_b64(current_time.split(".")[1])

    def generate_blind_poc(self):
        """
        生成poc,生成无回显poc ,根据选择的是Dns模块还是Socket模块,选择规则如下:
        1.Dns模块单独开启了,优先使用Dns模块
        2.Socket模块单独开启了,使用Socket模块
        3.Dns模块和Socket模块开启了,使用Dns模块
        4.都没开启则不调用
        
        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:
          
            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()
            
            if not blind_poc["data"]:
                return
            
            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名,域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])
            
            # 情况1 和情况2
            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)
            
            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)
            
            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()
            
            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload
                    
                        
        :return: 
        """
        system_config_single = SystemConfigService.get_single_instance(
            refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url,和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(
            system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.
                              hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {
                "host": system_config_single.hunter_log_socket_host,
                "port": system_config_single.hunter_log_socket_port,
                "uuid": plugin_uuid
            }

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url: 
        :param plugin_uuid: 
        :return: 
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(
                system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(
                system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw: 
        :return: 
        """
        parser_name = request_raw[
            "parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return: 
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包,展现原生数据格式
        :return: 
        """
        pass
Example #8
0
"""
import json
import logging
import traceback
import smtplib
from email.header import Header
import email.mime.multipart
import email.mime.text
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from model.system_config import SystemConfigService, SystemConfig
from common import log
from common.config_util import get_system_config

logger = log.get_default_logger()


class EmailUtils(object):
    """
    邮件发送工具类
    """

    def __init__(self):
        """
        初始化配置
        """
        email_config = self.get_email_config()
        self.mail_host = email_config.smtp_host
        self.mail_port = email_config.smtp_port
        self.mail_username = email_config.sender_email
Example #9
0
class RedisService:
    logger = log.get_default_logger()
    HUNTER_TASK_KEYS = "hunter-task:"
    HUNTER_URLCLASSIFICATIONS_KEYS = "simplify_request"
    HUNTER_USER = "******"
    HUNTER_PLUGIN = "hunter-plugin:"
    HUNTER_PLUGIN_SWITCH = "hunter-plugin-switch"

    @staticmethod
    def get_user(user_name):
        """
        根据用户名称获取用户
        :param user_name: 
        :return: 
        """
        user = dict()
        try:
            user = RedisManage.get_redis_client().hgetall("{}{}".format(
                RedisService.HUNTER_USER, user_name))
        except Exception as e:
            RedisService.logger.exception("RedisService get_user error")
            raise e
        finally:
            if not user:
                raise UserNotFoundInRedisException("未从redis中找到用户信息")
            user_object = dict_to_object(user)
            return user_object

    @staticmethod
    def remove_user(user_name):
        """
        根据用户名删除用户
        :param user_name: 
        :return: 
        """
        try:
            hashkey = "{}{}".format(RedisService.HUNTER_USER, user_name)
            if RedisManage.get_redis_client().exists(hashkey):
                hashkey_all = RedisManage.get_redis_client().hkeys(hashkey)
                for i in range(len(hashkey_all)):
                    RedisManage.get_redis_client().hdel(
                        hashkey, hashkey_all[i])
        except Exception as e:
            RedisService.logger.exception("RedisService remove_user error")

    @staticmethod
    def update_user(user_name, user_info):
        """
        保存用户信息到redis中,在用户退出登录之后同步,记得设置session,过期时间为两小时
        user_info包含字段如下 role
        warning: 需要注意的是不能出现为None的情况
        :param user_name: 
        :param user_info: 
        :return: 
        """
        try:
            for (key, value) in user_info.items():
                if value is None:
                    user_info[key] = ""
            RedisManage.get_redis_client().hmset(
                RedisService.HUNTER_USER + user_name, user_info)
        except Exception:
            RedisService.logger.exception("RedisService update_user error")

    @staticmethod
    def update_user_field(user_name, field_name, field_value):
        """
        修改用户某个字段的值
        :param user_name: 
        :param field_name: 
        :param field_value: 
        :return: 
        """
        try:
            RedisManage.get_redis_client().hset(
                RedisService.HUNTER_USER + user_name, field_name, field_value)
        except Exception:
            RedisService.logger.exception(
                "RedisService update_user_field error")

    @staticmethod
    def create_task(task_id, hook_rule, openid, status):
        """
        创建任务,即向redis中存入hook_rule,openid,status,access_token
        :return: 
        """
        try:
            RedisManage.get_redis_client().hset(
                "{}{}".format(RedisService.HUNTER_TASK_KEYS, task_id),
                "hook_rule", hook_rule)
            RedisManage.get_redis_client().hset(
                "{}{}".format(RedisService.HUNTER_TASK_KEYS, task_id),
                "user_name", openid)
            RedisManage.get_redis_client().hset(
                "{}{}".format(RedisService.HUNTER_TASK_KEYS, task_id),
                "status", status)
        except Exception:
            RedisService.logger.exception("RedisService create_task error")

    @staticmethod
    def stop_task(task_id):
        """
        停止任务
        :param task_id: 
        :return: 
        """
        try:
            RedisManage.get_redis_client().hset(
                "{}{}".format(RedisService.HUNTER_TASK_KEYS, task_id),
                "status", TaskStatus.KILLED)
        except Exception:
            RedisService.logger.exception("RedisService stop_task error")

    @staticmethod
    def update_task_hook_rule(task_id, hook_rule):
        """
        更新HookRule
        :param task_id: 
        :param hook_rule: 
        :return: 
        """
        try:
            RedisManage.get_redis_client().hset(
                "{}{}".format(RedisService.HUNTER_TASK_KEYS, task_id),
                "hook_rule", hook_rule)
        except Exception:
            RedisService.logger.exception(
                "RedisService update_task_hook_rule error")

    @staticmethod
    def get_task(task_id):
        """
        获取任务详情
        :param task_id: 
        :return: 
        """
        task = dict()
        # result = {"access_token": "", "status": TaskStatus.NONE, "hook_rule": "*", "openid": ""}
        try:
            task = RedisManage.get_redis_client().hgetall("{}{}".format(
                RedisService.HUNTER_TASK_KEYS, task_id))
        except Exception:
            RedisService.logger.exception("RedisService get_task error")
            task = {
                "status": TaskStatus.NONE,
                "hook_rule": "*",
                "user_name": ""
            }
        finally:
            if "status" not in task:
                task["status"] = TaskStatus.NONE
            if "hook_rule" not in task:
                task["hook_rule"] = "*"
            if "user_name" not in task:
                task["user_name"] = ""
            user_object = dict_to_object(task)
        return user_object

    @staticmethod
    def save_temp_urlsets(urlclassifications_md5, post_data):
        """
        这个百分之百能成功,因为每次的requestid必定是不同的
        temp_urlsets key为md5(urlclassifications) value为set
        sismember 
        :return: 
        """
        result = False
        try:
            is_exist = RedisManage.get_redis_client().sismember(
                "{}{}".format(RedisService.HUNTER_TEMP_URLSETS_KEYS,
                              urlclassifications_md5), post_data)
            if not is_exist:
                RedisManage.get_redis_client().sadd(
                    "{}{}".format(RedisService.HUNTER_TEMP_URLSETS_KEYS,
                                  urlclassifications_md5), post_data)
                result = True
        except Exception:
            RedisService.logger.exception(
                "RedisService save_temp_urlsets error")
            result = False
        return result

    @staticmethod
    def create_urlclassifications(task_id, post_data):
        """
        用于对抓取到的url进行分类,类型为hash,md5:str(),第一次存入返回true表示为新链接
        :return: 
        """
        try:
            post_data["data"].pop('requestid')
            request_raw = post_data["data"]

            http_method = str(request_raw["method"]).lower()
            url = str(request_raw["url"]).strip()
            headers = http_util.header_to_lowercase(
                json.loads(request_raw['headers']))
            content_type = headers[
                "content-type"] if headers is not None and http_util.ContentType.NAME.lower(
                ) in headers else None
            data = request_raw['data'] if "data" in request_raw else None
            simplify_request = BaseChecker.get_parser_class(
                request_raw).simplify_request(url=url,
                                              data=data,
                                              http_method=http_method,
                                              content_type=content_type)
            simplify_request_str = json.dumps(simplify_request)
            # 请求解析归类之后的MD5
            simplify_request_md5 = hashlib.new(
                'md5', simplify_request_str.encode("utf-8")).hexdigest()

            if not RedisManage.get_redis_client().hexists(
                    "{}{}".format(RedisService.HUNTER_URLCLASSIFICATIONS_KEYS,
                                  str(task_id)), simplify_request_md5):
                RedisManage.get_redis_client().hset(
                    RedisService.HUNTER_URLCLASSIFICATIONS_KEYS + str(task_id),
                    simplify_request_md5, simplify_request_str)
                return True
            return False
        except Exception:
            RedisService.logger.exception("create_urlclassifications error")
            return False

    @staticmethod
    def clean_urlclassifications(task_id):
        """
        清空 simplify_request:taskid
        :param task_id: 
        :return: 
        """
        try:
            RedisManage.get_redis_client().delete("{}{}".format(
                RedisService.HUNTER_URLCLASSIFICATIONS_KEYS, task_id))
        except Exception:
            RedisService.logger.exception("clean_urlclassifications error")

    @staticmethod
    def get_unuseable_plugin_names():
        """
        获取开关为OFF的插件,从扫描插件实体中去除
        :return: 
        """
        unuseable_plugin_name_list = list()
        try:
            plugin_switch = RedisManage.get_redis_client().hgetall(
                RedisService.HUNTER_PLUGIN_SWITCH)
            for plugin_name, plugin_swith in plugin_switch.items():
                if plugin_swith == PluginSwith.OFF:
                    unuseable_plugin_name_list.append(plugin_name)
        except Exception:
            RedisService.logger.exception(
                "RedisService get_unuseable_plugin_names error")
        return unuseable_plugin_name_list

    @staticmethod
    @deprecated(version='2.0', reason="You should use another function")
    def list_plugins_info():
        """
        展示所有插件信息,2.0 废弃
        :return: 
        """
        plugin_info_list = list()
        try:
            plugin_switch = RedisManage.get_redis_client().hgetall(
                RedisService.HUNTER_PLUGIN_SWITCH)
            for plugin_name, plugin_swith in plugin_switch.items():
                plugin_info = RedisManage.get_redis_client().hgetall(
                    RedisService.HUNTER_PLUGIN + plugin_name)
                plugin_info["useable"] = int(plugin_info["useable"])
                plugin_info_list.append(plugin_info)
        except Exception:
            RedisService.logger.exception(
                "RedisService list_plugins_info error")
        return plugin_info_list

    @staticmethod
    @deprecated(version='2.0', reason="You should use another function")
    def init_plugin_info():
        """
        初始化插件信息,2.0 废弃
        :return: 
        """
        from common.plugins_util import load_default_checkers
        try:
            checker_dict = load_default_checkers()
            for (plugin_name, checker_instance) in checker_dict.items():
                plugin_instance_info = {
                    "name": plugin_name,
                    "tag": checker_instance.info["tag"],
                    "imp_version": checker_instance.info["imp_version"],
                    "type": checker_instance.info["type"]["fullchinesename"],
                    "level": checker_instance.info["type"]["level"],
                    "description": checker_instance.info["description"],
                    "useable": PluginSwith.ON
                }
                RedisManage.get_redis_client().hmset(
                    RedisService.HUNTER_PLUGIN + plugin_name,
                    plugin_instance_info)
        except Exception:
            RedisService.logger.exception(
                "RedisService init_plugin_info error")

    @staticmethod
    def init_plugin_config():
        """
        初始化插件开关
        :return: 
        """
        from common.plugins_util import load_default_checkers
        try:
            checker_dict = load_default_checkers(True)
            for (plugin_name, checker_instance) in checker_dict.items():
                plugin_config_info = json.dumps({
                    "tag":
                    checker_instance.info["tag"],
                    "useable":
                    PluginSwith.ON,
                    "removed":
                    False
                })
                RedisManage.get_redis_client().hset(
                    RedisService.HUNTER_PLUGIN_SWITCH, plugin_name,
                    plugin_config_info)
        except Exception:
            RedisService.logger.exception("RedisService update_plugin error")

    @staticmethod
    def modify_plugin_config(plugin_name, plugin_config):
        """
        example {"tag": checker_instance.info["tag"], "useable": PluginSwith.ON, "removed": False}
        :param checker_instance: 
        :param kwargs: 
        :return: 
        """
        assert isinstance(plugin_config, dict)
        RedisManage.get_redis_client().hset(RedisService.HUNTER_PLUGIN_SWITCH,
                                            plugin_name,
                                            json.dumps(plugin_config))

    @staticmethod
    def modify_plugin_switch(checker_instance, plugin_swith=PluginSwith.OFF):
        """
        服务端更新插件开关,打开或者关闭插件开关,Redis出问题默认降级使用本地配置文件
        :param plugin_swith: 
        :return: 
        """
        try:
            plugin_tag = checker_instance.info["tag"]
            plugin_name = checker_instance.info["name"]
            plugin_config = {
                "tag": plugin_tag,
                "useable": plugin_swith,
                "removed": False
            }
            if RedisManage.get_redis_client().hexists(
                    RedisService.HUNTER_PLUGIN_SWITCH, plugin_name):
                pre_plugin_config = RedisManage.get_redis_client().hget(
                    RedisService.HUNTER_PLUGIN_SWITCH, plugin_name)
                plugin_config = json.loads(pre_plugin_config)
                plugin_config["useable"] = plugin_swith

            RedisService.modify_plugin_config(plugin_name, plugin_config)
        except Exception:
            RedisService.logger.exception(
                "RedisService modify_plugin_switch error")
Example #10
0
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
author: b5mali4
            
"""
import json
import threading
import configparser
from common.log import get_default_logger
from common.path import HUNTER_CONFIG_PATH
from common.plugin_config.base_plugin_config import BasePluginConfig

logger = get_default_logger()


class RedisPluginConfig(BasePluginConfig):
    """
    插件配置,从redis加载插件相关配置
    """
    _instance_lock = threading.Lock()

    def __init__(self, next_plugin_config=None):
        """
        降级方案,下一级插件配置
        """
        self.next_plugin_config = next_plugin_config
        self.plugin_config = dict()
Example #11
0
def print_logo():
    logger = log.get_default_logger()
    logger.setLevel(logging.DEBUG)
    logger.info(logo)