本文内容:

巡风漏洞扫描系统漏洞扫描模块源码分析

总体架构

主函数

框架目录

.
├── kunpeng.py
├── kunpeng_c.dll
├── kunpeng_c.dylib
├── kunpeng_c.so
├── vuldb
│ ├── Confluence_CVE20158399.json
│ ├── Docker_Remote_API_20161220120458.json
│ ├── ElasticSearch_unauth.json
│ ├── MS10-070.py
│ ├── MS15-034.py
│ ├── MS17_010.py
│ ├── activemq_upload.py
│ ├── axis_config_read.py
│ ├── axis_info.json
│ ├── crack_axis.py
│ ├── crack_cisco_web.py
│ ├── crack_ftp.py
│ ├── crack_glassfish.py
│ ├── crack_grafana.py
│ ├── crack_jboss.py
│ ├── crack_jenkins.py
│ ├── crack_mongo.py
│ ├── crack_mssql.py
│ ├── crack_mysql.py
│ ├── crack_postgres.py
│ ├── crack_redis.py
│ ├── crack_resin.py
│ ├── crack_supervisor_web.py
│ ├── crack_weblogic.py
│ ├── fastcgi_rce.py
│ ├── git_index_disclosure.json
│ ├── glassfish_filread.json
│ ├── hadoop_yarn_resourcemanager_unauth_rce.json
│ ├── heartbleed_poc.py
│ ├── hikvision_crackpass.py
│ ├── iis_shortfile.py
│ ├── iis_webdav.py
│ ├── iis_webdav_rce.py
│ ├── java_rmi_rce.py
│ ├── jboss_head.py
│ ├── jboss_info.json
│ ├── jboss_rce_un.py
│ ├── jenkins_CVE_2015_8103.py
│ ├── jenkins_CVE_2017_1000353.py
│ ├── jetty_refer.py
│ ├── memcache_drdos.py
│ ├── memcache_unauth.py
│ ├── netgear_passwd.json
│ ├── nginx_CVE_2017_7529.py
│ ├── nodejs_debugger_rce.py
│ ├── phpmyadmin_crackpass.py
│ ├── resin_fileread.json
│ ├── resin_fileread_1.json
│ ├── resin_fileread_3.json
│ ├── rsync_weak_auth.py
│ ├── s2_052.py
│ ├── shiro_550.py
│ ├── st2_eval.py
│ ├── svn_entries_disclosure.json
│ ├── testing.py
│ ├── tomcat_crackpass.py
│ ├── tomcat_cve_017_12615.py
│ ├── web_fileread.py
│ ├── web_shellshock.py
│ ├── weblogic_CVE_2015_4852.py
│ ├── weblogic_CVE_2017_10271.py
│ ├── weblogic_CVE_2018_2628.py
│ ├── weblogic_ssrf.json
│ ├── websphere_CVE_2015_7450.py
│ ├── wordpress_crackpass.py
│ ├── zabbix_jsrpc_SQL.json
│ ├── zabbix_latest_sql.py
│ └── zookeeper_unauth_access.py
└── vulscan.py

这里学长让我看看这几个文件:

  • vulscan/vulscan.py
  • nascan/nascan.py
  • web.py(但这个只是一个Flask入口.....)

那就主要是前两个文件了,其他文件都是Flask的模板文件之类的,如果分析完那两个文件之后,感觉可以看看

源码分析

导入库就先不看了,遇到了就追回去看看

第16-17行

sys.path.append(sys.path[0] + '/vuldb')
sys.path.append(sys.path[0] + "/../")

这两句在于把/vuldb和上层目录加入python解析器的搜索路径里

然后开始连接MongoDB数据库,之前已经配置好了,这些用来获那些结果、插件、设置等等

