巡风的源码理解

先看看巡风总体的架构css

 

 

   aider 这个目录是辅助验证的html

  db 是数据库前端

  dockerconf docker的配置web

  docs 里面保存是windows Linux docker环境下的使用文档ajax

  install 安装 这个是.sh ,那应该就是Linux的安装时才使用到的文件夹了mongodb

  masscan 目录 里面存放的是根据不一样操做系统选择不一样的masscan的版本docker

  nascan 这个目录存放的是资产探测相关的东西数据库

  venv 虚拟环境,这个是根据requirements.txt建立的虚拟安装flask

  views 巡风的视图存放windows

  vulscan 目录存放的是漏洞探测(其中包括打poc)

 

 

再看views这个目录,看这目录中的源码以前,必定必定要把巡风玩熟练了,再看,要否则很容易懵逼。玩的时候注意视图函数的跳转,有的地方是不跳转的。

 

 这个目录比较复杂,因此我就单独拿出来写了。若是作过flask框架,或者作事后端,比较容易理解,由于前端真的是很扎心。。。。

 lib能够说是一个动态库(我百度搜的。。。)

  lib 里面的 ___init__.py是作初始化的工做,就是在引用这个目录的文件以前,先执行这个__init___

AntiCSRF巡风的csrf防护,巡风对于csrf防护的时候用的是加referer的形式(csrf防护有两种机制,一种是加token,一种是加referer,相对来讲仍是加token更安全一点,毕竟referer是能够伪造的)

这里附上巡风的csrf防护的referer源码,判断referer的依据是,首先在一个request里面要有referer,再有这个referer的格式化必需要跟服务器内的相等

