Cyber Triage

Allows you to conduct a mini-forensic investigation on an endpoint. It pushes a collection tool to the remote endpoint, collects volatile and file system data, and analyzes the data.

Endpoint · Cyber Triage

Details

IDCyber Triage
ProviderBasis Technology
CategoryEndpoint
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Overview


Use the Cyber Triage integration to collect and analyze endpoint data

This integration requires Team version of Cyber Triage (not the Standalone desktop version).

This integration was integrated and tested with Cyber Triage v2.4.0.

 

Configure Cyber Triage on Cortex XSOAR


  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Cyber Triage.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Hostname of Cyber Triage server (e.g. 192.168.1.2) : the ip or hostname where the Cyber Triage server is setup.
    • REST Port : REST port for Cyber Triage server. 9443 is the default port and currently cannot be changed in Cyber Triage.
    • API Key : can be retrieved from the Cyber Triage server by going to Options -> Deployment Mode -> REST API Key.
    • Username : the username and password of a Windows account with administrative privileges on all endpoints that need to be investigated.
    • Use proxy : select if you have a proxy setup in your environment and need to use it to reach the Cyber Triage server.
  4. Click Test to validate the URLs, token, and connection.

 

Commands


You can execute these commands from the Cortex XSOAR 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.

  1. Initiate a collection on an endpoint: ct-triage-endpoint

Initiate a collection on an endpoint


Initiates a Cyber Triage collection on an endpoint.

Base Command
ct-triage-endpoint
Input
Argument Name Description Required
endpoint IP or hostname of a Windows endpoint Required
full_scan Scan the entire file system for suspicious files Optional
malware_hash_upload Send MD5 hashes to an external malware analysis service Optional
malware_file_upload Send unknown files to an external malware analysis service. Hash upload must be enabled to execute file uploads. Optional
 
Context Output
Path Type Description
CyberTriage.SessionId string The session ID for the newly created session
Endpoint.IPAddress string The endpoint IP address that Cyber Triage investigated
Endpoint.Hostname string The endpoint hostname that Cyber Triage investigated
 
Command Example
!ct-triage-endpoint endpoint=ct-win10-01 full_scan=no
Context Example

CyberTriage.SessionID: ct-win10-01|1538074422288
CyberTriage.Hostname: ct-win10-01

Human Readable Output

A collection has been scheduled for ct-win10-01

 

Configuration parameters

  • server — Hostname of Cyber Triage server (e.g. 192.168.1.2) (required)
  • rest_port — REST Port (required)
  • api_key
  • credentials — Username (required)
  • use_proxy — Use proxy

Commands (1)

  • ct-triage-endpoint

    initiates a cyber triage collection on an endpoint.

from typing import Any

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

# Disable warning for insecure requests when cert validation is disabled
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


def IS_2XX(x: int) -> bool:
    return int(x / 100) == 2  # Returns true if status code (int) is 2xx


class CyberTriageClient(BaseClient):
    SCAN_OPTIONS = ["pr", "nw", "nc", "st", "sc", "ru", "co", "lo", "ns", "wb", "fs"]

    def __init__(
        self,
        server: str,
        rest_port: str,
        api_key: str,
        user: str,
        password: str,
        verify_server_cert: bool,
        ok_codes: tuple[int, ...],
    ):
        base_url = (
            f"https://{server}:{rest_port}/api/"
            if not (server.startswith(("https://", "http://")))
            else f"{server}:{rest_port}/api/"
        )
        req_headers = {"restApiKey": api_key}
        self._user = user
        self._password = password
        super().__init__(base_url=base_url, verify=verify_server_cert, headers=req_headers, ok_codes=ok_codes)

    def test_connection(self):
        response = self._http_request("GET", url_suffix="correlation/checkcredentials", resp_type="response")
        return response

    def triage_endpoint(
        self, is_hash_upload_on: bool, is_file_upload_on: bool, endpoint: str, scan_options: str, incident_name: str
    ):
        # Validate scan options
        invalid_options = []
        if scan_options:
            invalid_options = [opt for opt in scan_options.split(",") if opt not in self.SCAN_OPTIONS]
        if invalid_options:
            raise DemistoException("The following are not valid scan options: {}".format(",".join(invalid_options)))

        # Make data dict for rest call
        api_data = {
            "incidentName": incident_name,
            "hostName": endpoint,
            "userId": self._user,
            "password": self._password,
            "scanOptions": scan_options,
            "malwareScanRequested": is_hash_upload_on,
            "sendContent": is_file_upload_on,
            "sendIpAddress": False,
        }
        response = self._http_request("POST", url_suffix="livesessions", data=api_data, resp_type="response")
        return response


def test_connection_command(client: CyberTriageClient) -> str:
    response = client.test_connection()
    response.raise_for_status()
    return "ok"


def triage_endpoint_command(client: CyberTriageClient, args: dict[str, Any]) -> CommandResults:
    def is_true(x: str) -> bool:
        return x == "yes"

    is_hash_upload_on = is_true(args.get("malware_hash_upload", ""))  # arg value = 'yes' or 'no'
    is_file_upload_on = is_true(args.get("malware_file_upload", ""))  # arg value = 'yes' or 'no'
    endpoint = args.get("endpoint", "")
    scan_options = args.get("scan_options", "")
    incident_name = args.get("incident_name", "")
    response = client.triage_endpoint(is_hash_upload_on, is_file_upload_on, endpoint, scan_options, incident_name)

    response.raise_for_status()

    if is_ip_valid(endpoint):
        endpoint_context = {"IPAddress": endpoint}
    else:
        endpoint_context = {"Hostname": endpoint}

    data = response.json()

    ec = {"CyberTriage": data, "Endpoint": endpoint_context}

    return CommandResults(readable_output=f"A collection has been scheduled for {endpoint}", outputs=ec, raw_response=data)


def main() -> None:  # pragma: no cover
    """Main function, parses params and runs command functions."""
    params = demisto.params()
    command = demisto.command()
    args = demisto.args()

    server = params.get("server", "")
    rest_port = params.get("rest_port", "")
    api_key = params.get("api_key", {}).get("password", "")
    user = params.get("credentials", {}).get("identifier", "")
    password = params.get("credentials", {}).get("password", "")
    verify_server_cert = False
    handle_proxy(proxy_param_name="use_proxy")

    demisto.debug(f"Command being called is {command}")
    try:
        acceptable_status_codes: tuple[int, ...] = tuple(int(code) for code in requests.status_codes.codes if IS_2XX(code))
        client = CyberTriageClient(server, rest_port, api_key, user, password, verify_server_cert, acceptable_status_codes)
        # This is the call made when running the ct-triage-endpoint command.
        if command == "ct-triage-endpoint":
            return_results(triage_endpoint_command(client, args))

        # This is the call made when pressing the integration test button.
        elif command == "test-module":
            return_results(test_connection_command(client))
        else:
            raise NotImplementedError(f"command={command} not implemented in this integration")
    except Exception as e:
        return_error(f"Failed to execute {command} command.\nError: {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):  # pragma: no cover
    main()