使用Python调用Nessus 接口实现自动化扫描 转自 https://www.jianshu.com/p/a4236809e11a

使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a

之前在项目中需要接入nessus扫描器,研究了一下nessus的api,现在将自己的成果分享出来。
Nessus提供了丰富的二次开发接口,无论是接入其他系统还是自己实现自动化扫描,都十分方便。
同时Nessus也提供了完备的API文档,可以在 Settings->My Account->API Keys->API documentation

 

认证

nessus提供两种认证方式,第一种采用常规的登录后获取token的方式,在https://localhost:8834/api#/resources/session条目中可以找到这种方式,它的接口定义如下:

POST /session

{
    "username":{string},
    "password":{string}
}

输入正确的用户名和密码,登录成功后会返回一个token
···
{
"token": {string}
}
···

在后续请求中,将token放入请求头信息中请求头的key为X-Cookie,值为 token=xxxx,例如 :X-Cookie: token=5fa3d3fd97edcf40a41bb4dbdfd0b470ba45dde04ebc37f8;,下面是获取任务列表的例子

import requests
import json
def get_token(ip, port, username, password):
    url = "https://{0}:{1}/session".format(ip, port)
    post_data = {
        'username': username,
        'password': password
    }
    
    respon = requests.post(url, data=post_data, verify=False)
    if response.status_code == 200:
        data = json.loads(response.text)
        return data["token"]

def get_scan_list()
    # 这里ip和port可以从配置文件中读取或者从数据库读取,这里我省略了获取这些配置值得操作
    url = "https://{ip}:{port}/scans".format(ip, port)
    token = get_token(ip, port, username, password)
    if token:
        header = {
            "X-Cookie":"token={0}".format(token),
            "Content-Type":"application/json"
        }
        response = requests.get(url, headers=header, verify=False)
        if response.status_code == 200:
            result = json.loads(respon.text)
            return result

第二种方式是使用Nessus生成的API Key,这里我们可以依次点击 Settings->My Account->API Keys-->Generate按钮,生成一个key,后续使用时填入头信息中,还是以获取扫描任务列表作为例子

def get_scan_list()
    accessKey = "XXXXXX" #此处填入真实的内容
    secretKey = "XXXXXX" #此处填入真实内容
    
    url = "https://{ip}:{port}/scans".format(ip, port)
    token = get_token(ip, port, username, password)
    if token:
        header = {
            'X-ApiKeys': 'accessKey={accesskey};secretKey={secretkey}'.format(accesskey=accessKey, secretkey=secretKey)
            "Content-Type":"application/json"
        }
        response = requests.get(url, headers=header, verify=False)
        if response.status_code == 200:
            result = json.loads(respon.text)
            return result

对比来看使用第二种明显方便一些,因此后续例子都采用第二种方式来呈现

策略模板配置

策略模板的接口文档在 https://localhost:8834/api#/resources/policies 中。

创建策略模板

创建策略模板使用的是 策略模板的create接口,它里面有一个必须填写的参数 uuid 这个参数是一个uuid值,表示以哪种现有模板进行创建。在创建之前需要先获取系统中可用的模板。获取的接口是 /editor/{type}/templates,type 可以选填policy或者scan。这里我们填policy

一般我们都是使用模板中的 Advanced 来创建,如下图

 
使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a
在这里插入图片描述


下面是获取该模板uuid的方法,主要思路是获取系统中所有模板,然后根据模板名称返回对应的uuid值

def get_nessus_template_uuid(ip, port, template_name = "advanced"):
    header = {
        'X-ApiKeys': 'accessKey={accesskey};secretKey={secretkey}'.format(accesskey=accesskey,
                                                                          secretkey=secretkey),
        'Content-type': 'application/json',
        'Accept': 'text/plain'}

    api = "https://{ip}:{port}/editor/scan/templates".format(ip=ip, port=port)
    response = requests.get(api, headers=header, verify=False)
    templates = json.loads(response.text)['templates']

    for template in templates:
        if template['name'] == template_name:
            return template['uuid']
    return None

有了这个id之后,下面来创建策略模板,这个接口的参数较多,但是很多参数都是选填项。

这个部分文档写的很简陋,很多参数不知道是干嘛用的,当时我为了搞清楚每个参数的作用,一个个的尝试,然后去界面上看它的效果,最后终于把我感兴趣的给弄明白了。
它的主体部分如下:

