巡風掃描器--web頁面源碼分析

1、view部分的結構
在這裏插入圖片描述

  • lib:主程序依賴的庫文件包;
  • static:靜態文件庫;
  • templates:模板文件庫;
  • init.py : app對象創建以及數據庫初始化的python文件;
  • view.py: app對象創建以及數據庫初始化的python文件。

2、登錄界面

@app.route('/login', methods=['get', 'post'])
def Login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        account = request.form.get('account')
        password = request.form.get('password')
        if account == app.config.get('ACCOUNT') and password == app.config.get('PASSWORD'):
            session['login'] = 'loginsuccess'
            return redirect(url_for('Search'))
        else:
            return redirect(url_for('Login'))

如果seesion中的login等於loginsuccess 就跳轉到登錄後的頁面,否則跳轉掉Error模板。
3、搜索任務界面

# 搜索頁
@app.route('/filter')
@logincheck
def Search():
    return render_template('search.html')

這個函數的功能是戰術搜索的頁面;
search.html

  <input type="text" class="form-control"
                        placeholder="Example:  ip: 192.168.1.1; port: 22"
                        style="color: #797979;" id="filter" name="q">

用戶在前端輸入搜索的條件,然後這裏的name=q的作用就是將用戶輸入的條件命名爲q;
4、Deleteall()函數

@app.route('/deleteall', methods=['post'])
@logincheck
def Deleteall():
    Mongo.coll['Task'].remove({}) #選擇Task這個數據表,移除所有數據
    return 'success'

Mongo 跟過去發現是連接mongoDB數據庫的,選擇Task這個數據表,移除所有數據,就是將任務總數全部刪除。
5、搜索結果頁–Main()

def Main():
    q = request.args.get('q', '')
    page = int(request.args.get('page', '1'))
    plugin = Mongo.coll['Plugin'].find()  # 插件列表  連接數據庫,列出plugin中所有的清單
    plugin_type = plugin.distinct('type')  # 插件類型列表,從獲取的所有清單裏面獲取名字是type的數據
    if q:  # 基於搜索條件顯示結果
        result = q.strip().split(';')  #然後將q進行分割“:”   主要是分割類似這種的 q= 127.0.0.1;127.8.8.1
        query = querylogic(result)
        cursor = Mongo.coll['Info'].find(query).sort('time', -1).limit(page_size).skip((page - 1) * page_size)
        return render_template('main.html', item=cursor, plugin=plugin, itemcount=cursor.count(),
                               plugin_type=plugin_type, query=q)
    else:  # 自定義,無任何結果,用戶手工添加
        return render_template('main.html', item=[], plugin=plugin, itemcount=0, plugin_type=plugin_type)

  • 先獲取傳入的q和page;
 q = request.args.get('q', '')
 page = int(request.args.get('page', '1'))
 plugin = Mongo.coll['Plugin'].find()  # 插件列表  連接數據庫,列出plugin中所有的清單
 plugin_type = plugin.distinct('type')  # 插件類型列表,從獲取的所有清單裏面獲取名字是type的數據
  • 接着將q進行分割“;” 主要是分割類似這種的 q= 127.0.0.1;127.0.0.8;
if q:  # 基於搜索條件顯示結果
        result = q.strip().split(';')  #然後將q進行分割“:”   主要是分割類似這種的 q= 127.0.0.1;127.8.8.1
  • 然後將result傳入到querylogic函數中;
 query = querylogic(result)
  • 將搜索的值轉化爲mongoDB能查詢的語句;
cursor = Mongo.coll['Info'].find(query).sort('time', -1).limit(page_size).skip((page - 1) * page_size)

在Info表裏把條件帶入;
6、新增任務—Addtask()

