macai
macai

Reputation: 11

Downloading custom Nessus scan report using Nessus API

I have python code that successfully downloads a Nessus scan report in csv format, but I need to add some additional fields to the downloaded report. I include parameters in the request payload to include some fields, but the scan that is downloaded does not include those fields.

I've tried changing the value of the reportedContents params to actual Boolean types with the True keyword.

Also, I changed the format to pdf and it exports a PDF file that is just a title page and a page with a blank table of contents.

The downloaded csv file has data in it, but only includes the default headers (i.e.):

Plugin ID,CVE,CVSS v2.0 Base Score,Risk,Host,Protocol,Port,Name,Synopsis,Description,Solution,See Also,Plugin Output

The raw output of the POST request looks like:

POST https://localhost:8834/scans/<scan_id>/export  
X-ApiKeys: accessKey=accessKey;secretKey=secretKey  
Content-Type: application/x-www-form-urlencoded  
Content-Length: 122   

format=csv&reportContents.vulnerabilitySections.exploitable_with=true&reportContents.vulnerabilitySections.references=true
def download_scan(scan_num):

    # Post an export request
    headers = {
        'X-ApiKeys': 'accessKey=accessKey;secretKey=secretKey',
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    
    data = {
        'format': 'csv',
        'reportContents.vulnerabilitySections.exploitable_with': 'true',
        'reportContents.vulnerabilitySections.references': 'true'
    }       

    res = requests.post(url + '/scans/{id_num}/export'.format(id_num = scan_num), data=data, verify=False, headers=headers)

    if res.status_code == 200:
        export = json.loads(res.text)
        file_id = export.get('file')
    
    # Continually check the scan status until the status is ready
    while True:
        # Check file status
        res = requests.get(url + '/scans/{id_num}/export/{file_num}/status'.format(id_num = scan_num, file_num = file_id), verify=False, headers=headers)
        
        if res.status_code == 200:
            status = json.loads(res.text)['status'] 
            if status == 'ready':
                break

    # Download the scan
    res = requests.get(url + '/scans/{scan_num}/export/{file_num}/download'.format(scan_num = scan_num, file_num = file_id), verify=False, headers=headers)

    # If the scan is successfully downloaded, get the attachment file
    if res.status_code == 200:
        attachment = res.content
        print("Scan downloaded!!!")
    else:
        raise Exception("Download request failed with status code: " + str(res))
    
    return attachment

def main():

    # Download the scan based on the scan_id. I have a helper function that returns the id that I am omitting here
    try:
        scan = download_scan(scan_id)   
    except Exception as e:
        print(e)
        quit()
    
    with open("scan.csv", "wb") as f:
        f.write(scan)
    f.close()

if __name__ == "__main__":
    main()

Upvotes: 1

Views: 2620

Answers (2)

Stepan Filonov
Stepan Filonov

Reputation: 324

I managed to fix it, my problem was that I was using Python's requests module and it's data={} keyword, which defaults to header content-type: application-x-www-form-urlencoded, it generates reports with strictly 13 fields regardless of your payload.

To make it actually consider your payload, use the header "content-type": "application/json", in your code implicitly and json={} in your payload instead of data={}.

WILL NOT WORK:

requests.post(
    nessus_url + f"/scans/{scan_id}/export",
    data={
        "format": "csv",
        "template_id": "",
        "reportContents": {
            "csvColumns": {
                "id": True,
                "cve": True,
                "cvss": True,
                **other_columns,
            }
        }
    },
    verify=False,
    headers={
    "X-ApiKeys": f"accessKey={credentials['access_key']}; secretKey={credentials['secret_key']}",
    },
)

WILL WORK:

requests.post(
    nessus_url + f"/scans/{scan_id}/export",
    json={
        "format": "csv",
        "template_id": "",
        "reportContents": {
            "csvColumns": {
                "id": True,
                "cve": True,
                "cvss": True,
                **other_columns
            }
        }
    },
    verify=False,
    headers={
        "X-ApiKeys": f"accessKey={credentials['access_key']}; secretKey={credentials['secret_key']}",
        "content-type": "application/json",
    },
)

Upvotes: 0

Bailz
Bailz

Reputation: 615

I'm having the exact same issue but with PowerShell. Neither my additional columns nor filters appear to be working. Was wondering if you'd had any joy getting this to work?

If I change the scan_id I get the correct different results, which suggests it is receiving the JSON but ignoring the columns and filters.

My JSON is as follows...

{
"scan_id":  3416,
"format":  "csv",
"reportContents.vulnerabilitySections.cvss3_base_score":  true,
"filters":  {
                "filter.0.quality":  "gt",
                "filter.0.filter":  "cvss2_base_score",
                "filter.0.value":  "6.9",
                "filter.1.quality":  "neq",
                "filter.1.filter":  "cvss2_base_score",
                "filter.1.value":  ""
            }
 }

Upvotes: 0

Related Questions