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加上去更新。