def Addtask():
    #先獲取頁面傳入的值
    title = request.form.get('title', '')   # 任務名稱
    plugin = request.form.get('plugin', '') # 從插件列表裏所選擇的插件
    condition = unquote(request.form.get('condition', ''))   # 所選結果的ip地址
    plan = request.form.get('plan', 0) # 執行週期
    ids = request.form.get('ids', '')  # 所選地址的 ip:端口
    isupdate = request.form.get('isupdate', '0')   # 是否自動更新列表
    resultcheck = request.form.get('resultcheck', '0')  # 結果集是否全選
    result = 'fail' #先默認result爲fail
    if plugin:
        targets = []
        if resultcheck == 'true':  # 結果集全選
            list = condition.strip().split(';')
            query = querylogic(list)
            cursor = Mongo.coll['Info'].find(query)
            for i in cursor:
                tar = [i['ip'], i['port']]
                targets.append(tar)
        else:  # 當前頁結果選擇
            for i in ids.split(','):
                tar = [i.split(':')[0], int(i.split(':')[1])]
                targets.append(tar)
        temp_result = True
        for p in plugin.split(','):
            query = querylogic(condition.strip().split(';'))
            item = {'status': 0, 'title': title, 'plugin': p, 'condition': condition, 'time': datetime.now(),
                    'target': targets, 'plan': int(plan), 'isupdate': int(isupdate), 'query': dumps(query)}
            insert_reuslt = Mongo.coll['Task'].insert(item)
            if not insert_reuslt:
                temp_result = False
        if temp_result:
            result = 'success'
    return result

在這裏插入圖片描述

  • 這個函數的作用是,在用戶選擇了新增任務後,根據用戶輸入的任務名稱、在插件列表所選的插件、執行週期等獲取他們傳入的參數的值;
  • 先默認result = 'fail',如果沒有插件那麼就直接返回fail;
  • 有的話,判斷是否是結果集全選,然後將結果集中的ip:port加入列表,然後執行插入傳到數據庫;沒有選結果集的話,根據當前頁結果選擇在執行插入。

7、任務列表界面–Task()

  #查詢出任務信息,並展示
    page = int(request.args.get('page', '1'))
    cursor = Mongo.coll['Task'].find().sort('time', -1).limit(page_size).skip((page - 1) * page_size)
    return render_template('task.html', item=cursor)

查詢出任務信息,然後展示到頁面上。
8、複測任務–Recheck()

# 複測任務異步
@app.route('/taskrecheck')
@logincheck
def Recheck():
    tid = request.args.get('taskid', '') #獲取前端需要複測的任務
    task = Mongo.coll['Task'].find_one({'_id': ObjectId(tid)})  # 找到任務,判斷掃描完成後更新數據庫,返回success
    result = 'fail'
    if task and task['plan'] == 0 and task['status'] == 2:  # 一次性任務,並且已經掃描完成
        #更新數據庫內容
        result = Mongo.coll['Task'].update({'_id': ObjectId(tid)}, {'$set': {'status': 0}})
        if result:
            result = 'success'
    return result

獲取前端需要進行復測的任務,然後在數據庫找到這個任務進行掃描,完後後更新數據庫內容。
9、任務詳情界面–TaskDetail()

def TaskDetail():
    id = request.args.get('taskid', '')#點擊具體任務時獲取到的參數
    page = int(request.args.get('page', '1'))
    taskdate = request.args.get('taskdate', "") #查詢指定的日期
    plugin_name = ''
    #從數據庫找到任務具體信息
    task_info = Mongo.coll['Task'].find_one({'_id': ObjectId(id)})
    if task_info:
        plugin_name = task_info['plugin']
    vulcount = 0
    #獲取集合中指定字段的不重複值
    lastscan = Mongo.coll["Result"].distinct('task_date', {'task_id': ObjectId(id)})
    result_list = []
    if len(lastscan) > 0:
        lastscan.sort(reverse=True)#reverse -- 排序規則,reverse = True 降序
        if taskdate:  # 根據掃描批次查看結果
            cursor = Mongo.coll['Result'].find(
                {'task_id': ObjectId(id), 'task_date': datetime.strptime(taskdate, "%Y-%m-%d %H:%M:%S.%f")}).sort(
                'time', -1).limit(page_size).skip((page - 1) * page_size)
        else:  # 查看最新批次結果
            taskdate = lastscan[0].strftime("%Y-%m-%d %H:%M:%S.%f")
            cursor = Mongo.coll['Result'].find(
                {'task_id': ObjectId(id), 'task_date': lastscan[0]}).sort('time', -1).limit(page_size).skip(
                (page - 1) * page_size)
        vulcount = cursor.count()
        for _ in cursor:
            result_list.append(
                {'ip': _['ip'], 'port': _['port'], 'info': _['info'], 'vul_level': _['vul_info']['vul_level'],
                 'time': _['time']})

        # 速度優化,數據量多采取不同的方式查詢
        if len(result_list) > 100:
            ip_hostname = {}
            hostname = Mongo.coll['Info'].aggregate(
                [{'$match': {'hostname': {'$ne': None}}}, {'$project': {'_id': 0, 'ip': 1, 'hostname': 1}}])
            for _ in hostname:
                if 'hostname' in hostname:
                    ip_hostname[_["ip"]] = _["hostname"]
            for _ in result_list:
                if 'ip' in ip_hostname:
                    _['hostname'] = ip_hostname[_["ip"]]
                else:
                    _['hostname'] = ''
        else:
            for _ in result_list:
                hostname = Mongo.coll['Info'].find_one({'ip': _['ip']})
                if hostname and 'hostname' in hostname:
                    _['hostname'] = hostname['hostname']
                else:
                    _['hostname'] = ''
    return render_template('detail.html', item=result_list, count=vulcount, id=id, taskdate=taskdate,
                           plugin_name=plugin_name, scanlist=lastscan)

