Cymptom

Cymptom is a Breach and Attack Simulation solution that revolutionizes the existing approach by transforming attack simulation into a data analysis question. Cymptom agentless scanning brings real-time always-on visibility into the entire security posture.

Vulnerability Management · Cymptom

Details

IDCymptom
ProviderTenable
CategoryVulnerability Management
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Cymptom is a Breach and Attack Simulation solution that revolutionizes the existing approach by transforming attack simulation into a data analysis question. Cymptom agentless scanning brings real-time always-on visibility into the entire security posture.
This integration was integrated and tested with version 0.3.4 of Cymptom.

Configure Cymptom in Cortex

Parameter Description Required
url Management URL (for ex: https://customer_name.cymptom.com/api/) True
api_key API key True
is_fetch Fetch incidents False
proxy Use system proxy settings False
insecure Trust any certificate (not secure) False
first_fetch First fetch time range (<number> <time unit>, e.g., 1 hour, 30 minutes). Default is “3 days” 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.

cymptom-get-mitigations


This command returns mitigations recommended by Cymptom

Base Command

cymptom-get-mitigations

Input

Argument Name Description Required
timeout Timeout for operation. Default is 60. Optional

Context Output

Path Type Description
Cymptom.Mitigations.SeverityType String The severity of the mitigation
Cymptom.Mitigations.Name String The name of the mitigation
Cymptom.Mitigations.AttackVectorsUsedPercentage String The percentage of attack vectors used that can be mitigated
Cymptom.Mitigations.ID String The mitigation’s ID
Cymptom.Mitigations.AttackVectorsCount number The attack vectors counts that can be mitigated
Cymptom.Mitigations.Techniques unknown Techniques relevant for this mitigation

Command Example

!cymptom-get-mitigations

Human Readable Output

Mitigations
ID Name Severity Type Attack Vectors Use Percentage Attack Vectors Count Techniques
3936 Steal or Forge Kerberos Tickets Critical 21.16 299 Encrypt Sensitive Information,
Privileged Account Management,
Active Directory Configuration,
Password Policies

cymptom-get-users-with-cracked-passwords


This command returns users with cracked password

Input

Argument Name Description Required
timeout Timeout for operation. Default is 60. Optional
privileged Return only privileged (Domain Admin or Local Admin) or unprivileged users. Default is True. Optional

Context Output

Path Type Description
Cymptom.CrackedUsers.Username String Username of users with cracked passwords

Context Example

[
    {'Username':'cymptom'},
    {'Username':'chen'}
]

Command Example

!cymptom-get-users-with-cracked-passwords privileged=False 

Human Readable Output

Unprivileged Users With Cracked Passwords

Username
user1
user2

Configuration parameters

  • url — Management URL. for example https://customerName.cymptom.com/api/) (required)
  • api_key — API key (required)
  • is_fetch — Fetch incidents
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)
  • first_fetch — First fetch time range (<number> <time unit>, e.g., 1 hour, 30 minutes). Default is "3 days"

Commands (2)

  • cymptom-get-mitigations

    This command returns mitigations recommended by Cymptom

  • cymptom-get-users-with-cracked-passwords

    This command returns users with cracked password

# Imports
from enum import Enum

import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401

# Disable insecure warnings
urllib3.disable_warnings()

headers: dict[str, str] = {}
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"


class MitigationsState(Enum):
    open = "open"
    archive = "archive"
    all = "all"


class Client(BaseClient):
    """
    Client will implement the service API, and should not contain any Demisto logic.
    Should only do requests and return data.
    """

    def api_test(self):
        """
        Sends a test api call to the management server.
        """
        results = self._http_request(method="GET", url_suffix="test")

        return results

    def get_mitigations(self, timeout=60, mitigations_state=MitigationsState.open.value):
        data = {mitigations_state: mitigations_state.lower()}

        if mitigations_state == MitigationsState.all.value:
            data.pop(mitigations_state)

        return self._http_request(method="GET", url_suffix="mitigations", data=data, timeout=timeout)

    def get_mitigation_by_id(self, mitigation_id, timeout=60):
        return self._http_request(
            method="GET", url_suffix="mitigations/mitigation", params={"id": str(mitigation_id)}, timeout=timeout
        )

    def get_mitigations_subtechnique_procedure_by_id(self, sub_tech_proc_id, timeout=60):
        return self._http_request(
            method="GET", url_suffix=f"mitigations/subtechnique-procedures/{sub_tech_proc_id!s}", timeout=timeout
        )


def api_test(client: Client):
    """
    Returning 'ok' indicates that the integration works like it is supposed to and the Connection to the service is successful.
    :param client: Cymptom client
    """
    try:
        results = client.api_test()
        if results and results.get("status") == "ok":
            return return_results("ok")
        else:
            return return_error(f"There was an error: {results.get('status', 'Failure')} - {results.get('error')}")
    except Exception as e:
        return_error(
            f"There was an error in testing connection to URL: {client._base_url},"
            f"Please make sure that the API key is valid and has the right permissions, "
            f"and that the URL is in the correct form. Error: {e!s}"
        )


