zoukankan      html  css  js  c++  java
  • 巡风的扫描与漏斗检测脚本分析

    启动并在配置好之后,巡风就i在后端开始的资产探测的扫描,先来看一下需要启动的三个脚本:aider.py、nasscan.py和vulscan.py

    0x01:aider.py

    这个脚本主要作用有两个,一是用作dns,建立socket连接,一个简单的DNS log平台,启动两个线程,一个线程执行udp服务,一个执行http服务;二是用来判断无返回类型的服务

    import socket,thread,datetime,time
    query_history = []
    url_history = []
    
    def web_server():
        # 创建http服务
        web = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        # 监听8088(http)端口
        web.bind(('0.0.0.0',8088))
        # 监听端口
        web.listen(10)
    
        while True:
            try:
                #     被动接受TCP客户端连接,(阻塞式)等待连接的到来
                # 连接成功返回非负值,失败时返回-1
                conn,addr = web.accept()
                # recv接受tcpp数据,最大为4096字节
                data = conn.recv(4096)
                req_line = data.split("
    ")[0]
                path = req_line.split()[1]
                route_list = path.split('/')
                html = "NO"
                if len(route_list) == 3:
                    if route_list[1] == 'add':
                        if route_list[2] not in url_history:
                            url_history.append(route_list[2])
                    elif route_list[1] == 'check':
                        if route_list[2] in url_history:
                            url_history.remove(route_list[2])
                            html = 'YES'
                else:
                    query_str = route_list[1]
                    for query_raw in query_history:
                        if query_str in query_raw:
                            query_history.remove(query_raw)
                            html = "YES"
                print datetime.datetime.now().strftime('%m-%d %H:%M:%S') + " " + str(addr[0]) +' web query: ' + path
                raw = "HTTP/1.0 200 OK
    Content-Type: application/json; charset=utf-8
    Content-Length: %d
    Connection: close
    
    %s" %(len(html),html)
                conn.send(raw)
                conn.close()
            except:
                pass
    
    
    if __name__=="__main__":
        # 创建一个socket对象,规定套接字家族和类型(非面向连接)
        dns = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        # 监听53(udp)端口
        dns.bind(('0.0.0.0', 53))
        # start_new_thread创建一个新线程,返回线程标识符
        thread.start_new_thread(web_server,())
        while True:
            try:
                # 接受udp数据,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
                recv,addr = dns.recvfrom(1024)
                # 将请求添加到query_history数组中。
                if recv not in query_history:query_history.append(recv)
                print datetime.datetime.now().strftime('%m-%d %H:%M:%S') + " " +str(addr[0]) +' Dns Query: ' + recv
            except Exception,e:
                print e
    aider.py

    0x02:nasscan.py

    大致的顺一下这个脚本的功能顺序:获取前端配置-进行日志记录-读取统计信息-判断是否应该扫描(包括两种情况)-删除失效记录-开始扫描

    这个脚本主要是用来进行网络资产的扫描,包括探测存活主机、开放端口、服务等。

    if __name__ == "__main__":
        try:
            # 读取配置
            CONFIG_INI = get_config()
            # 日志记录,传入参数scan_type, host, port, info
            log.write('info', None, 0, u'获取配置成功')
            # 读取统计信息
            STATISTICS = get_statistics()
            print STATISTICS
            MASSCAN_AC = [0]    # 标识符 masscan是否在使用
            NACHANGE = [0]      # 标识符 扫描列表是否被改变
            thread.start_new_thread(monitor, (CONFIG_INI, STATISTICS, NACHANGE))  # 心跳线程,主要用于判断扫描配置是否发生了变化
            thread.start_new_thread(cruise, (STATISTICS, MASSCAN_AC))  # 失效记录删除线程
            socket.setdefaulttimeout(int(CONFIG_INI['Timeout']) / 2)  # 设置连接超时
            ac_data = []
            # 扫描循环
            while True:
                # 获取当前具体时间信息
                now_time = time.localtime()
                # 获取当前小时
                now_hour = now_time.tm_hour
                # 获取当前星期几
                now_day = now_time.tm_mday
                # 获取年月日
                now_date = str(now_time.tm_year) + str(now_time.tm_mon) + str(now_day)
                # 获取资产探测周期
                cy_day, ac_hour = CONFIG_INI['Cycle'].split('|')
                log.write('info', None, 0, u'扫描规则: ' + str(CONFIG_INI['Cycle']))
                # 判断是否进入扫描时段
                # 判断是否达到了一个扫描的周期,或者心跳线程是否检测到扫描列表更新
                # 在心跳线程中可以看到base64不同时会将NACHANGE[0]置于1
                if (now_hour == int(ac_hour) and now_day % int(cy_day) == 0 and now_date not in ac_data) or NACHANGE[0]:
                    # 判断是否扫描过列表
                    ac_data.append(now_date)
                    NACHANGE[0] = 0
                    log.write('info', None, 0, u'开始扫描')
                    # 声明一个start对象,并传入配置参数
                    s = start(CONFIG_INI)
                    # 标识masscan是否在使用,标识扫描列表是否被改变
                    s.masscan_ac = MASSCAN_AC
                    s.statistics = STATISTICS
                    # 开始扫描
                    s.run()
                time.sleep(60)
        except Exception, e:
            print e
    View Code

    这个方法用来获取配置页面中的各项配置

    def get_config():
        config = {}
        # 从mongodb中读取`nascan`的配置,可以看到Config集合中有`vulscan`和`nascan`的扫描配置
        config_info = mongo.na_db.Config.find_one({"type": "nascan"})
        for name in config_info['config']:
            # 对于cms识别、组件容器、动态语言、服务 的配置存储是使用`|`进行分割存储的
            # 所以在取出之前要进行简单的格式化然后放到配置中
            if name in ['Discern_cms', 'Discern_con', 'Discern_lang', 'Discern_server']:
                config[name] = format_config(name, config_info['config'][name]['value'])
            else:
                config[name] = config_info['config'][name]['value']
        return config
    get_config

    用来进行日志记录

    import threading
    import time
    import sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    # 线程互斥锁
    mutex = threading.Lock()
    def write(scan_type, host, port, info):
        # 上锁,避免多个进程输出,导致格式混乱
        mutex.acquire()
        port = int(port)
        try:
            time_str = time.strftime('%X', time.localtime(time.time()))
            # 根据传入的scan_type,判断输出内容
            if scan_type == 'portscan':
                print "[%s] %s:%d open" % (time_str, host, port)
            elif scan_type == 'server':
                print "[%s] %s:%d is %s" % (time_str, host, port, str(info))
            elif scan_type == 'web':
                print "[%s] %s:%d is web" % (time_str, host, port)
                print "[%s] %s:%d web info %s" % (time_str, host, port, info)
            elif scan_type == 'active':
                print "[%s] %s active" % (time_str, host)
            elif scan_type == 'info':
                print "[%s] %s" % (time_str, info)
        except Exception, e:
            print 'logerror',e
            pass
        # 释放锁
        mutex.release()
    log.write

    读取统计信息

    def get_statistics():
        # 获取当日的统计信息
        date_ = datetime.datetime.now().strftime('%Y-%m-%d')
        now_stati = mongo.na_db.Statistics.find_one({"date": date_})
        if not now_stati:
            # 没有当日的信息则返回一个初始统计信息
            now_stati = {date_: {"add": 0, "update": 0, "delete": 0}}
            return now_stati
        else:
            # 有则返回
            return {date_: now_stati['info']}
    get_statistics

    心跳线程,判断是否到达扫描周期或资产探测列表已改变

    # 心跳线程,主要用于判断扫描配置是否发生了变化,如果改变,则立即触发扫描
    def monitor(CONFIG_INI, STATISTICS, NACHANGE):
        while True:
            try:
                time_ = datetime.datetime.now()
                # 记录心跳
                date_ = time_.strftime('%Y-%m-%d')
                mongo.na_db.Heartbeat.update({"name": "heartbeat"}, {"$set": {"up_time": time_}})
                if date_ not in STATISTICS: STATISTICS[date_] = {"add": 0, "update": 0, "delete": 0}
                # 更新统计信息
                mongo.na_db.Statistics.update({"date": date_}, {"$set": {"info": STATISTICS[date_]}}, upsert=True)
                new_config = get_config()   # 获取最新配置
                # 比较配置扫描列表的base64是否相同,不同则置NACHANGE[0]为1,说明配置发生改名,立即出发扫描
                if base64.b64encode(CONFIG_INI["Scan_list"]) != base64.b64encode(new_config["Scan_list"]):NACHANGE[0] = 1
                CONFIG_INI.clear()
                # 更新新配置
                CONFIG_INI.update(new_config)
            except Exception, e:
                print e
            # 每30秒检测一次
            time.sleep(30)
    monitor

    失效记录删除线程

    #   失效记录,删除线程,STATISTICS统计信息,MASSCAN_AC判断是否启用masscan
    def cruise(STATISTICS,MASSCAN_AC):
        while True:
            # 获取当前日期(年 月 日 时 分 秒)
            now_str = datetime.datetime.now()
            # 获取当天是星期几0-6
            week = int(now_str.weekday())
            # 获取当前时间的整点数
            hour = int(now_str.hour)
            if week >= 1 and week <= 5 and hour >= 9 and hour <= 18:  # 非工作时间不删除
                try:
                    # 获取扫描信息记录,根据time字段进行升序排列
                    data = mongo.NA_INFO.find().sort("time", 1)
                    for history_info in data:
                        while True:
                            # 如果masscan正在扫描即不进行清理,在用masscan进行扫描的时候会置1
                            if MASSCAN_AC[0]:  # 如果masscan正在扫描即不进行清理
                                time.sleep(10)
                            else:
                                break
                        ip = history_info['ip']
                        port = history_info['port']
                        try:
                            # 检测端口是否存活
                            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                            sock.connect((ip, int(port)))
                            sock.close()
                        except Exception, e:
                            time_ = datetime.datetime.now()
                            date_ = time_.strftime('%Y-%m-%d')
                            # 不存活则删除改记录
                            mongo.NA_INFO.remove({"ip": ip, "port": port})
                            # 日志记录
                            log.write('info', None, 0, '%s:%s delete' % (ip, port))
                            STATISTICS[date_]['delete'] += 1
                            del history_info["_id"]
                            history_info['del_time'] = time_
                            history_info['type'] = 'delete'
                            # 添加一条操作历史
                            mongo.NA_HISTORY.insert(history_info)
                except:
                    pass
            # 每小时检测一次
            time.sleep(3600)
    cruise

    start类,扫描的主要方法,包括IP地址段的解析,端口的扫描,白名单的绕过等

    class start:
        def __init__(self, config):  # 默认配置
            # 传入CONFIG_INI 配置,然后设置类的属性
            self.config_ini = config
            self.queue = Queue.Queue()  # 队列对象
            self.thread = int(self.config_ini['Thread'])  # 最大线程数
            self.scan_list = self.config_ini['Scan_list'].split('
    ')   # 扫描列表
            self.mode = int(self.config_ini['Masscan'].split('|')[0])   # MASSCAN配置
            self.icmp = int(self.config_ini['Port_list'].split('|')[0]) # 端口列表
            self.white_list = self.config_ini.get('White_list', '').split('
    ') # 白名单
    
        # 启动函数
        def run(self):
            # 在start.py中定义的全局变量,端口列表
            global AC_PORT_LIST
            all_ip_list = []
            for ip in self.scan_list:
                # 处理CIDR格式的ip, eg:192.168.0.1/24
                # 就不具体跟进看了,大约40行左右,涉及一些位运算格式转换啥的
                if "/" in ip:
                    # 把CIDR格式的地址转换成地址段
                    ip = cidr.CIDR(ip)
                if not ip:
                    continue
                # 获取IP列表
                ip_list = self.get_ip_list(ip)
                # 对于白名单ip进行移除
                for white_ip in self.white_list:
                    if white_ip in ip_list:
                        ip_list.remove(white_ip)
                # 是否开始了masscan扫描,开启了mode置为1,否则为0
                if self.mode == 1:
                    # 获取文件路径
                    masscan_path = self.config_ini['Masscan'].split('|')[2]
                    # 获取扫描速率
                    masscan_rate = self.config_ini['Masscan'].split('|')[1]
                    # 如果用户在前台关闭了ICMP存活探测则进行全IP段扫描
                    # 获取存活IP
                    if self.icmp:
                        ip_list = self.get_ac_ip(ip_list)
                    self.masscan_ac[0] = 1
                    # 如果安装了Masscan即使用Masscan进行全端口扫描
                    AC_PORT_LIST = self.masscan(
                        ip_list, masscan_path, masscan_rate)
                    if not AC_PORT_LIST:
                        continue
                    # 将self.masscan_ac[0]置0,表示结束使用
                    self.masscan_ac[0] = 0
                    for ip_str in AC_PORT_LIST.keys():
                        self.queue.put(ip_str)  # 加入队列
                    self.scan_start()  # 开始扫描
                else:
                    all_ip_list.extend(ip_list)
            # 不使用masscan扫描
            if self.mode == 0:
                if self.icmp:
                    all_ip_list = self.get_ac_ip(all_ip_list)
                for ip_str in all_ip_list:
                    self.queue.put(ip_str)  # 加入队列
                self.scan_start()  # TCP探测模式开始扫描
    
        # 开始扫描
        def scan_start(self):
            for i in range(self.thread):  # 开始扫描
                t = ThreadNum(self.queue)
                t.setDaemon(True)
                t.mode = self.mode
                t.config_ini = self.config_ini
                t.statistics = self.statistics
                t.start()
            self.queue.join()
    
        def masscan(self, ip, masscan_path, masscan_rate):
            try:
                if len(ip) == 0:
                    return
                sys.path.append(sys.path[0] + "/plugin")
                # 导入masscan.py,并在下面调用它的run方法
                m_scan = __import__("masscan")
                result = m_scan.run(ip, masscan_path, masscan_rate)
                return result
            except Exception, e:
                print e
                print 'No masscan plugin detected'
    
        # 接受cidr格式的ip,返回IP列表
        def get_ip_list(self, ip):
            ip_list_tmp = []
            def iptonum(x): return sum([256 ** j * int(i)
                                        for j, i in enumerate(x.split('.')[::-1])])
            def numtoip(x): return '.'.join(
                [str(x / (256 ** i) % 256) for i in range(3, -1, -1)])
            if '-' in ip:
                ip_range = ip.split('-')
                ip_start = long(iptonum(ip_range[0]))
                ip_end = long(iptonum(ip_range[1]))
                ip_count = ip_end - ip_start
                if ip_count >= 0 and ip_count <= 655360:
                    for ip_num in range(ip_start, ip_end + 1):
                        ip_list_tmp.append(numtoip(ip_num))
                else:
                    print 'IP format error'
            else:
                ip_split = ip.split('.')
                net = len(ip_split)
                if net == 2:
                    for b in range(1, 255):
                        for c in range(1, 255):
                            ip = "%s.%s.%d.%d" % (ip_split[0], ip_split[1], b, c)
                            ip_list_tmp.append(ip)
                elif net == 3:
                    for c in range(1, 255):
                        ip = "%s.%s.%s.%d" % (
                            ip_split[0], ip_split[1], ip_split[2], c)
                        ip_list_tmp.append(ip)
                elif net == 4:
                    ip_list_tmp.append(ip)
                else:
                    print "IP format error"
            return ip_list_tmp
    
        # 通过ping请求来探测主机存活,后期只对存活主机进行扫描
        def get_ac_ip(self, ip_list):
            try:
                s = icmp.Nscan()
                ipPool = set(ip_list)
                return s.mPing(ipPool)
            except Exception, e:
                print 'The current user permissions unable to send icmp packets'
                return ip_list
    start类

    动态引入masscan.py脚本(m_scan = __import__("masscan"))

    def run(ip_list,path,rate):
        try:
            ip_file = open('target.log','w')
            # 将存活的ip列表写到target.log中
            ip_file.write("
    ".join(ip_list))
            ip_file.close()
            # 进行过滤一些危险字符,translate用来转换字符串字符
            path = str(path).translate(None, ';|&`
    ')
            rate = str(rate).translate(None, ';|&`
    ')
            if not os.path.exists(path):return
            # 用系统命令进行masscan全端口扫描
            os.system("%s -p1-65535 -iL target.log -oL tmp.log --randomize-hosts --rate=%s"%(path,rate))
            # 读取扫描结果
            result_file = open('tmp.log', 'r')
            result_json = result_file.readlines()
            result_file.close()
            del result_json[0]
            del result_json[-1]
            open_list = {}
            # 对扫描结果进行格式化处理
            for res in result_json:
                try:
                    ip = res.split()[3]
                    port = res.split()[2]
                    if ip in open_list:
                        open_list[ip].append(port)
                    else:
                        open_list[ip] = [port]
                except:pass
            os.remove('target.log')
            os.remove('tmp.log')
            # 返回扫描结果
            return open_list
        except:
            pass
    masscan.py run

    0x03:vulscan.py

    用于对扫出的资产进行漏洞扫描,具体的扫描过程依赖于vuldb中的插件形式进行扫描,

    main函数:脚本运行的开始函数,主要是启动心跳线程、漏斗检查线程、更新kunpeng插件线程、获取任务、清理插件缓存等。

    if __name__ == '__main__':
        init()
        # 密码字典、线程数量、超时时间、白名单
        PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config()
        # 开启心跳线程
        thread.start_new_thread(monitor, ())
        # 开启漏斗检查线程
        thread.start_new_thread(kp_check, ())
        # 开启更新kunpeng线程
        thread.start_new_thread(kp_update, ())
        while True:
            try:
                # 获取未执行任务的任务id,、任务周期、任务目标、任务插件
                task_id, task_plan, task_target, task_plugin = queue_get()
                if task_id == '':
                    time.sleep(10)
                    continue
                # 清理插件缓存
                if PLUGIN_DB:
                    del sys.modules[PLUGIN_DB.keys()[0]]
                    PLUGIN_DB.clear()
                for task_netloc in task_target:
                    while True:
                        # thread._count返回正在运行的线程数量,包括主线程
                        if int(thread._count()) < THREAD_COUNT:
                            # 绕过白名单
                            if task_netloc[0] in WHITE_LIST:
                                break
                            # 进行漏斗检测
                            try:
                                thread.start_new_thread(
                                    vulscan, (task_id, task_netloc, task_plugin))
                            except Exception as e:
                                print e
                            break
                        else:
                            time.sleep(2)
                # 更新status文档
                if task_plan == 0:
                    na_task.update({"_id": task_id}, {"$set": {"status": 2}})
            except Exception as e:
                print e
    __main__

    init函数:用来初始化,获取插件列表,安装kunpeng等。

    def init():
        time_ = datetime.datetime.now()
        if na_plugin.find().count() >= 1:
            return
        script_plugin = []
        json_plugin = []
        print 'init plugins'
        # 获取插件列表
        file_list = os.listdir(sys.path[0] + '/vuldb')
        # 把插件添加到对应的列表中
        for filename in file_list:
            try:
                if filename.split('.')[1] == 'py':
                    script_plugin.append(filename.split('.')[0])
                if filename.split('.')[1] == 'json':
                    json_plugin.append(filename)
            except:
                pass
        for plugin_name in script_plugin:
            try:
                # 动态加载
                res_tmp = __import__(plugin_name)
                # 获取插件信息
                plugin_info = res_tmp.get_plugin_info()
                plugin_info['add_time'] = time_
                plugin_info['filename'] = plugin_name
                plugin_info['count'] = 0
                na_plugin.insert(plugin_info)
            except:
                pass
        for plugin_name in json_plugin:
            try:
                json_text = open(sys.path[0] + '/vuldb/' + plugin_name, 'r').read()
                plugin_info = json.loads(json_text)
                plugin_info['add_time'] = time_
                plugin_info['filename'] = plugin_name
                plugin_info['count'] = 0
                del plugin_info['plugin']
                na_plugin.insert(plugin_info)
            except:
                pass
        # 安装kunpeng
        install_kunpeng_plugin()
    init

    get_config:从数据库里查找配置,白名单、线程数、超时时间等

    def get_config():
        try:
            # 在Config集合中,找到type=vulscan的文档
            config_info = na_config.find_one({"type": "vulscan"})
            # 下面都是获取这个文档中的一些值,都是字典类型的
            pass_row = config_info['config']['Password_dic']
            thread_row = config_info['config']['Thread']
            timeout_row = config_info['config']['Timeout']
            white_row = config_info['config']['White_list']
            password_dic = pass_row['value'].split('
    ')
            thread_count = int(thread_row['value'])
            timeout = int(timeout_row['value'])
            white_list = white_row['value'].split('
    ')
            return password_dic, thread_count, timeout, white_list
        except Exception, e:
            print e
    get_config

    monitor:心跳线程函数,看的不是很明白,希望大佬们可以支教

    def monitor():
        global PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST
        while True:
            # 从数据库里找到相应的值
            queue_count = na_task.find({"status": 0, "plan": 0}).count()
            if queue_count:
                load = 1
            else:
                ac_count = thread._count()
                load = float(ac_count - 6) / THREAD_COUNT
            if load > 1:
                load = 1
            if load < 0:
                load = 0
            na_heart.update({"name": "load"}, {
                            "$set": {"value": load, "up_time": datetime.datetime.now()}})
            PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config()
            if load > 0:
                time.sleep(8)
            else:
                time.sleep(60)
    monitor

    kp_check:调用kunpeng,进行检查

    def kp_check():
        while True:
            try:
                new_release = kp.check_version()
                print new_release
                if new_release:
                    info = new_release['body']
                    if '###' in new_release['body']:
                        info = new_release['body'].split('###')[1]
                    row = {
                        'info': info,
                        'isInstall': 0,
                        'name': new_release['name'],
                        'author': new_release['author']['login'],
                        'pushtime': new_release['published_at'],
                        'location': "",
                        'unicode': new_release['tag_name'],
                        'coverage': 0,
                        'source': 'kunpeng'
                    }
                    na_update.insert(row)
                    time.sleep(60 * 60 * 48)
            except Exception as e:
                print e
            time.sleep(60 * 30)
    kp_check

    kp_update:更新kunpeng插件

    def kp_update():
        while True:
            try:
                # 找到相应的文档删除,并返回删除的数量
                row = na_update.find_one_and_delete(
                    {'source': 'kunpeng', 'isInstall': 1})
                if row:
                    kp.update_version(row['unicode'])
                    na_plugin.delete_many({'_id':re.compile('^KP')})
                    install_kunpeng_plugin()
            except Exception as e:
                print e
            time.sleep(10)
    kp_update

    queue_get:获取任务信息,id、周期、插件等

    def queue_get():
        # 全局字典
        global TASK_DATE_DIC
        # 在Task这个集合中提取并更新数据,找到所有status等于0,plan等于0的文档,返回后更新status=1,然后根据时间升序排列
        # find_and_modify
        task_req = na_task.find_and_modify(query={"status": 0, "plan": 0}, update={
                                           "$set": {"status": 1}}, sort={'time': 1})
        # 返回未执行任务
        if task_req:
            TASK_DATE_DIC[str(task_req['_id'])] = datetime.datetime.now()
            return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin']
        # 不存在还未执行任务,返回空或返回定期执行任务
        else:
            # $ne表示不等于
            task_req_row = na_task.find({"plan": {"$ne": 0}})
            if task_req_row:
                # 执行定期任务
                for task_req in task_req_row:
                    if (datetime.datetime.now() - task_req['time']).days / int(task_req['plan']) >= int(task_req['status']):
                        if task_req['isupdate'] == 1:
                            task_req['target'] = update_target(
                                json.loads(task_req['query']))
                            na_task.update({"_id": task_req['_id']}, {
                                           "$set": {"target": task_req['target']}})
                        na_task.update({"_id": task_req['_id']}, {
                                       "$inc": {"status": 1}})
                        TASK_DATE_DIC[str(task_req['_id'])
                                      ] = datetime.datetime.now()
                        return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin']
            return '', '', '', ''
    queue_get

    vulscan类:这是主要的,漏斗的检测主要在这里面进行。

    class vulscan():
        def __init__(self, task_id, task_netloc, task_plugin):
            self.task_id = task_id      # 任务id
            self.task_netloc = task_netloc  # 任务目标
            self.task_plugin = task_plugin # 任务插件
            self.result_info = ''
            self.start()
    
        # 线程启动函数,与run不同,会在一个新线程里开启
        def start(self):
            self.get_plugin_info()  # 获取插件信息
            if '.json' in self.plugin_info['filename']:  # 标示符检测模式
                self.load_json_plugin()  # 读取漏洞标示
                self.set_request()  # 标示符转换为请求
                self.poc_check()  # 检测
            elif 'KP-' in self.plugin_info['filename']:
                self.log(str(self.task_netloc) + 'call kunpeng - ' + self.plugin_info['filename'])
                kp.set_config(TIMEOUT, PASSWORD_DIC)
                if self.task_netloc[1] != 80:
                    self.result_info = kp.check('service', '{}:{}'.format(
                        self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename'])
                if not self.result_info:
                    scheme = 'http'
                    if self.task_netloc[1] == 443:
                        scheme = 'https'
                    self.result_info = kp.check('web', '{}://{}:{}'.format(
                        scheme, self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename'])
            else:  # 脚本检测模式
                plugin_filename = self.plugin_info['filename']
                self.log(str(self.task_netloc) + 'call ' + self.task_plugin)
                if task_plugin not in PLUGIN_DB: # 字典
                    plugin_res = __import__(plugin_filename)
                    setattr(plugin_res, "PASSWORD_DIC", PASSWORD_DIC)  # 给插件声明密码字典
                    PLUGIN_DB[plugin_filename] = plugin_res
                self.result_info = PLUGIN_DB[plugin_filename].check(
                    str(self.task_netloc[0]), int(self.task_netloc[1]), TIMEOUT)
            self.save_request()  # 保存结果
    
        # 获取插件信息
        def get_plugin_info(self):
            # 从插件库中找到插件
            info = na_plugin.find_one({"name": self.task_plugin})
            self.plugin_info = info
    
        def load_json_plugin(self):
            json_plugin = open(sys.path[0] + '/vuldb/' +
                               self.plugin_info['filename']).read()
            self.plugin_info['plugin'] = json.loads(json_plugin)['plugin']
        # 构造请求
        def set_request(self):
            url = 'http://' + 
                self.task_netloc[0] + ":" + 
                str(self.task_netloc[1]) + self.plugin_info['plugin']['url']
            if self.plugin_info['plugin']['method'] == 'GET':
                request = urllib2.Request(url)
            else:
                request = urllib2.Request(url, self.plugin_info['plugin']['data'])
            self.poc_request = request
    
        # 获取代码语言,主要是通过正则匹配
        def get_code(self, header, html):
            try:
                m = re.search(r'<meta.*?charset=(.*?)"(>| |/)', html, flags=re.I)
                if m:
                    return m.group(1).replace('"', '')
            except:
                pass
            try:
                if 'Content-Type' in header:
                    Content_Type = header['Content-Type']
                    m = re.search(r'.*?charset=(.*?)(;|$)',
                                  Content_Type, flags=re.I)
                    if m:
                        return m.group(1)
            except:
                pass
    
        def poc_check(self):
            try:
                # 发送请求
                res = urllib2.urlopen(self.poc_request, timeout=30)
                res_html = res.read(204800)
                # 获取i请求头
                header = res.headers
                # res_code = res.code
            except urllib2.HTTPError, e:
                # res_code = e.code
                header = e.headers
                res_html = e.read(204800)
            except Exception, e:
                return
            try:
                # 获取编码语言
                html_code = self.get_code(header, res_html).strip()
                if html_code and len(html_code) < 12:
                    res_html = res_html.decode(html_code).encode('utf-8')
            except:
                pass
            an_type = self.plugin_info['plugin']['analyzing']
            vul_tag = self.plugin_info['plugin']['tag']
            analyzingdata = self.plugin_info['plugin']['analyzingdata']
            if an_type == 'keyword':
                # print poc['analyzingdata'].encode("utf-8")
                if analyzingdata.encode("utf-8") in res_html:
                    self.result_info = vul_tag
            elif an_type == 'regex':
                if re.search(analyzingdata, res_html, re.I):
                    self.result_info = vul_tag
            elif an_type == 'md5':
                md5 = hashlib.md5()
                md5.update(res_html)
                if md5.hexdigest() == analyzingdata:
                    self.result_info = vul_tag
    
        #
        def save_request(self):
            if self.result_info:
                time_ = datetime.datetime.now()
                self.log(str(self.task_netloc) + " " + self.result_info)
                v_count = na_result.find(
                    {"ip": self.task_netloc[0], "port": self.task_netloc[1], "info": self.result_info}).count()
                if not v_count:
                    na_plugin.update({"name": self.task_plugin},
                                     {"$inc": {'count': 1}})
                vulinfo = {"vul_name": self.plugin_info['name'], "vul_level": self.plugin_info['level'],
                           "vul_type": self.plugin_info['type']}
                w_vul = {"task_id": self.task_id, "ip": self.task_netloc[0], "port": self.task_netloc[1],
                         "vul_info": vulinfo, "info": self.result_info, "time": time_,
                         "task_date": TASK_DATE_DIC[str(self.task_id)]}
                na_result.insert(w_vul)
                # self.wx_send(w_vul)  # 自行定义漏洞提醒
    
        def log(self, info):
            lock.acquire()
            try:
                time_str = time.strftime('%X', time.localtime(time.time()))
                print "[%s] %s" % (time_str, info)
            except:
                pass
            lock.release()
    vulscan类

    参考文章:https://xz.aliyun.com/t/4104#toc-9

    *************不积跬步无以至千里*************

  • 相关阅读:
    百度富文本编辑器的上传图片的路径问题
    laravel初次学习总结及一些细节
    macOS apache配置及开启虚拟服务器的开启,apache开启重写模式
    类似于qq空间类型的评论和回复
    向php提交数据及json
    mac 初次配置apache,及mac下安装mysql
    C#连接mysql数据库插入数据后获取自增长主键ID值
    PHP 真正多线程的使用
    C# 连接mysql数据库
    MySql状态查看方法 MySql如何查看连接数和状态?
  • 原文地址:https://www.cnblogs.com/liangshian/p/11739693.html
Copyright © 2011-2022 走看看