记录一下巡风扫描器view.py这个脚本里的视图函数的学习,里面有一些print 代码是为了把数据打印出来小白我自己加的,勿怪勿怪。可能存在一些理解错误和不到位的地方,希望大佬多多指正。。
0x01:跳转到登陆页面
第二遍看这个脚本的源码时,想到一个问题,如果你在浏览器地址栏里输入http://127.0.0.1/login
可以跳转到登陆页面,如果只输入127.0.0.1
,这时候并没有运行Login这个视图函数,却也能直接跳转到登陆页面,这是为什么呢?原来,在Main视图函数上面有这样两行代码:
@app.route('/') @logincheck def Main():
只输入127.0.0.1时,相当于访问了根目录,会运行Main视图函数,而要运行Main函数,要先运行logincheck这个用于判断是否已经登陆的修饰函数,因为此时还未登陆,在logincheck函数里给我们跳转到了Login函数。
return redirect(url_for('Login'))
好了,下面开始看代码(掉头发)。。
0x02:Login视图函数
为什么先看这个呢?因为你要先登陆啊(ps:屁话,不登陆怎么进入)
@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'))
调转到登陆页面后,就要输入账号密码了,前端输入的账号密码,传到login.html这个页面,并命名为account和password,是login.html里的这两行代码:
<input class="form-control" type="text" required="" placeholder="Account" name="account"> <input class="form-control" type="password" required="" placeholder="Password" name="password">
通过request.form.get获取到输入的账号密码之后,在数据库里面做验证,验证通过后,设置session['login'] = 'loginsuccess'
,用于后面的登陆验证,然后跳转到Search函数。
0x03:logincheck登陆验证函数
def logincheck(f): @wraps(f) def wrapper(*args, **kwargs): try: if session.has_key('login'): if session['login'] == 'loginsuccess': return f(*args, **kwargs) else: return redirect(url_for('Login')) else: return redirect(url_for('Login')) except Exception, e: print e return redirect(url_for('Error')) return wrapper
如果session的login字段等于loginsuccess,则说明已经登陆,否则跳转到Login函数,让用户登陆
0x04:Search视图函数
@app.route('/filter') @logincheck def Search(): return render_template('search.html')
这个函数的功能就是接受前端用书输入的搜索条件,并名为为q,是Search.html里面的这行代码
<input type="text" class="form-control" placeholder="Example: ip: 192.168.1.1; port: 22" style="color: #797979;" id="filter" name="q">
0x05:Deleteall视图函数
@app.route('/deleteall', methods=['post']) @logincheck def Deleteall(): # 移除仓库下的Task数据库 Mongo.coll['Task'].remove({}) return 'success'
Mongo是从__init.py__里面引入过来的,是数据库的连接对象,这个函数的作用是,使用Mongo,找到Task这个数据库并移除
0x06:Main视图函数
def Main(): # 获取前端的q q = request.args.get('q', '') page = int(request.args.get('page', '1')) plugin = Mongo.coll['Plugin'].find() # 插件列表,列出Plugin中的所有列表 plugin_type = plugin.distinct('type') # 插件类型列表 if q: # 基于搜索条件显示结果 # 分开多条件查询 result = q.strip().split(';') print result query = querylogic(result) # 判断搜索类型,返回字典{"port": ,"ip": , "banner": ,"hostname"} # 在info表里把条件代入查询sort()根据time字段排序 limit()分页 # .find返回<class 'pymongo.cursor.Cursor'>类型对象 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就是你在前端输入的搜索条件,鼠标放在“搜索”旁边的问号图标上,可以查到所支持的搜索方式,
0x07:Getplugin视图函数
@app.route('/getplugin', methods=['get', 'post']) @logincheck def Getplugin(): # 获取了type risk search 是否有值 # 没有的话,就全部查询。 # 有的话 在Plugin表代入条件查询。然后将插件名字和信息转json格式返回。 type = request.form.get('type', '') risk = request.form.get('risk', '') search = request.form.get('search', '') print type,risk,search query = {} if type: query['type'] = type if risk: query['level'] = risk if search: search = unquote(search) query['name'] = {"$regex": search, '$options': 'i'} cursor = Mongo.coll['Plugin'].find(query) print list(cursor) rsp = [] # 把插件的name和info添加到列表中 for i in cursor: result = {'name': i['name'], 'info': i['info']} rsp.append(result) return json.dumps(rsp)
type、risk在新增任务的那个弹窗里或偶的,search还没找到是从哪里获取的
0x08:Addtask视图函数
@app.route('/addtask', methods=['get', 'post']) @logincheck def Addtask(): # 先获取了页面传了的值 先默认result为fail # 没有plugin的话直接返回fail # 有的话,先判断结果集是否全选,将结果集的ip和port都加入列表,否则将当前页的ip将入列表。 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') # 结果集是否全选 print title,plugin,condition,plan,ids,isupdate,resultcheck 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
这个函数的作用,在搜索之后,如果要根据搜索结果新增任务,就获取你新增任务时的一些参数,任务名、是否自动更新、是都全选等。
0x09:Task视图函数
@app.route('/task') @logincheck def 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)
利用Mongo查询出Task数据库里面的所有任务,排序、分页并在前端展示
0x10:Recheck视图函数
# 复测任务异步 @app.route('/taskrecheck') @logincheck def Recheck(): # 获取前端需要复测的任务 tid = request.args.get('taskid', '') # 找到任务,判断扫描完成后更新数据库,返回success task = Mongo.coll['Task'].find_one({'_id': ObjectId(tid)}) 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
在任何一个任务框里,点击沙漏图标,可以对该任务进行复测。
0x11:TaskDetail视图函数
# 任务详情页面 @app.route('/taskdetail') @logincheck def TaskDetail(): # 获取任务id 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)}) print id,taskdate 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) 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,获取到该任务的数据,传到detail.html页面,并在前端展示
参考文章:一个还需要努力的人:巡风源码解读与分析