前言
之前的文章中我们完成了登录并发起了一次扫描,接下来我们将继续跟进,监听扫描进度并获取扫描结果。
一、监听扫描状态
扫描开始后,我们需要监听扫描任务来确定任务是否成功发起、结束或意外终止。
这里使用的API为:
GET /scans/{scan_id}
除了路径参数scan_id之外,它还可以接受一个查询字符串history_id这里我们用不着所以就不深入了,有兴趣的同学可以参看:
https://localhost:8834/api#/resources/scans/details
我们来看一下这个API的返回值,这里我省略了很多字段,大家可以在官方文档里看到完整的版本。目前我们感兴趣的信息只有respone[‘info’][‘status’],即本次扫描的状态。
{
"info": {
"status": {string},
...
"folder_id": {integer},
"targets": {string},
...
"scanner_name": {string},
...
},
"hosts": [...],
...
"vulnerabilities": [...],
}
那么监听任务状态也就很简单了,只要我们每隔一段时间发送请求查询任务状态即可,代码如下:
import time
# 监听指定任务状态
def listen_scan(scan_id):
# 调用/scans/{scan_id}/launch
url = 'https://localhost:8834/scans/{scan_id}'.format(scan_id=scan_id)
while True:
# 每隔60秒发送一次请求
time.sleep(60)
# 发送请求
respon = requests.get(url, headers=header, verify=False)
# 是否请求成功
if respon.status_code == 200:
status = json.loads(respon.text)['info']['status']
print status
if status == 'running':
continue
elif status == 'completed':
return True
else:
break
return False
二、获取扫描结果
当我们监听到任务结束,接下来要做的就是处理本次扫描的结果了,从之前贴出来的返回值中我们可以看到,/scans/{scan_id}接口除了返回扫描任务的状态之外,也返回了一个漏洞列表response[‘vulnerabilities’]和一个主机列表response[‘hosts],漏扫结果的获取就和这里面的信息有关。
1、漏洞总览
首先我们来看一下vulnerabilities’字段中都返回了什么东西。
"vulnerabilities":[
{
"plugin_id": {integer},
"plugin_name": {string},
"plugin_family": {string},
"count": {integer},
"vuln_index": {integer},
"severity_index": {integer}
}
]
可以看到返回的是一个字典列表,包含的信息是本次漏扫的一个总览,内容为漏洞数目的统计和漏洞的基本信息,其中包括了一个plugin_id,可以用来获取漏洞的详情。
2、漏洞详情
从扫描的详情中我们只能看到本次漏洞的概览,如果想要进一步了解漏洞的详情(注意是漏洞本身的详情,与本次扫描无关)。
漏洞信息详情查询的接口为:
GET /plugins/plugin/{id}
文档位置如下:
https://localhost:8834/api#/resources/plugins/plugin-details
照惯例我们来看一下这个接口的返回值。
{
"id": {integer},
"name": {string},
"family_name": {string},
"attributes": [
{
"attribute_name": {string},
"attribute_value": {string}
}
]
}
乍一看它的返回值似乎有些过于简单了,不过其中有个非常有趣的字典列表attributes,它可以用来表示漏洞各类信息,包括漏洞的CVE编号,危险等级,描述,详情,解决方案等,下面的实例代码可能有助于我们更好的理解它的作用。
# 根据id获取漏洞详情
def get_vul_detail(plugin_id):
vul_detail = {
# CVE编号
'cve_number': '',
# 漏洞名称
'vul_name': '',
# 漏洞描述
'vul_intro': '',
# 漏洞详情
'vul_detail': '',
# 漏洞等级
'vul_level': 0,
# 解决方案
'solution': '',
# 漏洞发布时间
'release_time': '',
# 漏洞发现时间
'discover_time': ''
}
url = 'https://localhost:8834/plugins/plugin/{plugin_id}'.format(plugin_id=plugin_id)
respone = requests.get(url, headers=header, verify=False)
if respone is not None:
result = json.loads(respone.text)
# 漏洞名称
vul_detail['vul_name'] = result['name']
# 遍历attributes生成结果
for attr in result['attributes']:
attr_name = attr['attribute_name']
# cve编号
if attr_name == 'cve':
vul_detail['cve_number'] = attr['attribute_value']
continue
# 漏洞描述
elif attr_name == 'synopsis':
vul_detail['vul_intro'] = attr['attribute_value']
continue
# 漏洞详情
elif attr_name == 'description':
vul_detail['vul_detail'] = attr['attribute_value']
continue
# 漏洞等级
elif attr_name == 'risk_factor':
vul_detail['risk_factor'] = attr['attribute_value']
continue
# 漏洞描述
elif attr_name == 'solution':
vul_detail['solution'] = attr['attribute_value']
continue
# 漏洞发布时间
elif attr_name == 'plugin_publication_date':
vul_detail['release_time'] = attr['attribute_value']
continue
# 漏洞发现时间
elif attr_name == 'vuln_publication_date':
vul_detail['discover_time'] = attr['attribute_value']
continue
return vul_detail
return None
这里我采取方法的是遍历attributes,然后根据attribute_name来确定是否是我需要的信息,并将相应的attribute_value取出放入我自己的输出字典中。
当然你也可以利用下面这种方式将所有的值全部取出来。
for attr in result['attributes']:
vul_detail[attr['attribute_name']] = attr['attribute_value']
3、基于主机的漏洞列表
漏洞详情仅是对于漏洞的描述,接下来我们来看一下我们最感兴趣的主机漏洞详情。先看一下scan接口返回的hosts列表里都有什么。
{
"host_id": {integer},
"host_index": {string},
"hostname": {integer},
"progress": {string},
"critical": {integer},
"high": {integer},
"medium": {integer},
"low": {integer},
"info": {integer},
"totalchecksconsidered": {integer},
"numchecksconsidered": {integer},
"scanprogresstotal": {integer},
"scanprogresscurrent": {integer},
"score": {integer}
}
其中host_id不用说,接下来我们获取主机漏洞详情的时候肯定用得着,另外还有几个比较有意思的字段,比如high, medium, low, info,顾名思义是对各等级风险的统计。
可以通过下面的接口获取主机漏洞的详情。
GET /scans/{scan_id}/hosts/{host_id}
文档位置为:
https://localhost:8834/api#/resources/scans/host-details
我们先来看一下它的返回值。
{
"info": {
"host_start": {string},
"mac-address": {string},
"host-fqdn": {string},
"host_end": {string},
"operating-system": {string},
"host-ip": {string}
},
"compliance": [
host_compliance Resource
],
"vulnerabilities": [
host_vulnerability Resource
]
}
其中info是主机的基本信息,compliance是主机合规性的信息,这里我们不用管,最后vulnerabilities就是我们关注的漏洞信息,它以字典列表的形式存放,我们来看一下里面的具体内容。
"vulnerabilities": [
{
"host_id": {integer},
"hostname": {string},
"plugin_id": {integer},
"plugin_name": {string},
"plugin_family": {string},
"count": {integer},
"vuln_index": {integer},
"severity_index": {integer},
"severity": {integer}
}
]
其中包括了漏洞的名称(plugin_name),漏洞ID(plugin_id),漏洞等级(severity,0: info, 1: low, 2: medium, 3: high, 4: critical)等信息,我们可以按需来读取。
例子,返回一个主机-漏洞列表:
# 获取扫描结果(基于主机)
def get_scan_detail_baseon_host(scan_id):
# 结果,主机漏洞字典列表
result = []
# 主机漏洞字典
host_detail = {}
# 调用
url = 'https://localhost:8834/scans/{scan_id}'.format(scan_id=scan_id)
# 发送请求
respon = requests.get(url, headers=header, verify=False)
# 取出主机列表
host_list = json.loads(respon.text)['hosts']
# 遍历主机列表并生成结果
for host in host_list:
# IP为键名
host_detail[host['hostname']] = get_vul_by_host(scan_id, host['host_id'])
result.append(host_detail)
return result
# 获得主机漏洞信息
def get_vul_by_host(scan_id, host_id):
# 调用
url = 'https://localhost:8834/scans/{scan_id}/hosts/{host_id}'.\
format(scan_id=scan_id, host_id=host_id)
# 发送请求
respon = requests.get(url, headers=header, verify=False)
return json.loads(respon.text)['vulnerabilities']
日常使用中,基于主机是一个常用的维度,上面的代码,实现了一次简单的基于主机的漏洞结果整理,并将结果以字典列表的形式输出,代码比较简陋,希望对大家有所启发。