通過id找到任務詳情,然後將詳情在web頁面展示出來。
10、刪除異步任務–DeleteTask()

def DeleteTask():
    # 獲取將要刪除任務的id
    oid = request.form.get('oid', '')#點擊刪除的時候,獲取參數
    if oid:
        #Mongodb刪除數據,可以使用delect_one()方法來刪除一個文檔,該方法爲第一個參數爲查詢對象,指定要刪除哪些數據
        result = Mongo.coll['Task'].delete_one({'_id': ObjectId(oid)})
        if result.deleted_count > 0:
            result = Mongo.coll['Result'].delete_many({'task_id': ObjectId(oid)})
            if result:
                return 'success'
    return 'fail'
  • 點擊刪除時,獲取要刪除任務的id,然後在數據庫中刪除。
  • Mongodb刪除數據,可以使用delect_one()方法來刪除一個文檔,該方法爲第一個參數爲查詢對象,指定要刪除哪些數據。
   result = Mongo.coll['Task'].delete_one({'_id': ObjectId(oid)})

11、下載excel報表異步–DownloadXls()

def DownloadXls():
    tid = request.args.get('taskid', '')
    taskdate = request.args.get('taskdate', '')
    result_list = []
    #下載某一個具體的任務
    if tid:  # 有任務id
        if taskdate:  # 從任務中拉取指定批次掃描結果
            taskdate = datetime.strptime(taskdate, "%Y-%m-%d %H:%M:%S.%f")
            cursor = Mongo.coll['Result'].find({'task_id': ObjectId(tid), 'task_date': taskdate}).sort(
                'time', -1)#找到對應任務具體的信息
        else:  # 從任務中直接取該任務最新一次掃描結果
            #在表中,一個列可能會包含多個重複值,有時您也許希望僅僅列出不同(distinct)的值。DISTINCT 關鍵詞用於返回唯一不同的值,並以數組的形式返回。
            lastscan = Mongo.coll["Result"].distinct('task_date', {'task_id': ObjectId(tid)})#獲取任務最新的日期--在Result表中,對task_date子段去重,篩選條件task_id
            #db.getCollection("表名").distinct('字段名',{篩選條件})
            if len(lastscan) == 0:
                cursor = []
                taskdate = datetime.now()
            else:
                lastscan.sort(reverse=True)
                taskdate = lastscan[0]
                cursor = Mongo.coll['Result'].find({'task_id': ObjectId(tid), 'task_date': taskdate}).sort(
                    'time', -1)#sort函數排序,1爲升序,-1爲降序
        title = Mongo.coll['Task'].find_one({'_id': ObjectId(tid)})['title']
        for _ in cursor:
            hostname = ''
            result = Mongo.coll['Info'].find_one({'ip': _['ip']})
            if result and 'hostname' in result:
                hostname = result['hostname']
            #將掃描結果查詢出來後加到result_list
            result_list.append(
                {'ip': _['ip'], 'port': _['port'], 'info': _['info'], 'vul_level': _['vul_info']['vul_level'],
                 'time': _['time'], 'vul_name': _['vul_info']['vul_name'], 'lastscan': taskdate, 'title': title,
                 'hostname': hostname})
        response = make_response(CreateTable(result_list, taskdate.strftime("%Y%m%d-%H%M%S")))
        if taskdate == '':
            response.headers["Content-Disposition"] = "attachment; filename=nodata.xls;"
        else:
            response.headers["Content-Disposition"] = "attachment; filename=" + quote(
                title.encode('utf-8')) + taskdate.strftime(
                "%Y-%m-%d-%H-%M-%S") + ".xls;"
    else:  # 下載綜合報表,下載任務頁面裏面的所有任務
        tasks = Mongo.coll['Task'].find({})#在數據庫裏面找到找到Task的所有數據
        t_list = []
        for t in tasks:
            name = t['title']
            lastscan = Mongo.coll["Result"].distinct('task_date', {'task_id': t['_id']})#獲取最新更新任務的信息
            if len(lastscan) == 0:
                cursor = Mongo.coll['Result'].find({'task_id': t['_id']})
                taskdate = None
            else:
                lastscan.sort(reverse=True)
                taskdate = lastscan[0]
                cursor = Mongo.coll['Result'].find({'task_id': t['_id'], 'task_date': taskdate})
            for _ in cursor:  # 單任務詳情
                hostname = Mongo.coll['Info'].find_one({'ip': _['ip']})
                if hostname:
                    _['hostname'] = hostname['hostname']
                else:
                    _['hostname'] = None
                _['title'] = name
                _['vul_level'] = _['vul_info']['vul_level']
                _['vul_name'] = _['vul_info']['vul_name']
                _['lastscan'] = taskdate
                t_list.append(_)
        response = make_response(CreateTable(t_list, 'all_data'))
        response.headers["Content-Disposition"] = "attachment; filename=all_data.xls;"
    response.headers["Content-Type"] = "application/x-xls"
    return response
  • 可以下載單個任務,也可以在任務總數頁面選擇下載全部任務;
  • lastscan = Mongo.coll["Result"].distinct('task_date', {'task_id': ObjectId(tid)})
    db.getCollection(“表名”).distinct(‘字段名’,{篩選條件})
    這句可以實現在Result這張表中,我們可以選擇從task_date中選擇task_id不同的,即我們可以獲得任務最新的掃描結果;
  • 在表中,一個列可能會包含多個重複值,有時我們只需要列出不同(distinct)的值所以distinct關鍵詞用於返回唯一不同的值,並以數組的形式返回。
    12、搜索結果報表下載—search_result_xls()