from functools import wraps
from flask import url_for, redirect, request
# 检查referer
def anticsrf(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            if request.referrer and request.referrer.replace('http://', '').split('/')[0] == request.host:
                return f(*args, **kwargs)
            else:
                return redirect(url_for('NotFound'))
        except Exception, e:
            print e
            return redirect(url_for('Error'))

    return wrapper

ps:巡风自己是一个内网探测的工具,但是这个工具还须要加上csrf防护?我感受这就不必了吧。。毕竟是内网,csrf的用处不就是加权限?难道我作内网测试的时候,还有人打我?(嘻嘻)

  Conn.py 是链接mongdb的数据的文件,大体知道下就好了

 

from pymongo import MongoClient


# 数据库链接
class MongoDB(object):
    def __init__(self, host='localhost', port=27017, database='xunfeng', username='', password=''):
        self.host = host
        self.port = port
        self.database = database
        self.conn = MongoClient(self.host, self.port)
        self.coll = self.conn[self.database]
        self.coll.authenticate(username, password)

 

Create_Excel.py

这个是建立excel的脚本,这个脚本自我感受仍是比较有意思的,当我点击这个下载的时候就会执行这个脚本里面的某个函数,一会详细分析

 

源码:

# -*- coding: UTF-8 -*-

import xlwt
import StringIO


# 将数据保存成excel
def write_data(data, tname):
    file = xlwt.Workbook(encoding='utf-8')
    table = file.add_sheet(tname, cell_overwrite_ok=True)
    l = 0
    for line in data:
        c = 0
        for _ in line:
            table.write(l, c, line[c])
            c += 1
        l += 1
    sio = StringIO.StringIO()
    file.save(sio)
    return sio


# excel业务逻辑处理
def CreateTable(cursor, id):
    item = []
    item.append(['IP', '端口', '主机名', '风险等级', '漏洞描述', '插件类型', '任务名称', '时间', '扫描批次'])
    for i in cursor:
        if i['lastscan']:
            _ = [i['ip'], i['port'], i['hostname'], i['vul_level'], i['info'],
                 i['vul_name'], i['title'], i['time'].strftime('%Y-%m-%d %H:%M:%S'),
                 i['lastscan'].strftime('%Y-%m-%d %H:%M:%S')]
        else:
            _ = [i['ip'], i['port'], i['hostname'], i['vul_level'], i['info'],
                 i['vul_name'], i['title'], i['time'].strftime('%Y-%m-%d %H:%M:%S'), '']
        item.append(_)
    file = write_data(item, id)
    return file.getvalue()

  这个里面最重要的是导入了一个xlwt这个库,这个库在写文件的时候是整行整行的写

  StringIO与file对象很是像,但它不是磁盘上文件,而是一个内存里的“文件”,咱们能够像操做磁盘文件那样来操做StringIO。

  def write_data()就是一个保存和写一个excel的操做,create_table是excel里面写的操做。

   Login.py 这个用的是一个装饰器,就是限制有没有session的,不过巡风的这个比较有意思,一会看到视图函数的时候,再去说

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'))

QueryLogic,就是在首页点搜索的时候,要对数据进行处理,由于这个我以前写过一个搜索的方法,因此看着仍是比较容易的。

import re


def mgo_text_split(query_text):
    ''' split text to support mongodb $text match on a phrase '''
    sep = r'[`\-=~!@#$%^&*()_+\[\]{};\'\\:"|<,./<>?]'
    word_lst = re.split(sep, query_text)
    text_query = ' '.join('\"{}\"'.format(w) for w in word_lst)
    return text_query


# 搜索逻辑
def querylogic(list):
    query = {}
    if len(list) > 1 or len(list[0].split(':')) > 1:
        for _ in list:
            if _.find(':') > -1:
                q_key, q_value = _.split(':', 1)
                if q_key == 'port':
                    query['port'] = int(q_value)
                elif q_key == 'banner':
                    zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
                    contents = q_value
                    match = zhPattern.search(contents)
                    # 若是没有中文用全文索引
                    if match:
                        query['banner'] = {"$regex": q_value, '$options': 'i'}
                    else:
                        text_query = mgo_text_split(q_value)
                        query['$text'] = {'$search': text_query, '$caseSensitive':True}
                elif q_key == 'ip':
                    query['ip'] = {"$regex": q_value}
                elif q_key == 'server':
                    query['server'] = q_value.lower()
                elif q_key == 'title':
                    query['webinfo.title'] = {"$regex": q_value, '$options': 'i'}
                elif q_key == 'tag':
                    query['webinfo.tag'] = q_value.lower()
                elif q_key == 'hostname':
                    query['hostname'] = {"$regex": q_value, '$options': 'i'}
                elif q_key == 'all':
                    filter_lst = []
                    for i in ('ip', 'banner', 'port', 'time', 'webinfo.tag', 'webinfo.title', 'server', 'hostname'):
                        filter_lst.append({i: {"$regex": q_value, '$options': 'i'}})
                    query['$or'] = filter_lst
                else:
                    query[q_key] = q_value
    else:
        filter_lst = []
        for i in ('ip', 'banner', 'port', 'time', 'webinfo.tag', 'webinfo.title', 'server', 'hostname'):
            filter_lst.append({i: {"$regex": list[0], '$options': 'i'}})
        query['$or'] = filter_lst
    return query

 简单说下,若是用户输入的是port:22,那么巡风如才知道的呢??分析下端口是22。这个要先根据 :分片,这个巡风在进行检索的时候才会是知道port 22的全部结果。

 views.static 目录,这个目录就是把图片,css,js前端相关的一些东西除了html都封装到这个目录里面了,这个是flask定义好的。

 views.templates这个目录里面封装的是视图函数用到的全部html。不过我可纳闷,为何html 、css、js这些东西非要分开放两个文件夹呢?不直接放到一个目录下,这个有空再去搜索看吧(主要仍是暂时没搜索到)。

 view.views 这个文件就是存放巡风全部的视图了。视图能够理解为就是一个url连接。

 导入库就不说了,没啥东西,直奔主题,从头开始说。

 

 巡风没有注册(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'))

分析下,获取account,password而后判断一下是否跟config里面保存的ACCOUNT、PASSWORD同样

这里导入下config.py

 

# coding:utf-8
class Config(object):
    ACCOUNT = 'admin'
    PASSWORD = '123456'


class ProductionConfig(Config):
    DB = '127.0.0.1'
    PORT = 65521
    DBUSERNAME = 'scan'
    DBPASSWORD = 'scanlol66'
    DBNAME = 'xunfeng'

这里巡风仍是比较巧的,由于是我的使用,因此在数据库里面没有建立一张用户表。直接跟config里面的验证一下,而后session添加一个常量就能够了。若是验证正确就重定向到Search,若是错误就重定向到Login里面。

  

  filter就是直接渲染一个search.html

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

 search.html里面中,除了这个搜索,其余都是经过a标签的方式进行重定向,就只有搜索功能是经过ajax,发送消息(这个暂时没有找到,等我找到了,就补充上)

  

 

  假如搜索的是port : 22,那么结果就应该是

 

 对应的就是这个视图

@app.route('/')
@logincheck
def Main():
    q = request.args.get('q', '')
    # 这里获取里两个参数,经过这样获取到搜索的条件进行查询
    page = int(request.args.get('page', '1'))
    plugin = Mongo.coll['Plugin'].find()  # 插件列表
    plugin_type = plugin.distinct('type')  # 插件类型列表
    if q:  # 基于搜索条件显示结果
        result = q.strip().split(';')
        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是获取搜索的标签,其中这个request.args.get()这个是能够好好研究研究的

   分析web界面实在是太长了,就有空再写回来吧,基本上都是经过ajax方式给后端发送数据。好好找都是可以找到的,若是是作后端的话,感受看不懂ajax也不要紧,由于ajax是前端写的(嘻嘻)。

 

    有空必定要学习下用markdown是怎么上传到博客园的,博客园写小的博客记录还行,大的话,滚动条拉的蓝瘦

相关文章
相关标签/搜索