巡風的源碼理解

先看看巡風總體的架構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是怎麼上傳到博客園的,博客園寫小的博客記錄還行,大的話,滾動條拉的藍瘦

相關文章
相關標籤/搜索