def search_result_xls():
    query = request.args.get('query', '')
    if query:
        result = query.strip().split(';')
        filter_ = querylogic(result)
        cursor = Mongo.coll['Info'].find(filter_).sort('time', -1)
        title_tup = ('IP', '端口號', '主機名', '服務類型')
        xls = [title_tup, ]
        for info in cursor:
            item = (
                info.get('ip'), info.get('port'),
                info.get('hostname'), info.get('server')
            )
            xls.append(item)
        file = write_data(xls, 'search_result')
        resp = make_response(file.getvalue())
        resp.headers["Content-Disposition"] = "attachment; filename=search_result.xls;"
        resp.headers["Content-Type"] = "application/x-xls"
        resp.headers["X-Content-Type-Options"] = "nosniff"
        return resp
    else:
        redirect(url_for('NotFound'))

搜索結果有的話寫入文件下載,沒有的返回NotFound。
13、插件列表頁—Plugin()

def Plugin():
    page = int(request.args.get('page', '1'))
    cursor = Mongo.coll['Plugin'].find().limit(page_size).skip((page - 1) * page_size)#在數據庫裏面找到插件的信息
    return render_template('plugin.html', cursor=cursor, vultype=cursor.distinct('type'), count=cursor.count())#在前端頁面展示

14、新增插件異步—AddPlugin()

