Introduction to pyTenable

avatar guy
By Steve McGrath, Tenable Principal Solutions Architect

Introduction

All the capabilities of Tenable Vulnerability Management are available in the Vulnerability Management API, a robust platform for users of all experience levels. Using the Vulnerability Management API, you can seamlessly integrate Tenable Vulnerability Management into your cybersecurity infrastructure; for example, you can:

  • Automate asset data import into Vulnerability Management.
  • Import third-party scan data.
  • Export scan results from Vulnerability Management into a workflow management system for remediation.

You can automate almost any task within the Tenable's cloud platform via the API. For some, this thought can seem daunting; however, Tenable has made the learning curve as low as possible with an API Python library called pyTenable.

The pyTenable library gives users a low-level interface into the API, and it uses pythonic nomenclature to make interacting with the API simple, empowering, and ultimately pain-free. This article provides an introduction to pyTenable to help you develop your Vulnerability Management integrations as quickly and as easily as possible.

Prerequisites

Before using pyTenable, you need the following prerequisites:

  • Python 3.6 or later—pyTenable requires Python 3.6 or later.
  • API Keys—You need an account in Vulnerability Management with a set of generated API keys. To generate API keys for use with the Vulnerability Management API, see Generate API Keys in the Tenable Vulnerability Management User Guide. For information on how to use the API keys, see Authorization in the Tenable Developer Portal. It's worth noting that to use many endpoints, you may need elevated permissions or an administrator account to perform the action.

Environment Configuration

This article assumes that you already have a working Python 3.6 installation or later. I could write a whole guide on the different ways in which you could configure your environment, but for the sake of brevity, you can refer to My Development Setup on my developer blog if you're curious about my personal development environment.

  1. Install the pyTenable package

    Installing pyTenable is generally a simple one-line command using pip (Python Package Index). Pip can pull all of the required dependencies needed for the pyTenable library, and it's the recommended approach for installation. To install pyTenable via pip, simply execute the following command:
pip install pytenable
  1. (OPTIONAL) Set your API keys as environment variables

    pyTenable supports the use of environment variables for retrieving your API keys. Using environment variables can be quite useful when prototyping or writing a quick script. To set your API keys as environment variables, simply execute the following commands:
export TIO_ACCESS_KEY="YOUR_API_ACCESS_KEY_HERE"
export TIO_SECRET_KEY="YOUR_API_SECRET_KEY_HERE"

Once you have completed these steps, you are done with the environment configuration and ready to dive in!

Diving In

Instantiate an Object

The pyTenable library uses the concept of a connection class to act as the primary interface to the Vulnerability Management API. Connection classes are commonly used in many libraries, and allows for developers to have multiple connection objects within the code. This is useful if you need to use different users or interface into different instances. In practice, the pyTenable library is easy to use, simply instantiate an object and you have everything you need within that object.

For example, to instantiate an object named tio:

from tenable.io import TenableIO

tio = TenableIO(
    access_key='ACCESS_KEY_GOES_HERE',
    secret_key='SECRET_KEY_GOES_HERE'
)

If you have configured your API keys as environment variables, the instantiation is even easier:

from tenable.io import TenableIO

tio = TenableIO()

In either case, you now have an object named tio that you can interact with.

Identify Your Integration

When you develop an integration for Vulnerability Management, Tenable recommends that you identify yourself to the API. Identification allows Tenable to identify your integrations and API calls and it assists with debugging and troubleshooting if you have issues with the API, rate limits, or concurrency limits. Additionally, this is generally a requirement for partner integrations.

In the pyTenable library, adding identification is easy. You just pass a couple of additional parameters when instantiating an object, for example:

from tenable.io import TenableIO

tio = TenableIO(
    vendor='Widgets Inc.',
    product='Widget Maker 5000',
    build='0.0.1'
)

With these parameters, the pyTenable library now has the information needed to construct a User-Agent header string with the appropriate information. For more information about using User-Agent headers with the Vulnerability Management API, see User-Agent Header.

