Jugando con la API de Tenable.sc y Powershell

Aquí empiezo una nueva serie de entradas donde voy a mostrar como trabajar con la API de Tenable.sc usando PowerShell como lenguaje de scripting. Hay una librería muy buena de Python, pyTenable, que si puede usar Python en tu entorno te la recomiendo; pero si por alguna razón no puedes usar Python - ni confirmo ni desmiento que conozca a alguien que le pase eso 😉 - o simplemente quieres aprender una alternativa disponible nativamente en Windows, ¡este es tu blog!

Voy a empezar con lo básico y luego iremos profundizando desde ahí.

Login

Lo primero es lo primero, cómo hacer login o autenticarse para acceder a la API. Voy a enseñarte 2 opciones, una con nuestros viejos amigos usuario y contraseña, y otra con claves API - este es el método preferido para Tenable.

Usuario y Contraseña

Para esta opción voy a usar un archivo cifrado con clave AES donde guardaré la contraseña del usuario de la API para mi servidor Tenable.sc

Podemos usar un archivo cifrado sin clave AES, en ese caso se usan las credenciales del usuario en Windows. En ese caso, solo el mismo usuario y en el mismo equipo podrá descifrar el archivo. Si quieres automatizar todo el proceso de acceso a la API, este no es el camino.

Los siguientes pasos solo los hacemos una vez al principio, despues accedemos a los archivos desde nuestros scripts. Creamos la clave AES y la guardamos para descifrar posteriormente:

$AESkey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESkey)
$AESkey | Out-File 'C:\creds\aeskey.key'

En el siguiente paso se te pide una contraseña, que debe ser la del usuario que utilizaremos con la API. Se almacena en un archivo cifrado - encrypt.xml. Usa preferiblemente un usuario de solo lectura si no necesitas hacer cambios a través de la API.

Read-Host -AsSecureString | ConvertFrom-SecureString -key $AESkey | Out-File 'C:\creds\encrypt.xml'

Guarda la clave AES en un sitio seguro, porque la usaremos para descifrar la contraseña.

Construimos las credenciales de login y conseguimos el token

Preparamos las credenciales con los archivos anteriores y un objeto PSCredential.

$key = Get-Content 'C:\Users\myuser\Documents\04.Tenable.sc\scripts\creds\aeskey.key'
$username = "api_user"
$password = Get-Content 'C:\Users\myuser\Documents\04.Tenable.sc\scripts\creds\encrypt.xml' | ConvertTo-SecureString -Key $key
$MyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password

A continuación construimos el body de nuestra primera petición, que contiene nuestro usuario y contraseña; usamos un PSObject y lo convertimos a JSON.

$login = New-Object PSObject 
$login | Add-Member -MemberType NoteProperty -Name username -Value $MyCredential.UserName
$login | Add-Member -MemberType NoteProperty -Name password -Value $MyCredential.GetNetworkCredential().password
$Data = (ConvertTo-Json -Compress $login)

El último paso es conseguir el token de autenticación y la variable de sesión que luego reutilizaremos para autenticar nuestras peticiones posteriores.

$tenableSC = "https://192.168.1.111"
$request = Invoke-WebRequest -Uri $tenableSC/rest/token -Method Post -Body $Data -UseBasicParsing -SessionVariable sv
$token = (ConvertFrom-Json $request.Content).response.token
Token

Ya estamos listos para hace peticiones a la API con las variables $token and $sv y el commandlet Invoke-WebRequest.

$request = Invoke-WebRequest -Uri " $tenableSC/rest/API_endpoint " -Method Get -Headers @{"X-SecurityCenter"="$token"} -WebSession $sv

Claves API

La segunda opción para autenticación son las claves API, que es el método preferido por Tenable actualmente. Primero tenemos que generar las claves de acceso y secreto para nuestro usuario.

Hacemos login con un usuario Security Manager y vamos al menú Users, hacemos click en el icono de acción a la derecha del usuario que queremos usar con la API y hacemos click en Gnerate API keys.

Options

Obtenemos 2 claves, access y secret, las copiamos, ya que una vez cerrada la ventana no podemos acceder a ellas de nuevo en la interfaz gráfica.

Keys

Construimos el login con las claves API

Yo utilizo variables de entorno en Windows para evitar tener las claves en claro dentro de los scripts; luego las recupero como muestro abajo. También creamos las cabeceras para autenticación.

$accessKey = $env:TENABLE_ACCESS_KEY
$secretKey = $env:TENABLE_SECRET_KEY
$headers = @{}
$headers.Add("x-apikey", "accessKey=$accessKey;secretKey=$secretKey")

