ACTI Vulnerability Query

ACTI provides intelligence regarding security threats and vulnerabilities.

Vulnerability Management · Accenture CTI v2

Details

IDACTI Vulnerability Query
ProviderAccenture
CategoryVulnerability Management
From Version5.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Accenture CTI provides intelligence regarding security threats and vulnerabilities.
This integration was integrated and tested with version v2.93.0 of ACTI

Configure ACTI Vulnerability Query in Cortex

Parameter Description Required
url URL True
api_token API Token True
Source Reliability Reliability of the source providing the intelligence data. B - Usually reliable
insecure Trust any certificate (not secure) False
use_proxy Use system proxy settings False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

acti-vuln


Checks the reputation of the given common vulnerabilities and exposures ID.

Base Command

acti-vuln

Input

Argument Name Description Required
cve CVE ID to check. Optional

Context Output

Path Type Description
CVE.ID String The ID of the CVE, for example: CVE-2022-1653
CVE.CVSS2 String The CVSS2 temporal score of the CVE based on exploitability, remediation level & report confidence, for example: 10.0
CVE.CVSS3 String The CVSS3 temporal score of the CVE based on exploitability, remediation level & report confidence, for example: 10.0
CVE.Published String The timestamp of when the CVE was published.
CVE.Modified String The timestamp of when the CVE was last modified.
CVE.Description String A description of the CVE.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Reliability String Reliability of the source providing the intelligence data.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor that was used to calculate the score.
DBotScore.Score String The actual score.

Command Example

!acti-vuln cve=CVE-2022-1653

Context Example

{
    "DBotScore": {
        "Indicator": "CVE-2022-1653",
        "Reliability": "B - Usually reliable",
        "Score": 2,
        "Type": "cve",
        "Vendor": "ACTI Vulnerability Query"
    },
    "CVE": {
        "CVSS2": "10.0",
        "CVSS3": "10.0",
        "Description": "Description of the vulnerability",
        "ID": "CVE-2022-1653",
        "Modified": "2022-01-27 03:40:00",
        "Published": "2022-01-22 04:01:42",
    }
}

Human Readable Output

Results

CPEs CVSS2 CVSS3 DbotReputation Description LastModified LastPublished Name UUID
cpe:/a:f5:big-ip:16.1.1 10 10 2 Description of the vulnerability 2022-01-27 03:40:00 2022-01-22 04:01:42 CVE-2022-1653 cbc55efe-aa5c-4114-b532-e44f9b824fe1

Configuration parameters

  • url — URL (required)
  • api_token — (required)
  • integrationReliability — Source Reliability (required)
  • insecure — Trust any certificate (not secure)
  • use_proxy — Use system proxy settings

Commands (1)

  • acti-vuln

    Checks reputation of the vulnerability.

import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
import urllib3
import traceback

# Disable insecure warnings
urllib3.disable_warnings()  # pylint: disable=no-member


""" CONSTANTS """
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
ENDPOINTS = {"vulnerability": "/rest/vulnerability"}


""" CLIENT CLASS """


class Client(BaseClient):
    def __init__(self, input_url: str, api_key: str, verify_certificate: bool, proxy: bool, endpoint="/rest/vulnerability"):
        base_url = urljoin(input_url, endpoint)
        PACK_VERSION = get_pack_version()
        DEMISTO_VERSION = demisto.demistoVersion()
        DEMISTO_VERSION = f'{DEMISTO_VERSION["version"]}.{DEMISTO_VERSION["buildNumber"]}'
        headers = {
            "Content-Type": "application/json",
            "auth-token": api_key,
            "User-Agent": f"AccentureCTI Pack/{PACK_VERSION} Palo Alto XSOAR/{DEMISTO_VERSION}",
        }
        super().__init__(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy)

    def vulnerability_search(self, url_suffix: str, data: dict = {}) -> dict:
        return self._http_request(method="GET", url_suffix=url_suffix, params=data)