Make Your First API Call

Now that you have the tio object instantiated you can make a simple call to the Vulnerability Management API. For this first call, let's get a list of configured scans from the Vulnerability Management platform:

scans = tio.scans.list()
for scan in scans:
    print(f'Scan {scan["id"]} is named {scan["name"]}')

You should see a list of every scan that you have access to along with their ID and name.

Creating and Launching Scans

One of the basic functions of Tenable's platform is data acquisition through scanning. Scanning can be simple or complicated depending on your environment and your specific need. The pyTenable library attempts to make scanning as simple as possible. For this introduction, we'll focus on pre-defined scan policy templates and save more complex scanning for another article.

External Scanning

External scanning is the simplest type, since you don't need to deploy a scanner within your environment to scan the perimeter. Conveniently, the pyTenable library makes pre-defined assumptions in order to reduce the amount of parameters you to pass.

For example, to run a Basic Network Scan against a host (called external-target.company.tld in the following examples), you only need a few lines:

scan = tio.scans.create(
    name='External Example Scan',
    targets=['external-target.company.tld']
)
tio.scans.launch(scan['id'])

We only had to specify the name of the scan and the list of targets to scan. If you want to do something a little more complex, such as a PCI-ASV scan, you just need to specify the scan template name:

scan = tio.scans.create(
    name='External Example ASV Scan',
    template='asv',
    targets=['external-target.company.tld']
)
tio.scans.launch(scan['id'])

If you happen to know the UUID of the scan template, you can pass the UUID instead since the library is intelligent enough to know what is being passed:

scan = tio.scans.create(
    name='External Example Advanced Scan',
    template='11cb4669-5aff-47d2-8f00-3ef60a669325d24bd260ef5f9e66',
    targets=['external-target.company.tld']
)
tio.scans.launch(scan['id'])

To get a list of policy template names and UUIDs:

tio.policies.templates()

Internal Scanning

Internal scanning is only marginally more difficult than external scanning. When you run an internal scan, you are required to define a scanner or scanner group as part of the scan definition. You can define the scanner or scanner group by either the name or UUID, just like with the scan policy templates in the external scanning examples.

To create an external scan using the name of the scanner:

internal_scan = tio.scans.create(
    name='Example Internal Scan',
    scanner='io-scanner',
    targets=['192.0.2.0/24']
)

To create an external scan using the UUID of the scanner:

internal_scan = tio.scans.create(
    name='Example Internal Scan',
    scanner='ac096e47-8505-4ae7-a479-9ad068a19241',
    targets=['192.0.2.0/24']
)

Just like before, you can get the list of scanners and scanner groups through the library:

tio.scanners.allowed_scanners()

Tracking the Status of a Scan

Tenable's platform is generally asynchronous in practice, giving you the option to track the state of stateful actions, such as scans, however you see fit. When you launch a scan via the Vulnerability Management API or pyTenable library, it's never a blocking call, and a response is returned immediately. There are a variety of endpoints to track the state of a given job, for example, to see if a scan has been completed. In the example of scans, pyTenable has exposed this capability with the status method. This method can be used like so:

import time
status = 'pending'
while status[-2:] != 'ed':
    time.sleep(60)
    status = tio.scans.status(scan['id'])

While the example may seem odd, if you look at the Scan Status documentation, all of the end-state statuses are past-tense ("ed"), whereas the intermediary statuses are present tense ("ing"). This means you can make the while loop's condition to continue a check on the last two characters of the status.

What's Next?

As you can see, the capabilities of the Vulnerability Management API, when coupled with the pyTenable library as an interface layer, is quite easy to work with using a minimal amount of code. This article is just the first in a series of articles to explore some common Vulnerability Management API use cases. If you would like to see more, look for the next expert article in this series, where we will discuss exporting data from Tenable Vulnerability Management.

For More Information