def AddPlugin():
    result = 'fail'
    isupload = request.form.get('isupload', 'false')#是否共享上傳
    methodurl = request.form.get('methodurl', '')
    file_name = ''
    if methodurl == '':
        f = request.files['file']#獲取上傳的文件
        fname = secure_filename(f.filename)#使用secure_filename處理文件名,secure_filename()函數只返回ASCII字符,非ASCII字符會被過濾掉。
        if fname.split('.')[-1] == 'py':#如果上傳的文件類型爲python文件。
            path = file_path + fname
            #判斷插件是否存在
            if os.path.exists(file_path + fname):
                fname = fname.split('.')[0] + '_' + str(datetime.now().second) + '.py'#存在,就將插件重命名
                path = file_path + fname
            f.save(path)#
            if os.path.exists(path):
                file_name = fname.split('.')[0]
                module = __import__(file_name)#用__import__()加載這個文件
                mark_json = module.get_plugin_info()#獲取到裏面的get_plugin_info()的信息
                mark_json['filename'] = file_name#加入文件名
                mark_json['add_time'] = datetime.now()#加入時間
                mark_json['count'] = 0
                if 'source' not in mark_json:
                    mark_json['source'] = 0
                insert_result = Mongo.coll['Plugin'].insert(mark_json)#將以上信息保存到數據庫中
                if insert_result:
                    result = 'success'
                    file_name = file_name +'.py'

    else:#上傳的文件類型爲json格式
        #獲取傳入的參數
        name = request.form.get('name', '')
        info = request.form.get('info', '')
        author = request.form.get('author', '')
        level = request.form.get('level', '')
        type = request.form.get('vultype', '')
        keyword = request.form.get('keyword', '')
        pluginurl = request.form.get('pluginurl', '')
        methodurl = request.form.get('methodurl', '')
        pdata = request.form.get('pdata', '')
        analyzing = request.form.get('analyzing', '')
        analyzingdata = request.form.get('analyzingdata', '')
        tag = request.form.get('tag', '')
        try:
            query = {'name': name, 'info': info, 'level': level, 'type': type, 'author': author, 'url': pluginurl,
                     'keyword': keyword, 'source': 0}
            query['plugin'] = {'method': methodurl.split(' ', 1)[0], 'url': methodurl.split(' ', 1)[1],
                               'analyzing': analyzing, 'analyzingdata': analyzingdata, 'data': pdata, 'tag': tag}
            file_name = secure_filename(name) + '_' + str(datetime.now().second) + ".json"
            with open(file_path + file_name, 'wb') as wt:
                wt.writelines(json.dumps(query))
            query.pop('plugin')
            query['add_time'] = datetime.now()
            query['count'] = 0
            query['filename'] = file_name
            insert_result = Mongo.coll['Plugin'].insert(query)#保存在數據庫中
            if insert_result:
                result = 'success'
        except:
            pass
    if isupload == 'true' and result == 'success':#選擇共享插件並且上傳成功
        code_tuple = open(file_path+file_name).read()
        code = ''
        for _ in code_tuple:
            code += _
        params = {'code': code}
        req = urllib2.Request('https://sec.ly.com/xunfeng/pluginupload')
        req.add_header('Content-Type','application/x-www-form-urlencoded')
        rsp = urllib2.urlopen(req,urlencode(params))
        print 'upload result:' + rsp.read()
    return result

根據插件的格式不同進行分類。
15、刪除插件異步—DeletePlugin()

def DeletePlugin():
    oid = request.form.get('oid', '')#獲取要刪除的插件
    if oid:
        result = Mongo.coll['Plugin'].find_one_and_delete({'_id': ObjectId(oid)}, remove=True)#在數據庫找到這個插件並刪除
        if not result['filename'].find('.') > -1:
            result['filename'] = result['filename'] + '.py'
        if os.path.exists(file_path + result['filename']):#將file_path下面的這個插件移除
            os.remove(file_path + result['filename'])
            return 'success'
    return 'fail'

獲取要刪除的插件,在數據庫中刪除這個插件的信息,在插件的文件下移除這個插件。
16、統計頁面