class CustomCVE(Common.CVE):
    def __init__(self, id, cvss3, cvss2, published, modified, description, dbot, relationships=None):
        # type (str, str, str, str, str, str, str) -> None
        super().__init__(
            id=id, cvss="", published=published, modified=modified, description=description, relationships=relationships
        )
        self.cvss3 = cvss3
        self.cvss2 = cvss2
        self.dbot_score = dbot

    def to_context(self):
        context_values = super().to_context()
        context_values[self.CONTEXT_PATH]["CVSS2"] = self.cvss2
        context_values[self.CONTEXT_PATH]["CVSS3"] = self.cvss3
        context_values[self.CONTEXT_PATH]["CVSS"]["CVSS2"] = self.cvss2
        context_values[self.CONTEXT_PATH]["CVSS"]["CVSS3"] = self.cvss3
        if self.dbot_score:
            context_values.update(self.dbot_score.to_context())
        return context_values


""" HELPER FUNCTIONS """


def _calculate_dbot_score(severity: int) -> int:
    """
    Calculates Dbot score according to table:
    Dbot Score   | severity
     0           | 0
     1           | 1,2
     2           | 3,4
     3           | 5,6,7
    Args:
        severity: value from 1 to 5, determined by iDefense threat indicator

    Returns:
        Calculated score
    """
    dbot_score = Common.DBotScore.NONE

    if severity > 4:
        dbot_score = Common.DBotScore.BAD
    elif severity > 2:
        dbot_score = Common.DBotScore.SUSPICIOUS
    elif severity > 0:
        dbot_score = Common.DBotScore.GOOD

    return dbot_score


def _extract_result(res: dict, dbot_score_type: str, reliability: DBotScoreReliability) -> list[dict]:
    analysis_results = []
    if res.get("total_size"):
        results_array = res.get("results", [])
        if len(results_array):
            for result in results_array:
                cpes = []
                indicator_value = result.get("key", "")
                dbot_score: int = _calculate_dbot_score(result.get("severity", 0))
                desc = "Match found in AccentureCTI database"
                dbot = Common.DBotScore(indicator_value, dbot_score_type, "ACTIVulnerabilityQuery", dbot_score, desc, reliability)

                vuln_techs = result.get("affects", "").get("vuln_techs", "")
                for vuln in vuln_techs:
                    cpes.append(vuln.get("display_text", ""))

                last_published = result.get("last_published", "")
                if last_published:
                    last_published_format = parse_date_string(last_published, DATE_FORMAT)
                else:
                    last_published_format = "NA"

                last_modified = result.get("last_modified", "")
                if last_modified:
                    last_modified_format = parse_date_string(last_modified, DATE_FORMAT)
                else:
                    last_modified_format = "NA"
                display_text = result.get("display_text", "NA")
                analysis_info = {
                    "Name": result.get("key", "NA"),
                    "DbotReputation": dbot_score,
                    "CVSS2": result.get("cvss2_temporal_score", "NA"),
                    "CVSS3": result.get("cvss3_temporal_score", "NA"),
                    "UUID": result.get("uuid", "NA"),
                    "CPEs": cpes,
                    "LastPublished": str(last_published_format),
                    "LastModified": str(last_modified_format),
                    "Description": result.get("description", ""),
                }
                analysis_results.append({"analysis_info": analysis_info, "dbot": dbot, "display_text": display_text})

    return analysis_results


def _returned_response(res: dict) -> list[str]:
    """
    Checks which indicator value found on AccentureCTI database.
    Args:
        res: api response

    Returns: list of indicator values that returned from api request

    """
    returned_res = []
    if res.get("total_size"):
        results_array = res.get("results", [])
        if len(results_array):
            for result_content in results_array:
                returned_res.append(result_content.get("key", ""))
    return returned_res