def get_mitigations(client: Client) -> CommandResults:
    """
    This function uses a client argument
    """
    args = demisto.args()
    timeout = args.get("timeout", 60)
    state = args.get("state", MitigationsState.open.value)
    timeout = int(timeout)

    mitigations_results = client.get_mitigations(timeout=timeout, mitigations_state=state)

    mitigations_formatted = []
    table_headers = ["ID", "Name", "SeverityType", "AttackVectorsUsePercentage", "AttackVectorsCount", "Techniques"]

    for mitigation in mitigations_results.get("mitigations", {}):
        extended_info = client.get_mitigation_by_id(mitigation["id"])
        severity_type = mitigation["severity"]["name"].capitalize()
        severity_percentage = round(extended_info["severity"]["percentage"], 2)

        mitigations_formatted.append(
            {
                "ID": mitigation["id"],
                "Name": mitigation["name"],
                "SeverityType": severity_type,
                "AttackVectorsUsePercentage": severity_percentage,
                "AttackVectorsCount": mitigation["vectorCount"],
                "Techniques": mitigation["mitigations"],
            }
        )

    readable_output = tableToMarkdown("Mitigations", mitigations_formatted, headers=table_headers)

    command_results = CommandResults(
        outputs_prefix="Cymptom.Mitigations",
        outputs_key_field="ID",
        readable_output=readable_output,
        outputs=mitigations_formatted,
        raw_response="",
    )
    return command_results


def get_users_with_cracked_passwords(client: Client):
    """
    This function uses a client argument
    """

    args = demisto.args()
    timeout = args.get("timeout", 60)
    timeout = int(timeout)

    mitigations_results = client.get_mitigations(timeout=timeout)
    users_formatted = []
    table_headers = ["Username"]
    procedures_ids: set[str] = set()
    mitigation_id = None
    privileged_users = []
    unprivileged_users = []

    privileged = argToBoolean(args.get("privileged", "True"))

    for mitigation in mitigations_results["mitigations"]:
        if mitigation["name"] == "Brute Force":
            mitigation_id = mitigation["id"]
            break

    if mitigation_id:
        l_mitigations = client.get_mitigation_by_id(mitigation_id)

        for subtechnique in l_mitigations["subtechniques"]:
            if subtechnique["name"] == "Password Guessing":
                procedures_ids.add(subtechnique["id"])

    if procedures_ids:
        for proc_id in procedures_ids:
            l_users = client.get_mitigations_subtechnique_procedure_by_id(proc_id)

            if l_users:
                l_users = l_users[0]["targets"]
                for user in l_users:
                    if "Domain" in user["labels"]:
                        username_dict = user["name"]
                        if privileged and ("DomainAdmin" in user["labels"] or "ComputerAdmin" in user["labels"]):
                            privileged_users.append(username_dict)
                        else:
                            unprivileged_users.append(username_dict)

    if privileged_users:
        for username in privileged_users:
            users_formatted.append({"Username": username})
        readable_output = tableToMarkdown("Privileged Users With Cracked Passwords", privileged_users, headers=table_headers)
    elif unprivileged_users:
        for username in unprivileged_users:
            users_formatted.append({"Username": username})
        readable_output = tableToMarkdown("Unprivileged Users With Cracked Passwords", unprivileged_users, headers=table_headers)
    else:
        readable_output = tableToMarkdown("Users With Cracked Passwords", unprivileged_users, headers=table_headers)

    command_results = CommandResults(
        outputs_prefix="Cymptom.CrackedUsers",
        outputs_key_field="Username",
        readable_output=readable_output,
        outputs=users_formatted,
    )

    return command_results


def main():
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    params = demisto.params()

    base_url = params["url"]
    api_key = params["api_key"]

    # Flag if use server proxy
    use_proxy = params.get("proxy", False)

    # Flag if use server 'verification'
    insecure = not params.get("insecure", False)

    headers = {
        "Authorization": f"Bearer {api_key}"  # Replace ${token} with the token you have obtained
    }

    demisto.debug(" ---- MAIN CALL -----")
    demisto.debug(" ---- PARAMS -----")
    demisto.debug(f"base_url: {base_url}")
    demisto.debug(f"insecure: {insecure}")
    demisto.debug(f"use_proxy: {use_proxy}")

    demisto.info(f"Command being called is: {demisto.command()}")

    client = Client(base_url=base_url, headers=headers, proxy=use_proxy, verify=insecure)

    try:
        # This is the call made when pressing the integration Test button.
        if demisto.command() == "test-module":
            return api_test(client)

        elif demisto.command() == "cymptom-get-mitigations":
            return_results(get_mitigations(client))

        elif demisto.command() == "cymptom-get-users-with-cracked-passwords":
            return_results(get_users_with_cracked_passwords(client))

    # Log exceptions
    except Exception as e:
        demisto.debug(str(e))
        return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}")


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