db_conn = pymongo.MongoClient(ProductionConfig.DB, ProductionConfig.PORT)
na_db = getattr(db_conn, ProductionConfig.DBNAME)
na_db.authenticate(ProductionConfig.DBUSERNAME, ProductionConfig.DBPASSWORD)
na_task = na_db.Task
na_result = na_db.Result
na_plugin = na_db.Plugin
na_config = na_db.Config
na_heart = na_db.Heartbeat
na_update = na_db.Update

然后生成一个多线程的锁

lock = thread.allocate()

这几个目前不知道是干什么的,除了那个超时和多线程以外

PASSWORD_DIC = []
THREAD_COUNT = 50
TIMEOUT = 10
PLUGIN_DB = {}
TASK_DATE_DIC = {}
WHITE_LIST = []

下面的kp = kunpeng()

是实例化的kunpeng类,这玩意好像是个漏洞库?

image-20200324004438191

这里来分析一下


kunpeng.py

def __init__(self):
self.kunpeng = None
self.system = platform.system().lower()
self.pwd = os.path.split(os.path.realpath(__file__))[0]
self.suf_map = {
'windows': '.dll',
'darwin': '.dylib',
'linux': '.so'
}
self._load_kunpeng()

构造函数没啥好说的

image-20200324005230570

得了当前路径,系统类型,以及好像是动态链接库的后缀?

最后调用了_load_kunpeng()方法

def _get_lib_path(self):
file_list = os.listdir(self.pwd)
for v in file_list:
if 'kunpeng' in v and os.path.splitext(v)[1] == self.suf_map[self.system]:
return v

这个函数先是读取了当前路径下的文件,然后检测文件名是否存在kunpeng并且后缀是否符合那个字典的要求,然后返回这个文件名

可以看到它实际上是用来寻找那个链接库

image-20200324005030207

def check_version(self):
print 'check version'
release = self._get_release_latest()
# print(release)
if release['tag_name'] != self.get_version():
print 'new version', release['tag_name']
self._down_release(release['tag_name'])
return release

然后是检测版本信息,这里看看_get_release_latest()函数

def _get_release_latest(self):
body = urllib2.urlopen(
'https://api.github.com/repos/opensec-cn/kunpeng/releases/latest').read()
release = json.loads(body)
return release

可以看到是请求了,返回了请求的json

image-20200324005530150

然后判断是否有新版本,这里把版本获取函数没找到,看一下下载最新版本的函数

def _down_release(self, version):
print 'kunpeng update ', version
save_path = self.pwd + \
'/kunpeng_{}_v{}.zip'.format(self.system, version)
down_url = 'https://github.com/opensec-cn/kunpeng/releases/download/{}/kunpeng_{}_v{}.zip'.format(
version, self.system.lower(), version)
print 'url', down_url
urlretrieve(down_url, save_path, self._callbackinfo)

这里很简单,把系统类型、版本号格式化进去之后进行下载,然后调用urlretrieve函数进行保存,其中_callbackinfo函数是个回调函数,用来显示下载进度

_callbackinfo

@down:已经下载的数据块 
@block:数据块的大小
@size:远程文件的大小
def _callbackinfo(self, down, block, size):
per = 100.0*(down*block)/size
if per > 100:
per = 100
print '%.2f%%' % per

然后看看版本更新函数

def update_version(self, version):
self.close()
os.remove(self.pwd + '/' + self._get_lib_path())
save_path = self.pwd + \
'/kunpeng_{}_v{}.zip'.format(self.system, version)
z_file = zipfile.ZipFile(save_path, 'r')
dat = z_file.read('kunpeng_c' + self.suf_map[self.system])
print len(dat)
new_lib = self.pwd + '/kunpeng_v' + version + self.suf_map[self.system]
lib_f = open(new_lib,'wb')
lib_f.write(dat)
lib_f.close()
z_file.close()
print 'update success',version
self._load_kunpeng()

这里先关闭kunpeng

def close(self):
if self.system == 'windows':
_ctypes.FreeLibrary(self.kunpeng._handle)
else:
handle = self.kunpeng._handle
del self.kunpeng
_ctypes.dlclose(handle)