def _match_not_found(all_inputs: list, res: list) -> list[str]:
    """

    Args:
        all_inputs: all indicator values received from the user
        res: list of all indicator values that returned from api request

    Returns: Which indicator has no match on AccentureCTI database

    """
    value_not_found = []

    for val in all_inputs:
        if val not in res:
            value_not_found.append(val)

    return value_not_found


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:  # type: ignore
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param Client: client to use

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """

    try:
        client.vulnerability_search(url_suffix="/v0")
        return "ok"
    except Exception as e:
        if "Error in API call [403]" in e.args[0]:
            return_results(f"This API token doesn't have permission for accessing vulnerability API!.\n Error: {str(e)}")
            demisto.debug(e.args[0])
        else:
            raise DemistoException(f"Error in API call - check the input parameters and the API Key. Error: {e}.")


def vuln_command(client: Client, args: dict, reliability: DBotScoreReliability) -> list[CommandResults]:
    vulns: list = argToList(args.get("cve", None))
    if not vulns:
        raise ValueError("vuln not specified")

    result = client.vulnerability_search(url_suffix="/v0", data={"key.values": vulns})
    analysis_results = _extract_result(result, DBotScoreType.CVE, reliability)
    returned_cves = _returned_response(result)
    cves_not_found = _match_not_found(vulns, returned_cves)

    command_results = []
    for analysis_result in analysis_results:
        analysis_info: dict = analysis_result.get("analysis_info", {})
        dbot = analysis_result.get("dbot")
        cve_name = analysis_info.get("Name", "")
        display_name = analysis_result.get("display_text", "")
        readable_output = tableToMarkdown(f"{display_name}", analysis_info)
        indicator = CustomCVE(
            cve_name,
            analysis_info.get("CVSS3"),
            analysis_info.get("CVSS2"),
            analysis_info.get("LastPublished"),
            analysis_info.get("LastModified"),
            analysis_info.get("Description"),
            dbot,
        )  # noqa: E501
        command_results.append(CommandResults(indicator=indicator, raw_response=result, readable_output=readable_output))  # type: ignore  # noqa: E501

    for cve in cves_not_found:
        desc = "No results were found on AccentureCTI database"
        dbot = Common.DBotScore(cve, DBotScoreType.CVE, "ACTIVulnerabilityQuery", 0, desc)
        indicator = CustomCVE(cve, None, None, None, None, None, dbot)
        readable_output = f"No results were found for cve {cve}"
        command_results.append(CommandResults(indicator=indicator, readable_output=readable_output))  # type: ignore  # noqa: E501

    return command_results


""" MAIN FUNCTION """


def main():
    params = demisto.params()
    api_key = params.get("api_token")
    if isinstance(api_key, dict):
        api_key = api_key.get("password")

    base_url = urljoin(params.get("url", ""))
    reliability = params.get("integrationReliability", "B - Usually reliable")

    if DBotScoreReliability.is_valid_type(reliability):
        reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability)
    else:
        Exception("Accenture CTI error: Please provide a valid value for the Source Reliability parameter")

    commands = {
        "acti-vuln": vuln_command,
    }

    verify_certificate = not params.get("insecure", False)
    proxy = params.get("use_proxy", False)

    try:
        command = demisto.command()
        client = Client(base_url, api_key, verify_certificate, proxy, endpoint=ENDPOINTS["vulnerability"])
        demisto.debug(f"Command being called is {command}")

        if command == "test-module":
            return_results(test_module(client))
        elif command in commands:
            return_results(commands[command](client, demisto.args(), reliability))

    # Log exceptions and return errors   # noqa: E303
    except Exception as e:
        if "Error in API call [403]" in e.args[0]:
            return_error(f"This API token doesn't have permission for accessing vulnerability API!.\n Error: {str(e)}")
        else:
            demisto.error(traceback.format_exc())
            return_error(f"Failed to execute {demisto.command()} command.\nError:\n{str(e)}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()