这篇文章上次修改于 1484 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

自学了一段时间Golang,写了一些练习demo, 想想还是写点小东西出来总和练习下比较好, 通过本次练习能够串联起学习的知识点.

Nessus已经提供了API接口,无论是接入其他系统还是自己自动化扫描都特别方便. 官方提供了Python和Java的SDK方便用户调用,但没有提供Golang的SDK, 所以用Golang写一个简易版的吧.

环境

Go: 1.15.5

Nessus: Nessus Professional Version 8.13.1

Nessus也已经提供了完备的API文档,查阅地址是https://<Nessus_IP>:8834/api#/overview

认证

Nessus有两种认证方式,第一种是常规的登录获取token,API如下:

POST /session

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

输入正确用户名和密码后会响应一个token

{
    "token": {string}
}

伪代码实现如下:

package nessus_request

import (
    "crypto/tls"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
    "time"
)

func GetToken(username, password string) (token string) {
    var url string = "https://10.211.55.54:8834/session"
    timeout := time.Duration(10 * time.Second) //超时时间50ms
    bodyString := "{\"username\":" + username + "+,\"password\":" + password + "}"

    req, err := http.NewRequest("POST", url, strings.NewReader(bodyString))
    if err != nil {
        panic(err)
    }

    if req != nil {
        req.Header.Add("Content-Type", "application/json")
    // 使用AK进行登录,添加下面一行即可
    // req.Header.Add("X-ApiKeys", "accessKey={accessKey}; secretKey={secretKey};")
    }

    client := &http.Client{
        Timeout: timeout,
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 忽略证书检测
        }}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)

    var tokens struct {
        Token string `json:"token"`
    }
    json.Unmarshal(body, &tokens)
    fmt.Println(tokens.Token)
    return tokens.Token
}

TIPS: 我们需要为响应创建与之匹配的结构体, 可以使用在线网站自动生成:https://mholt.github.io/json-to-go/

第二种使用AK方式登录,即可直接进行操作,只要在每次请求时在HTTP请求头中添加"X-ApiKeys", "accessKey={accessKey}; secretKey={secretKey};”即可.示例代码参考上面示例即可.

任务

添加任务

添加任务的数据格式如下,其中必填的字段有:uuid,settings.name,settings.enabled,settings.text_targets,settings.agent_group_id

POST /scans
{
    "uuid": {template_uuid}, // 模板UUID
    "settings": {
        "name": {string},  // 扫描任务的名称
        "description": {string},
        "emails": {string},
        "enabled": "true",
        "launch": {string},  // 是否马上运行,建议设置为"true"
        "folder_id": {integer}, // folder_id 需要抓包看下
        "policy_id": {integer}, //策略可以不填
        "scanner_id": {integer}, // 为1
        "text_targets": {string}, //扫描的目标
        "agent_group_id": []
    }
}

uuid是扫描模板的id为必填项.

这里值得注意的是launch参数我们最好填上并设置为true,这样添加完任务后能够自动扫描.

代码片段:

// NewScan 新建扫描
func (n *nessusImpl) NewScan(
    TmplUUID string,
    Name string,
    TextTargets string,
) (*Scan, error) {
    data := NewScanRequest{
        UUID: TmplUUID,
        Settings: Settings{
            Name:        Name,
            Description: "",
            LaunchNow:   true,
            FolderID:    3, // 请自行替换为自己的folder_id
            Enabled:     false,
            ScannerID:   "1",
            TextTargets: TextTargets,
        },
    }
    return n.CreateScan(data)
}

func (n *nessusImpl) CreateScan(newScanRequest NewScanRequest) (*Scan, error) {
    if n.verbose {
        log.Println("Creating a new scan...")
    }

    resp, err := n.Request("POST", "/scans", newScanRequest, []int{http.StatusOK})
    if err != nil {
        return nil, err
    }

    defer resp.Body.Close()
    reply := struct {
        Scan Scan `json:"scan"`
    }{}

    if err = json.NewDecoder(resp.Body).Decode(&reply); err != nil {
        return nil, err
    }
    return &reply.Scan, nil
}

暂停任务数据格式

POST /scans/{scan_id}/pause

停止任务数据格式

POST /scans/{scan_id}/stop

恢复任务数据格式

POST /scans/{scan_id}/resume

添加任务,暂停任务,恢复任务均为POST请求方式,body字段为空

获取任务扫描结果

GET /scans/{scan_id}
# 响应如下
{
    "info": {
        "acls": [
            permission Resource
        ],
        "edit_allowed": {boolean},
        "status": {string},
        "policy": {string},
        "pci-can-upload": {boolean},
        "hasaudittrail": {boolean},
        "scan_start": {string},
        "folder_id": {integer},
        "targets": {string},
        "timestamp": {integer},
        "object_id": {integer},
        "scanner_name": {string},
        "haskb": {boolean},
        "uuid": {string},
        "hostcount": {integer},
        "scan_end": {string},
        "name": {string},
        "user_permissions": {integer},
        "control": {boolean}
    },
    "hosts": [
        host Resource
    ],
    "comphosts": [
        host Resource
    ],
    "notes": [
        note Resource
    ],
    "remediations": {
        "remediations": [
            remediation Resource
        ],
        "num_hosts": {integer},
        "num_cves": {integer},
        "num_impacted_hosts": {integer},
        "num_remediated_cves": {integer}
    },
    "vulnerabilities": [
        vulnerability Resource
    ],
    "compliance": [
        vulnerability Resource
    ],
    "history": [
        history Resource
    ],
    "filters": [
        filter Resource
    ]
}

go实例代码

func (n *nessusImpl) ScanDetails(scanID int64) (*ScanDetailsResp, error) {
    if n.verbose {
        log.Println("获取扫描明细...")
    }

    resp, err := n.Request("GET", fmt.Sprintf("/scans/%d", scanID), nil, []int{http.StatusOK})
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    reply := &ScanDetailsResp{}
    if err = json.NewDecoder(resp.Body).Decode(&reply); err != nil {
        return nil, err
    }
    return reply, nil
}

小结

这篇文章我们主要介绍了nessus API从登录到扫描任务创建、启动、停止、以及结果的获取的内容.当然nessus的api不止这些,目前还是个半成品,后面继续完善吧.

完整代码 https://github.com/ucstone/go-nessus/tree/master

参考项目

https://github.com/attwad/nessie