{
   "uuid": {template_uuid},
   "audits": {
       "feed": {
           "add": [
               {
                   "id": {audit_id},
                   "variables": {
                       "1": {audit_variable_value},
                       "2": {audit_variable_value},
                       "3": {audit_variable_value}
                   }
               }
           ]
       }
   },
   "credentials": {
       "add": {
           {credential_category}: {
               {credential_name}: [
                   {
                       {credential_input_name}: {string}
                   }
               ]
           }
       }
   },
   "plugins": {
       {plugin_family_name}: {
           "status": {string},
           "individual": {
               {plugin_id}: {string}
           }
       }
   },
   "scap": {
       "add": {
           {scap_category}: [
               {
                   {scap_input_name}: {string}
               }
           ]
       }
   },
   "settings": {
       "acls": [
           permission Resource
       ],
       //其他的减值对,这里我将他们都省略了
}

他们与界面上配置的几个大项有对应关系,能对应的上的我给做了标记,但是有的部分对应不上。

 
使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a
在这里插入图片描述

settings 是给策略模板做基础配置的,包括配置扫描的端口范围,服务检测范围等等。
credentials 是配置登录扫描的,主要包括 windows、ssh、telnet等等
plugins 配置扫描使用的插件,例如服务扫描版本漏洞等等

在settings中,对应关系如下图所示

 
使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a
host scan
 
使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a
port scan
 
使用Python调用Nessus 接口实现自动化扫描
转自 https://www.jianshu.com/p/a4236809e11a
在这里插入图片描述

下面是创建扫描策略模板的实际例子:

def create_template(ip, port, **kwargs): # kwargs 作为可选参数,用来配置settings和其他项
    header = {
        "X-ApiKeys": "accessKey={accesskey};secretKey={secretkey}".format(accesskey=accesskey,
                                                                          secretkey=secretkey),
        "Content-Type": "application/json",
        "Accept": "text/plain"
    }
    policys = {}

    # 这里 grouppolicy_set 存储的是策略模板中各个脚本名称以及脚本是否启用的信息
    for policy in grouppolicy_set:
        enabled = "enabled" if policy.enable else "disabled"
        policys[policy.name] = {
            "status": enabled
        }
    
    # settings里面的各小项必须得带上,否则会创建不成功
    "settings": {
        "name": template.name,
        "watchguard_offline_configs": "",
        "unixfileanalysis_disable_xdev": "no",
        "unixfileanalysis_include_paths": "",
        "unixfileanalysis_exclude_paths": "",
        "unixfileanalysis_file_extensions": "",
        "unixfileanalysis_max_size": "",
        "unixfileanalysis_max_cumulative_size": "",
        "unixfileanalysis_max_depth": "",
        "unix_docker_scan_scope": "host",
        "sonicos_offline_configs": "",
        "netapp_offline_configs": "",
        "junos_offline_configs": "",
        "huawei_offline_configs": "",
        "procurve_offline_configs": "",
        "procurve_config_to_audit": "Saved/(show config)",
        "fortios_offline_configs": "",
        "fireeye_offline_configs": "",
        "extremeos_offline_configs": "",
        "dell_f10_offline_configs": "",
        "cisco_offline_configs": "",
        "cisco_config_to_audit": "Saved/(show config)",
        "checkpoint_gaia_offline_configs": "",
        "brocade_offline_configs": "",
        "bluecoat_proxysg_offline_configs": "",
        "arista_offline_configs": "",
        "alcatel_timos_offline_configs": "",
        "adtran_aos_offline_configs": "",
        "patch_audit_over_telnet": "no",
        "patch_audit_over_rsh": "no",
        "patch_audit_over_rexec": "no",
        "snmp_port": "161",
        "additional_snmp_port1": "161",
        "additional_snmp_port2": "161",
        "additional_snmp_port3": "161",
        "http_login_method": "POST",
        "http_reauth_delay": "",
        "http_login_max_redir": "0",
        "http_login_invert_auth_regex": "no",
        "http_login_auth_regex_on_headers": "no",
        "http_login_auth_regex_nocase": "no",
        "never_send_win_creds_in_the_clear": "yes" if kwargs["never_send_win_creds_in_the_clear"] else "no",
        "dont_use_ntlmv1": "yes" if kwargs["dont_use_ntlmv1"] else "no",
        "start_remote_registry": "yes" if kwargs["start_remote_registry"