这个FreeLibraey函数我没搜到,不过猜测作用是释放Lib

在其他系统上是通过del self.kunpeng这个属性以及关闭句柄的

image-20200324011302856

反正ctypes这个库感觉就是用来处理动态库的

然后通过读取下载的zip文件,把数据二进制方式写入一个新的文件里,加载了_load_kunpeng()方法进行了加载

这个方法就不写了,反正就是用了ctypes这个库进行了动态链接库加载以及参数设置

self.kunpeng.GetPlugins.restype = c_char_p
self.kunpeng.Check.argtypes = [c_char_p]
self.kunpeng.Check.restype = c_char_p
self.kunpeng.SetConfig.argtypes = [c_char_p]
self.kunpeng.GetVersion.restype = c_char_p

image-20200324012236228

kunpeng.py先到这里,继续分析vulscan.py,遇到了就回到看定义


vulscan.py

构造函数没啥好看的,就直接看start函数了

def start(self):
self.get_plugin_info()
if '.json' in self.plugin_info['filename']: # 标示符检测模式
self.load_json_plugin() # 读取漏洞标示
self.set_request() # 标示符转换为请求
self.poc_check() # 检测

这里查询了插件的信息之后(通过Mongo来查询

> db.Plugin.find()
{ "_id" : ObjectId("5e797eb938e88b00324eb4bb"), "count" : 0, "add_time" : ISODate("2020-03-24T11:30:01.657Z"), "info" : "导致数据库敏感信息泄露,严重可导致服务器被入侵。", "name" : "Redis弱口令", "keyword" : "server:redis", "level" : "高危", "url" : "http://www.freebuf.com/vuls/85021.html", "author" : "[email protected]", "filename" : "crack_redis", "source" : 1, "type" : "弱口令" }
{ "_id" : ObjectId("5e797eb938e88b00324eb4bc"), "count" : 0, "add_time" : ISODate("2020-03-24T11:30:01.657Z"), "info" : "攻击者通过此漏洞最终可以达到任意文件读取的效果。", "name" : ".NET Padding Oracle信息泄露", "keyword" : "tag:aspx", "level" : "高危", "url" : "", "author" : "[email protected]", "filename" : "MS10-070", "source" : 1, "type" : "任意文件读取" }
{ "_id" : ObjectId("5e797eb938e88b00324eb4bd"), "count" : 0, "add_time" : ISODate("2020-03-24T11:30:01.657Z"), "info" : "可直接执行任意代码,进而直接导致服务器被入侵控制。", "name" : "Jboss反序列化代码执行", "keyword" : "tag:jboss", "level" : "紧急", "url" : "http://www.freebuf.com/articles/86950.html", "author" : "[email protected]", "filename" : "jboss_rce_un", "source" : 1, "type" : "代码执行" }

漏洞检测

他这里有三种检测方式

  • json
  • kunpeng漏洞库
  • python脚本

json检测

if '.json' in self.plugin_info['filename']:  # 标示符检测模式
self.load_json_plugin() # 读取漏洞标示
self.set_request() # 标示符转换为请求
self.poc_check() # 检测
def load_json_plugin(self):
json_plugin = open(sys.path[0] + '/vuldb/' +
self.plugin_info['filename']).read()
self.plugin_info['plugin'] = json.loads(json_plugin)['plugin']

读取他的plugin信息

{
"name" : "Axis2信息泄露",
"info" : "HappyAxis.jsp 页面存在系统敏感信息。",
"level" : "低危",
"type" : "信息泄露",
"author" : "[email protected]",
"url": "",
"keyword" : "tag:axis2",
"source" : 1,
"plugin" : {
"url" : "/axis2/axis2-web/HappyAxis.jsp",
"tag" : "敏感信息泄露",
"analyzing" : "keyword",
"analyzingdata" : "Axis2 Happiness Page",
"data" : "",
"method" : "GET"
}
}

然后开始把json里的url和请求方式转化为一个urllib2.Request请求

def set_request(self):
url = 'http://' + \
self.task_netloc[0] + ":" + \
str(self.task_netloc[1]) + self.plugin_info['plugin']['url']
if self.plugin_info['plugin']['method'] == 'GET':
request = urllib2.Request(url)
else:
request = urllib2.Request(url, self.plugin_info['plugin']['data'])
self.poc_request = request

然后进行POC检测

def poc_check(self):
try:
res = urllib2.urlopen(self.poc_request, timeout=30)
res_html = res.read(204800)
header = res.headers
# res_code = res.code
except urllib2.HTTPError, e:
# res_code = e.code
header = e.headers
res_html = e.read(204800)
except Exception, e:
return

前半部分是用来发送HTTP请求的,这里吐槽一下,不能直接用request库吗???

然后对页面进行了解码和编码操作

try:
html_code = self.get_code(header, res_html).strip()
if html_code and len(html_code) < 12:
res_html = res_html.decode(html_code).encode('utf-8')
except:
pass

这时候对漏洞发现关键字进行检测:

an_type = self.plugin_info['plugin']['analyzing']
vul_tag = self.plugin_info['plugin']['tag']
analyzingdata = self.plugin_info['plugin']['analyzingdata']
if an_type == 'keyword':
# print poc['analyzingdata'].encode("utf-8")
if analyzingdata.encode("utf-8") in res_html:
self.result_info = vul_tag
elif an_type == 'regex':
if re.search(analyzingdata, res_html, re.I):
self.result_info = vul_tag
elif an_type == 'md5':
md5 = hashlib.md5()
md5.update(res_html)
if md5.hexdigest() == analyzingdata:
self.result_info = vul_tag

如果说是keyword的话,他会检测那个analyzingdata数据是否在页面里,是的话返回结果是那个标签

下面的regex是通过在页面正则查找某个内容,然后返回结果

md5是通过对比页面的内容的md5analyzingdata的差别,然后返回标签结果

kunpeng检测

elif 'KP-' in self.plugin_info['filename']:
self.log(str(self.task_netloc) + 'call kunpeng - ' + self.plugin_info['filename'])
kp.set_config(TIMEOUT, PASSWORD_DIC)
if self.task_netloc[1] != 80:
self.result_info = kp.check('service', '{}:{}'.format(
self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename'])
if not self.result_info:
scheme = 'http'
if self.task_netloc[1] == 443:
scheme = 'https'
self.result_info = kp.check('web', '{}://{}:{}'.format(
scheme, self.task_netloc[0], self.task_netloc[1]), self.plugin_info['filename'])

这里的log方法通过上锁和开锁的方式来打印那个调用信息

这时候判断是否是80端口,如果不是的话调用kunpengcheck方法

def check(self, t, netloc, kpid):
task_dic = {
'type': t,
'netloc': netloc,
'target': kpid
}
r = json.loads(self.kunpeng.Check(json.dumps(task_dic)))
result = ''
if not r:
return ''
for v in r:
result += v['remarks'] + ','
return result

这里的Check方法找不到,但我推测应该是导入那个模块进行检测,然后返回remarks结果

如果发现check没有返回时,就认定他是web服务,通过设置协议、IP、端口号重新导入kunpeng::check()进行检测

python脚本检测

else:  # 脚本检测模式
plugin_filename = self.plugin_info['filename']
self.log(str(self.task_netloc) + 'call ' + self.task_plugin)
if task_plugin not in PLUGIN_DB:
plugin_res = __import__(plugin_filename)
setattr(plugin_res, "PASSWORD_DIC", PASSWORD_DIC) # 给插件声明密码字典
PLUGIN_DB[plugin_filename] = plugin_res
self.result_info = PLUGIN_DB[plugin_filename].check(
str(self.task_netloc[0]), int(self.task_netloc[1]), TIMEOUT)
self.save_request() # 保存结果

这边如果任务插件不在那个PLUGIN_DB的话

就动态import那个插件py文件

这个setattr(plugin_res, "PASSWORD_DIC", PASSWORD_DIC) # 给插件声明密码字典

是用来给弱口令啥的增加字典的

然后就是把import的那个py导入到一个字典里,然后调用那个py文件的check方法,这里随便拿一个举个例子

# coding:utf-8
# author:wolf
import urllib2


def get_plugin_info():
plugin_info = {
"name": "Axis2控制台弱口令",
"info": "攻击者通过此漏洞可以登陆管理控制台,通过部署功能可直接获取服务器权限。",
"level": "高危",
"type": "弱口令",
"author": "[email protected]",
"url": "http://www.codesec.net/view/247352.html",
"keyword": "tag:axis",
"source": 1
}
return plugin_info


def check(host, port, timeout):
url = "http://%s:%d" % (host, int(port))
error_i = 0
flag_list = ['Administration Page</title>', 'System Components', '"axis2-admin/upload"',
'include page="footer.inc">', 'axis2-admin/logout']
user_list = ['axis', 'admin', 'root']
PASSWORD_DIC.append('axis2')
for user in user_list:
for password in PASSWORD_DIC:
try:
login_url = url + '/axis2/axis2-admin/login'
PostStr = 'userName=%s&password=%s&submit=+Login+' % (user, password)
request = urllib2.Request(login_url, PostStr)
res = urllib2.urlopen(request, timeout=timeout)
res_html = res.read()
except urllib2.HTTPError, e:
return
except urllib2.URLError, e:
error_i += 1
if error_i >= 3:
return
continue
for flag in flag_list:
if flag in res_html:
info = u'存在弱口令,用户名:%s,密码:%s' % (user, password)
return info

它上面的get_plugin_info是用来获取插件信息的,但在漏洞检测进程里面是没用的

他的检测很有意思,会有错误统计,超过三次以上就直接返回了,这里如果检测到了就直接返回那个信息

保存结果

最后save_request函数保存结果

def save_request(self):
if self.result_info:
time_ = datetime.datetime.now()
self.log(str(self.task_netloc) + " " + self.result_info)
v_count = na_result.find(
{"ip": self.task_netloc[0], "port": self.task_netloc[1], "info": self.result_info}).count()
if not v_count:
na_plugin.update({"name": self.task_plugin},
{"$inc": {'count': 1}})
vulinfo = {"vul_name": self.plugin_info['name'], "vul_level": self.plugin_info['level'],
"vul_type": self.plugin_info['type']}
w_vul = {"task_id": self.task_id, "ip": self.task_netloc[0], "port": self.task_netloc[1],
"vul_info": vulinfo, "info": self.result_info, "time": time_,
"task_date": TASK_DATE_DIC[str(self.task_id)]}
na_result.insert(w_vul)
# self.wx_send(w_vul) # 自行定义漏洞提醒

这里大概就是先看看漏洞扫描结果数据库里有没有这条记录

没有的话就把Plugin库里的这个插件的扫到的漏洞的count + 1

使用$inc操作符将一个字段的值增加或者减少

{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }

然后就是把漏洞信息以及IP地址、端口啥的都插进Result数据库里

主函数

接下来看main函数

if __name__ == '__main__':
init()
PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config()
thread.start_new_thread(monitor, ())
thread.start_new_thread(kp_check, ())
thread.start_new_thread(kp_update, ())
while True:
try:
task_id, task_plan, task_target, task_plugin = queue_get()
if task_id == '':
time.sleep(10)
continue
if PLUGIN_DB:
del sys.modules[PLUGIN_DB.keys()[0]] # 清理插件缓存
PLUGIN_DB.clear()
for task_netloc in task_target:
while True:
if int(thread._count()) < THREAD_COUNT:
if task_netloc[0] in WHITE_LIST:
break
try:
thread.start_new_thread(
vulscan, (task_id, task_netloc, task_plugin))
except Exception as e:
print e
break
else:
time.sleep(2)
if task_plan == 0:
na_task.update({"_id": task_id}, {"$set": {"status": 2}})
except Exception as e:
print e

构造函数

这里先看看init函数

def init():
time_ = datetime.datetime.now()
if na_plugin.find().count() >= 1:
return
install_kunpeng_plugin()

这里如果插件数大于一个的话,就直接return

Plugin写入Mongo

script_plugin = []
json_plugin = []
print 'init plugins'
file_list = os.listdir(sys.path[0] + '/vuldb')
for filename in file_list:
try:
if filename.split('.')[1] == 'py':
script_plugin.append(filename.split('.')[0])
if filename.split('.')[1] == 'json':
json_plugin.append(filename)
except:
pass

然后就是把vuldb下的文件前缀给提取出来,然后存到两个相应的列表里

for plugin_name in script_plugin:
try:
res_tmp = __import__(plugin_name)
plugin_info = res_tmp.get_plugin_info()
plugin_info['add_time'] = time_
plugin_info['filename'] = plugin_name
plugin_info['count'] = 0
na_plugin.insert(plugin_info)
except:
pass

然后对于py的文件,动态加载,然后获得插件信息,插入时间、插件文件名还有个数count,最后把这个插入到Plugin

同理对于json文件

for plugin_name in json_plugin:
try:
json_text = open(sys.path[0] + '/vuldb/' + plugin_name, 'r').read()
plugin_info = json.loads(json_text)
plugin_info['add_time'] = time_
plugin_info['filename'] = plugin_name
plugin_info['count'] = 0
del plugin_info['plugin']
na_plugin.insert(plugin_info)
except:
pass

原理一致,都是读取json文件然后把信息写入Plugin,不过插入之前把plugin这个给删掉了

然后就是安装kunpeng-plugin

def install_kunpeng_plugin():
time_ = datetime.datetime.now()
for plugin in kp.get_plugin_list():
level_list = ['紧急','高危','中危','低危','提示']
plugin_info = {
'_id': plugin['references']['kpid'],
'name': 'Kunpeng -' + plugin['name'],
'info': plugin['remarks'] + ' ' + plugin['references']['cve'],
'level': level_list[int(plugin['level'])],
'type': plugin['type'],
'author': plugin['author'],
'url': plugin['references']['url'],
'source': 1,
'keyword': '',
'add_time': time_,
'filename': plugin['references']['kpid'],
'count': 0
}
na_plugin.insert(plugin_info)

获得这些信息后插入到Plugin当中

然后获取配置信息,包括PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST

启动了三个线程

thread.start_new_thread(monitor, ())
thread.start_new_thread(kp_check, ())
thread.start_new_thread(kp_update, ())

这里看看三个函数的作用

心跳检测

def monitor():
global PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST
while True:
queue_count = na_task.find({"status": 0, "plan": 0}).count()
if queue_count:
load = 1
else:
ac_count = thread._count()
load = float(ac_count - 6) / THREAD_COUNT
if load > 1:
load = 1
if load < 0:
load = 0
na_heart.update({"name": "load"}, {
"$set": {"value": load, "up_time": datetime.datetime.now()}})
PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST = get_config()
if load > 0:
time.sleep(8)
else:
time.sleep(60)

这里是心跳检测的,大概是如果判断有执行的任务的话,就设置load为1,否则就为0

然后对Heart进行更新,然后重新获取PASSWORD_DIC, THREAD_COUNT, TIMEOUT, WHITE_LIST

如果有任务执行的话,就每隔8秒就检测一次心跳,否则的话就60秒检测一次心跳

kunpeng新版本检测

def kp_check():
while True:
try:
new_release = kp.check_version()
print new_release
if new_release:
info = new_release['body']
if '###' in new_release['body']:
info = new_release['body'].split('###')[1]
row = {
'info': info,
'isInstall': 0,
'name': new_release['name'],
'author': new_release['author']['login'],
'pushtime': new_release['published_at'],
'location': "",
'unicode': new_release['tag_name'],
'coverage': 0,
'source': 'kunpeng'
}
na_update.insert(row)
time.sleep(60 * 60 * 48)
except Exception as e:
print e
time.sleep(60 * 30)

body的值如下:

"body": "### 插件更新\r\n增加 Atlassian Confluence Widget Connector macro RCE漏洞检测插件 CVE-2019-3396 @ywolf \r\n增加 Microsoft Remote Desktop RCE漏洞检测插件  CVE-2019-0708 @Medicean \r\n\r\n### 代码更新\r\n修复 go并发调用时出现goroutine leak问题 @l3m0n \r\n修复 加载json插件内存未释放问题 @l3m0n \r\n\r\n### 其他\r\n无"

kunpeng升级(依赖于前端触发)

然后对kunpeng的安装状态进行删除,重新写入kunpeng的插件信息

def kp_update():
while True:
try:
row = na_update.find_one_and_delete(
{'source': 'kunpeng', 'isInstall': 1})
if row:
kp.update_version(row['unicode'])
na_plugin.delete_many({'_id':re.compile('^KP')})
install_kunpeng_plugin()
except Exception as e:
print e
time.sleep(10)

这一步需要配合view.py里面的对安装状态更改后,才能让isInstall变成1,这时候才能删除

任务队列获取

然后是获取任务队列信息

def queue_get():
global TASK_DATE_DIC
task_req = na_task.find_and_modify(query={"status": 0, "plan": 0}, update={
"$set": {"status": 1}}, sort={'time': 1})
if task_req:
TASK_DATE_DIC[str(task_req['_id'])] = datetime.datetime.now()
return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin']
else:
task_req_row = na_task.find({"plan": {"$ne": 0}})
if task_req_row:
for task_req in task_req_row:
if (datetime.datetime.now() - task_req['time']).days / int(task_req['plan']) >= int(task_req['status']):
if task_req['isupdate'] == 1:
task_req['target'] = update_target(
json.loads(task_req['query']))
na_task.update({"_id": task_req['_id']}, {
"$set": {"target": task_req['target']}})
na_task.update({"_id": task_req['_id']}, {
"$inc": {"status": 1}})
TASK_DATE_DIC[str(task_req['_id'])
] = datetime.datetime.now()
return task_req['_id'], task_req['plan'], task_req['target'], task_req['plugin']
return '', '', '', ''

大概逻辑是先查询是否有任务,有的话把状态status改成1,然后设置任务的时间为当前时间,返回查询到的编号、计划、目标以及使用的插件等等

要是没找到状态不是在计划当中的任务(plan!=0)

然后就会把查询到的写入到数据库当中(待补充)

漏洞检测

task_id, task_plan, task_target, task_plugin = queue_get()
if task_id == '':
time.sleep(10)
continue
if PLUGIN_DB:
del sys.modules[PLUGIN_DB.keys()[0]] # 清理插件缓存
PLUGIN_DB.clear()
for task_netloc in task_target:
while True:
if int(thread._count()) < THREAD_COUNT:
if task_netloc[0] in WHITE_LIST:
break
try:
thread.start_new_thread(
vulscan, (task_id, task_netloc, task_plugin))
except Exception as e:
print e
break
else:
time.sleep(2)
if task_plan == 0:
na_task.update({"_id": task_id}, {"$set": {"status": 2}})
except Exception as e:
print e

如果说这时候没有任务的话,就会sleep(10)

然后下面两句删除了插件的缓存,因为Vulscan中一个插件是被多个目标调用,所以PLUGIN_DB.keys()是唯一的,然后清空PLUGIN_DB字典的值(为了加载最新的插件)

然后针对没一个目标for循环读取,当当前线程数小于设置的线程总数时候,才会添加线程,并且对于白名单的目标是不会添加线程的

然后对于每一个目标都实例化一个vulscan类,并启动新的线程进行扫描

下面的if判断用来设置扫描的优先级(这里得配合前端看看)