Ahora ya estamos listos para usar el commandlet Invoke-RestMethod y hacer peticiones a la API; por ejemplo, vamos a obtener todas las queries guardadas en nuestro servidor:

$queries = Invoke-RestMethod -Uri $scURL/rest/query -Method Get -Headers $headers

¡A jugar!

Con la autenticación hecha, podemos empezar a jugar con la API. Voy a usar un ejemplo práctico con assets. Digamos que tienes un montón de assets creados por ti, tanto estáticos como dinámicos y de tipo combinación. Los dinámicos se generan en tiempo real cada vez que se añaden datos nuevos a la base de datos de vulnerabilidades de los repositorios de dichos assets. Cuanto más usas Tenable.sc, más assets vas a crear que luego pueden quedar obsoletos o son reemplazados por otros nuevos, pero no puedes borrar los antiguos porque no estás seguro si algún informe o escaneo los está usando y su funcionamiento fallaría si los borras.

Cuando tienes demasiados assets dinámicos, estos consumen un montón de recursos en tu servidor, tanto si los estás usando o no. Para limpiar tu lista de assets, tendrías que ir uno por uno mirando en todos tus escaneos activos e informes, y anotar que assets todavía están en uso. Con la API, podemos extraer todos tus assets, todos tus escaneos activos, y comprobar para cada asset si está siendo usado por un escaneo. Después cogemos la lista de assets que no se están usando y los podemos borrar para reducir el consumo de recursos.

IMPORTANTE: Si usas assets del tipo combination, se muy cuidadoso, porque puedes pasar por alto assets dinámicos que se usan dentro de los combination. Si solo utilizas estáticos y dinámicos, entonces puedes utilizar estos scripts, pero siempre bajo tu responsabilidad 😅.

Empezamos por obtener los siguientes campos de todos los assets: name, id, type(combination, dynamic, static).

$request = Invoke-WebRequest -Uri " $tenableSC/rest/asset?fields=name,id,type,typeFields " -Method Get -Headers @{"X-SecurityCenter"="$token"} -WebSession $sv
$assets = (ConvertFrom-Json $request.Content)
$assets = $assets.response.usable
Assets

A continuación obtenemos todos los escaneos activos configurados con los siguientes campos: name, id, assets

$request = Invoke-WebRequest -Uri " $tenableSC/rest/scan?fields=name,id,assets " -Method Get -Headers @{"X-SecurityCenter"="$token"} -WebSession $sv
$scans = (ConvertFrom-Json $request.Content)
$scans = $scans.response.usable
Scans

Ahora vemos qué assets dinámicos están siendo usados por algún escaneo; recorremos todos los assets y si es del tipo Dynamic comprobamos si están asignados a algún escaneo:

$inuse = @()
foreach ( $asset in $assets ) {
    if ($asset.type -eq "Dynamic") {
        if ($asset.id -in $scans.assets.id) {
            $inuse += $asset
        }
    }
}

Hemos guardado todos los assets actualmente en uso por algún escaneo en la variable $inuse Estos son los que no debemos borrar o el escaneo dejaría de funcionar.

$NOTinuse = @()
foreach ( $asset in $assets ) {
    if ($asset.type -eq "Dynamic")  {
        if ($asset.id -notin $scans.assets.id) {
            $NOTinuse += $asset
        }
    }
}

Los assets en $NOTinuse no están siendo utilizados por ningún escaneo, pero están usando recursos todo el tiempo para recalcular su contenido. Si sabemos que no son usados en ningún otro sitio, como informes - esto también lo podríamos hacer con la API y el endpoint de reports - los podemos borrar.

Los siguientes pasos hazlos bajo tu propia responsabilidad - después de hacer una comprobación para asegurarte que el resultado es correcto, podríamos borrar todos los assets no utilizados en tres líneas de código:

$NOTinuse = @()
foreach ($asset in $NOTinuse) {
    $request = Invoke-WebRequest -Uri " $tenableSC/rest/asset/$asset.id " -Method Delete -Headers @{"X-SecurityCenter"="$token"} -WebSession $sv
}

Conclusión

Estos son solo algunos ejemplos que te doy como idea de como operar con la API de Tenable.sc. El próximo día te mostraré como trabajar con la herramienta de Análisis, que también puede ser muy útil, o cómo importar automáticamente resultados de escaneos desde una red a otra aislada.

¡Estate atento!

Puedes encontrar todo el código de este post aquí.