def Analysis():
    ip = len(Mongo.coll['Info'].distinct('ip'))#獲取Info中ip的的個數(去重)
    record = Mongo.coll['Info'].find().count()
    task = Mongo.coll['Task'].find().count()#獲取任務數
    vul = int(Mongo.coll['Plugin'].group([], {}, {'count': 0},'function(doc,prev){prev.count = prev.count + doc.count}')[0]['count'])
    plugin = Mongo.coll['Plugin'].find().count()#插件總數
    vultype = Mongo.coll['Plugin'].group(['type'], {"count":{"$ne":0}}, {'count': 0},'function(doc,prev){prev.count = prev.count + doc.count}')
    cur = Mongo.coll['Statistics'].find().sort('date', -1).limit(30)
    trend = []
    for i in cur:
        trend.append(
            {'time': i['date'], 'add': i['info']['add'], 'update': i['info']['update'], 'delete': i['info']['delete']})
    vulbeat = Mongo.coll['Heartbeat'].find_one({'name': 'load'})
    scanbeat = Mongo.coll['Heartbeat'].find_one({'name': 'heartbeat'})
    if vulbeat == None or scanbeat == None:
        taskpercent = 0
        taskalive = False
        scanalive = False
    else:
        taskpercent = vulbeat['value'] * 100
        taskalive = (datetime.now() - vulbeat['up_time']).seconds
        scanalive = (datetime.now() - scanbeat['up_time']).seconds
        taskalive = True if taskalive < 120 else False
        scanalive = True if scanalive < 120 else False
    server_type = Mongo.coll['Info'].aggregate(
        [{'$group': {'_id': '$server', 'count': {'$sum': 1}}}, {'$sort': {'count': -1}}])
    web_type = Mongo.coll['Info'].aggregate([{'$match': {'server': 'web'}}, {'$unwind': '$webinfo.tag'},
                                             {'$group': {'_id': '$webinfo.tag', 'count': {'$sum': 1}}},
                                             {'$sort': {'count': -1}}])
    return render_template('analysis.html', ip=ip, record=record, task=task, vul=vul, plugin=plugin, vultype=vultype,
                           trend=sorted(trend, key=lambda x: x['time']), taskpercent=taskpercent, taskalive=taskalive,
                           scanalive=scanalive, server_type=server_type, web_type=web_type)

將數據庫中的值查詢然後顯示在web頁面。
17、配置頁面—Config()

def Config():
    val = []
    table = request.args.get('config', '')
    if table in ("vulscan", "nascan"):#判斷是爬蟲引擎還是掃描引擎
        dict = Mongo.coll['Config'].find_one({'type': table})#從數據庫中找到對應的數據
        if dict and 'config' in dict:
            dict = dict['config']
            for _ in dict:
                if _.find('_') > 0:
                    item_type = "list"
                else:
                    item_type = "word"
                val.append({"show": item_type, "type": _, "info": dict[_]["info"], "help": dict[_]["help"],
                            "value": dict[_]["value"]})
    val = sorted(val, key=lambda x: x["show"], reverse=True)
    return render_template('config.html', values=val)

判斷是爬蟲引擎還是掃描引擎,然後從數據庫查出數據。
18、配置更新異步—UpdateConfig()

def UpdateConfig():
    rsp = 'fail'
    name = request.form.get('name', 'default')
    value = request.form.get('value', '')
    conftype = request.form.get('conftype', '')
    if name and value and conftype:
        if name == 'Masscan' or name == 'Port_list':
            origin_value = Mongo.coll['Config'].find_one({'type': 'nascan'})["config"][name]["value"]
            value = origin_value.split('|')[0] + '|' + value
        elif name == 'Port_list_Flag':
            name = 'Port_list'
            origin_value = Mongo.coll['Config'].find_one({'type': 'nascan'})["config"]['Port_list']["value"]
            value = value + '|' + origin_value.split('|')[1]
        elif name == 'Masscan_Flag':
            name = 'Masscan'
            path = Mongo.coll['Config'].find_one({'type': 'nascan'})["config"]["Masscan"]["value"]
            if len(path.split('|')) == 3:
                path = path.split('|')[1] + "|" + path.split('|')[2]
            else:
                path = path.split('|')[1]
            if value == '1':
                value = '1|' + path
            else:
                value = '0|' + path
        result = Mongo.coll['Config'].update({"type": conftype}, {'$set': {'config.' + name + '.value': value}})
        if result:
            rsp = 'success'
    return rsp

根據name來判斷是哪個配置,就從數據庫去取對應的值,然後把提交過來的value加上去更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章