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