How To Use Tenable.SC Python Script To Build Custom Reports

Why Should We Build A Script To Create Custom Reports?

If you have ever used Tenable.SC you will know how frustrating it can be to build a report. You are tied down to the limited filters and settings provided by Tenable. With using the API and Python, the options are nearly limitless.

In this video we will cover an example of a script I had developed previously in order to build out a custom report for the Tenable administrators on the team. The problem we were having was in regards to many authentication failures going unnoticed. We needed one report that would give a clear picture as to what hosts are failing and for what Operating System. Allowing us to regularly and quickly identify any problems and giving us the data we need to work with the appropriate teams to remediate.

How Does This Python Script Work?

You can find this script on our GitHub page here!

Lets do a quick overview of how this script will work, starting with the required libraries.

Python Libraries

In order to interact with the API we will need the requests library imported. The concurrent.futures is not necessarily required but will speed up the processing time when performing the API request. We will also need the datetime which will help filter out any old findings. Once we have all of the data we will need to do some parsing/manipulation. To do so we will use json and re. Lastly is pandas. Pandas is a great library you must get familiar with in order to export to csv. requests.packages.urllib3.disable_warnings() is not required but will remove any warnings that pop up in the script related to certificates.

import requests
import concurrent.futures
from datetime import datetime,timedelta
import json
import re
import pandas as pd
requests.packages.urllib3.disable_warnings()

Pull Data From Tenable API

We can spend all day repeating everything we covered in the video but instead I just want this discussion to be a brief overview. In this next session I will cover a few of the lines but for further clarification be sure to check out the video.

We start by defining a function get_failed_ip and pass through repoID. We will then use the analysis api endpoint, url = rf”https://YOUR_IPADDRESS/rest/analysis”. In order to successfully authenticate with the API you will need to pass through the accesskey and secret key in the headers.

In order to interact with the API we will need to use json format as discussed in the API reference from Tenable. For those that are familiar with Tenable some keywords will stand out here. Mainly the sourceType, tool type and filter name. Similar to what is done using the Tenable UI when building a report. We build out several filters in order to get as close to the data as possible. In this case we filter for 19506 to get us the plugin ‘Nessus Scan Information’. This plugin later will be used to determine if the authetnication was success or failure.

def get_failed_ip(repoID):
    url = rf"https://YOUR_IPADDRESS/rest/analysis"
    # Authentication
    headers = { "x-apikey" : 'accesskey=YOUR_ACCESSKEY; secretkey=YOUR_SECRETKEY'}
    # Data
    data = {
        'type' : 'vuln',
        "query": {
    "name": "",
    "description": "",
    "context": "",
    "createdTime": 0,
    "modifiedTime": 0,
    "groups": [],
    "type": "vuln",
    "tool": "vulnipdetail",
    "sourceType": "cumulative",
    "startOffset": 0,
    "endOffset": 100000,
    "filters": [
    {
    "filterName": "pluginID",
    "operator": "=",
    "value": "19506"

We then set a few more filters. The biggest takeaway here for this particular script would be the pluginText filter. This value Credentialed checks : no will help us filter out any findings from plugin id 19506 that are a fail.

   "filterName": "repositoryIDs",
    "operator": "=",
    "value": repoID
    },
    {
    "filterName": "pluginText",
    "operator": "=",
    "value": "Credentialed checks : no"
    },
    {
    "filterName": "lastSeen",
    "operator": "=",
    "value": "0:30"
    },
    ]
    },
    "sourceType": "cumulative",
    "sortField": "severity",
    "sortDir": "desc",

Using Requests Library To Perform A Post Request Tenable API

In order for us to actually pull out the data we need to use the requests library. Inside of the requests library we will need to perform a Post request. This post request along with the headers and json data we just put together will pull in the filtered data from Tenable API. Once we have the data from res.json() we will then start manipulating the data to get what we need out of it. Starting with the osType. Depending on the repository id that is pulled in will determine what OS it is. This is an example repository id and will be different for each environment.

# Get request
    res = requests.post(url, headers=headers, verify=False, data=json.dumps(data))
    # Convert request to json
    data = res.json()
    #print(len(data['response']['results']))
    if len(data['response']['results'])> 0 :
       
        c = 0
        osType = ""
        if repoID == "59":
            osType = "Windows 2019"
        if repoID == "47":
            osType = "Windows 2016"

Next we will want to create a list of all the ip addresses found in the nested json/list from API request. We will loop through the failedIpList and grab the individual pieces such as ip, dns and netbios. This wil all then get appended to the filteredFailedIpList including the osType defined in the last step.

failedIpList = data['response']['results'][0]['hosts'][0]['iplist']
        filteredFailedIpList = []
        for i in failedIpList:
            ip = failedIpList[c]['ip']
            dns = failedIpList[c]['dnsName']
            netbios = failedIpList[c]['netbiosName']
            filteredFailedIpList.append([ip,dns,netbios,osType])
            #print(filteredFailedIpList)
            c+=1
        return filteredFailedIpList

To close it out we will define the repository id’s. This is what can be used to determine the osType. We will then combine the windowsList and rhelList in order to loop through and run the get_failed_ip function. Lastly we will use pandas in order to export this data to output.xlsx.

Conclusion

In this instructional post we covered a Python script that interacts with the Tenable.SC API in order to grab data to build a custom report. This instructional post is limited in its explanation and I recommend those who are interested in this topic to check out the video. I hope you were able to take something away. Learning to use Python and work with the API can save you and the team a lot of time.

As Always, Never Stop Learning.

CyberMe



Leave a Comment

Your email address will not be published. Required fields are marked *