-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e6b7cb
commit 967cae4
Showing
6 changed files
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
## Cyberwatch & Nessus Integration | ||
|
||
This script will retrieve vulnerabilities of assets on Nessus and import them into Cyberwatch automatically. | ||
|
||
For this, sets of credentials for the Nessus and Cyberwatch APIs will be required to allow the retrieval of data from Nessus, and the creation of assets on Cyberwatch. | ||
|
||
Given the technical constraints, only the list of vulnerabilities for each asset on Nessus can be exported to Cyberwatch, without any association related to the affected technologies and recommended corrective actions. | ||
|
||
### Use case | ||
|
||
Let's consider the following Nessus assets. | ||
|
||
![image](img/image1.png) | ||
|
||
After executing the script, these assets will be available on Cyberwatch, along with their associated list of vulnerabilities. | ||
|
||
![image](img/image2.png) | ||
|
||
The vulnerabilities come from the Reference Information panel of the Nessus plugins results. However, not all plugins associate vulnerabilities with their results. | ||
|
||
![image](img/image3.png) | ||
|
||
The name of the assets created on Cyberwatch can be customized, particularly to customize the string "Nessus |" in front of it. | ||
|
||
_Caution : When using the script, all assets starting with the identifier ('Nessus |' by default) will be deleted, make sure no conflict can happen with any other unwanted asset._ | ||
|
||
### How to use | ||
|
||
1. Generate a set of [Nessus](https://docs.tenable.com/vulnerability-management/Content/Settings/my-account/GenerateAPIKey.htm#To-generate-API-keys-for-your-own-account:) and [Cyberwatch](https://docs.cyberwatch.fr/help/en/API_documentation/API_use/#authenticate-to-the-api-and-test-your-connection) Api keys ; | ||
2. Fill out the `api.conf` file accordingly ; | ||
3. Launch the script without any needed arguments. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
from cyberwatch_api import Cyberwatch_Pyhelper | ||
import json | ||
import requests | ||
import configparser | ||
import urllib3 | ||
urllib3.disable_warnings(category=urllib3.exceptions.InsecureRequestWarning) | ||
|
||
def jprint(text): | ||
print(json.dumps(text, indent=4)) | ||
return json.dumps(text, indent=4) | ||
|
||
def cbw_createAirgapAsset(HOSTNAME, TECHNOLOGIES): | ||
output = "HOSTNAME:{}\n".format(HOSTNAME) | ||
# for TECHNOLOGIE in TECHNOLOGIES: | ||
# (package, version) = TECHNOLOGIE.split("_") | ||
# output += "NVD_APPLICATION:(from nessus : unanalyzed) {}|{}\n".format(package, version) | ||
|
||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="POST", | ||
endpoint="/api/v2/cbw_scans/scripts", | ||
body_params={ | ||
"output" : output | ||
}, | ||
timeout=90, | ||
verify_ssl=False | ||
) | ||
result = next(apiResponse) | ||
|
||
if 'error' in result: | ||
raise "ERROR : " + result["error"]["message"] | ||
return result.json()["server_id"] | ||
|
||
|
||
def cbw_deleteAsset(id): | ||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="DELETE", | ||
endpoint="/api/v3/assets/servers/{}".format(id), | ||
verify_ssl=False, | ||
) | ||
|
||
return next(apiResponse).json() | ||
|
||
def cbw_retrieveSecurityIssue(sid): | ||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="GET", | ||
endpoint="/api/v3/security_issues", | ||
verify_ssl=False, | ||
body_params={ | ||
"sid" : sid, | ||
} | ||
) | ||
|
||
return next(apiResponse).json() | ||
|
||
def cbw_deleteSecurityIssue(id): | ||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="DELETE", | ||
endpoint="/api/v3/security_issues/{}".format(id), | ||
verify_ssl=False, | ||
) | ||
|
||
return next(apiResponse).json() | ||
|
||
def cbw_createSecurityIssue(sid, title, cve_announcements, servers): | ||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="POST", | ||
endpoint="/api/v3/security_issues", | ||
verify_ssl=False, | ||
body_params={ | ||
"sid" : sid, | ||
"title" : title, | ||
"cve_announcements" : cve_announcements, | ||
"servers" : servers, | ||
} | ||
) | ||
|
||
return next(apiResponse).json() | ||
|
||
def cbw_refreshAssetAnalysis(server_id): | ||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="PUT", | ||
endpoint="/api/v3/vulnerabilities/servers/{}/refresh".format(server_id), | ||
verify_ssl=False, | ||
) | ||
|
||
return next(apiResponse).json() | ||
|
||
def cbw_clearNessusData(): | ||
##### | ||
# Clear All Nessus Security Issues | ||
##### | ||
|
||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="GET", | ||
endpoint="/api/v3/security_issues", | ||
verify_ssl=False, | ||
) | ||
|
||
security_issues = [] | ||
for page in apiResponse: | ||
security_issues = security_issues + page.json() | ||
|
||
for security_issue in security_issues: | ||
if security_issue["title"].startswith(PARSE_CONFIG()["SCRIPT_ASSET_IDENTIFIER"]): | ||
print("Deleting Security Issue {}".format(security_issue["title"])) | ||
cbw_deleteSecurityIssue(security_issue["id"]) | ||
|
||
##### | ||
# Clear All Nessus Assets | ||
##### | ||
|
||
apiResponse = Cyberwatch_Pyhelper().request( | ||
method="GET", | ||
endpoint="/api/v3/assets/servers", | ||
verify_ssl=False, | ||
) | ||
print("Retrieving assets : 0") | ||
assets = [] | ||
for page in apiResponse: | ||
assets = assets + page.json() | ||
print("\033[A\033[A\nRetrieving assets : " + str(len(assets))) | ||
|
||
for asset in assets: | ||
if asset["hostname"].startswith(PARSE_CONFIG()["SCRIPT_ASSET_IDENTIFIER"]): | ||
print("Deleting Asset {}".format(asset["hostname"])) | ||
cbw_deleteAsset(asset["id"]) | ||
|
||
def PARSE_CONFIG(): | ||
config = configparser.ConfigParser() | ||
config.read('api.conf') | ||
NESSUS_API_KEY = config['nessus']['api_key'] | ||
NESSUS_SECRET_KEY = config['nessus']['secret_key'] | ||
NESSUS_URL = config['nessus']['url'] | ||
SCRIPT_DEBUG = config['script']['debug'] | ||
SCRIPT_ASSET_IDENTIFIER = config['script']['asset_identifier'] | ||
|
||
return { | ||
"NESSUS_API_KEY" : NESSUS_API_KEY, | ||
"NESSUS_SECRET_KEY" : NESSUS_SECRET_KEY, | ||
"NESSUS_URL" : NESSUS_URL, | ||
"SCRIPT_DEBUG" : "true" in SCRIPT_DEBUG.lower(), | ||
"SCRIPT_ASSET_IDENTIFIER" : SCRIPT_ASSET_IDENTIFIER | ||
} | ||
|
||
def NESSUS_API(URL): | ||
nessus_config = PARSE_CONFIG() | ||
return requests.get(URL, headers={'X-ApiKeys': f'accessKey={nessus_config["NESSUS_API_KEY"]}; secretKey={nessus_config["NESSUS_SECRET_KEY"]}'}, verify=False) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
from cyberwatch_api import Cyberwatch_Pyhelper | ||
from cbw_helper import * | ||
import sys | ||
import urllib3 | ||
urllib3.disable_warnings(category=urllib3.exceptions.InsecureRequestWarning) | ||
|
||
##### | ||
# Initializing global variables | ||
##### | ||
NESSUS_URL = PARSE_CONFIG()["NESSUS_URL"] | ||
DEBUG = PARSE_CONFIG()["SCRIPT_DEBUG"] | ||
NESSUS_ASSETS = {} | ||
|
||
##### | ||
# Retrieving all Nessus Scans | ||
##### | ||
|
||
SCANS = NESSUS_API('{}/scans'.format(NESSUS_URL)).json()["scans"] | ||
|
||
for SCAN in SCANS: | ||
print("[+] Nessus - Retrieving scan {}".format(SCAN["id"])) | ||
##### | ||
# Retrieve all hosts associated to the current scan | ||
##### | ||
HOSTS = NESSUS_API('{}/scans/{}'.format(NESSUS_URL, SCAN["id"])).json()["hosts"] | ||
|
||
for HOST in HOSTS: | ||
print("\t Host : {}".format(HOST["hostname"])) | ||
##### | ||
# Clean the previous host data | ||
##### | ||
CVE_LIST = [] | ||
|
||
##### | ||
# Get the current host informations and plugins list | ||
##### | ||
HOST_ID = HOST["host_id"] | ||
HOST_NAME = HOST["hostname"] | ||
HOST_INFORMATIONS = NESSUS_API('{}/scans/{}/hosts/{}'.format(NESSUS_URL, SCAN["id"], HOST_ID)).json() | ||
HOST_PLUGINS_LIST = list(set([CVE["plugin_id"] for CVE in HOST_INFORMATIONS["vulnerabilities"] if CVE["plugin_id"]])) | ||
print("\t\t Plugins : {}".format(len(HOST_PLUGINS_LIST))) | ||
|
||
##### | ||
# For each plugin, retrieve the associated CVEs | ||
##### | ||
for PLUGIN in HOST_PLUGINS_LIST: | ||
try: | ||
URL = '{}/scans/{}/hosts/{}/plugins/{}'.format(NESSUS_URL, SCAN["id"], HOST_ID, PLUGIN) | ||
RESPONSE = NESSUS_API(URL) | ||
CVE_LIST = list(set(CVE_LIST + next(ref["values"]["value"] for ref in NESSUS_API(URL).json()["info"]["plugindescription"]["pluginattributes"]["ref_information"]["ref"] if ref["name"] == "cve"))) | ||
except: | ||
next | ||
|
||
##### | ||
# If it is the first time we find the host, we save it. Otherwise, we just append the found CVEs | ||
##### | ||
|
||
if HOST_NAME not in NESSUS_ASSETS: | ||
NESSUS_ASSETS[HOST_NAME] = { | ||
"id" : HOST_ID, | ||
"hostname" : HOST_NAME, | ||
"cve_list" : CVE_LIST, | ||
} | ||
else: | ||
NESSUS_ASSETS[HOST_NAME]["cve_list"] = list(set(NESSUS_ASSETS[HOST_NAME]["cve_list"] + CVE_LIST)) | ||
|
||
print("\t\t Total CVEs : {}".format(len(NESSUS_ASSETS[HOST_NAME]["cve_list"]))) | ||
|
||
##### | ||
# Clean cyberwatch data related to nessus | ||
##### | ||
print("[+] Cleaning all related Nessus data on Cyberwatch") | ||
cbw_clearNessusData() | ||
|
||
##### | ||
# Foreach found nessus host, we create a security issue linking all the found CVEs, then we associate it to the matching airgap asset | ||
##### | ||
|
||
for NESSUS_ASSET_ID, NESSUS_ASSET in NESSUS_ASSETS.items(): | ||
##### | ||
# Creating or updating the airgap asset | ||
##### | ||
CBW_ASSET_HOSTNAME = "Nessus | {}".format(NESSUS_ASSET["hostname"]) | ||
CBW_ASSET_ID = cbw_createAirgapAsset(CBW_ASSET_HOSTNAME, []) | ||
print("[+] Cyberwatch - Creating asset {}".format(CBW_ASSET_HOSTNAME)) | ||
|
||
##### | ||
# Creating the security issue | ||
##### | ||
CBW_HOST_SID = "Nessus_{}".format(NESSUS_ASSET["hostname"]) | ||
CBW_HOST_SID_TITLE = "Nessus | {}".format(NESSUS_ASSET["hostname"]) | ||
|
||
if bool(cbw_retrieveSecurityIssue(CBW_HOST_SID)): # Security Issues Already Exists | ||
cbw_deleteSecurityIssue(cbw_retrieveSecurityIssue(CBW_HOST_SID)[0]["id"]) | ||
|
||
cbw_createSecurityIssue(CBW_HOST_SID, CBW_HOST_SID_TITLE, NESSUS_ASSET["cve_list"], [CBW_ASSET_ID]) |