LogsignSiem

Logsign SIEM provides to collect and store unlimited data, investigate and detect threats, and respond automatically.

Analytics & SIEM · Logsign SIEM

Details

IDLogsignSiem
ProviderLogsign
CategoryAnalytics & SIEM
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Logsign SIEM provides comprehensive visibility and control of your data lake by allowing security analysts to collect and store unlimited data, investigate and detect threats, and respond automatically.

This integration was integrated and tested with version 4.6.x of Logsign SIEM

Configure Logsign SIEM in Cortex

Parameter Description Required
Server URL (e.g. https://192.168.0.1) Logsign SIEM API URL True
API Key The API Key to use for connection True
Trust any certificate (not secure)   False
Use system proxy settings   False
Last Run Time (ISO8601 format with UTC) Last run time format like ‘%Y-%m-%dT%H:%M:%SZ’ True
First Fetch Time (default 1 hour) First Fetch Time (e.g 1 hour) False
isFetch   False
Max Fetch Maximum number of incidents per fetch (Recommended less than 200) 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.

logsign-fetch-incidents


Gets Incidents from Logsign SIEM

Base Command

logsign-fetch-incidents

Input

Argument Name Description Required
last_run Last Run Fomat: ‘%Y-%m-%dT%H:%M:%SZ’. Possible values are: . Required
query Lucene query support Optional

Context Output

Output Path Type Description
Logsign.Incidents.Severity.Name String Incident Severity
Logsign.Incidents.Time.Generated date Time Generation of incident
Logsign.Incidents.EventMap.Context String Context of Event
Logsign.Incidents.Event.Category String Category of triggered event
Logsign.Incidents.EventSource.IP unknown The eventsource addresses that are associated with the incident
Logsign.Incidents.Destination.IP unknown The destination addresses that are associated with the incident
Logsign.Incidents.Source.IP unknown The source addresses that are associated with the incident
Logsign.Incidents.Destination.Port unknown Destination Port
Logsign.Incidents.Source.Port unknown Source Port
Logsign.Incidents.Alert.Category String Category of Incident
Logsign.Incidents.Alert.Info String Incident Description
Logsign.Incidents.Alert.AlertUID Number ID of Incident
Logsign.Incidents.Alert.TriggerEventID Number Trigger Event ID
Logsign.Incidents.EventSource.HostName String The eventsource hostname that are associated with the incident

Command Example

!logsign-fetch-incidents last_run=2021-07-27T10:27:00Z

Context Example

{
  "success": true,
  "incidents": [
    {
      "Packets": {
        "Total": 2,
        "Sent": 1,
        "Received": 1
      },
      "Bytes": {
        "Total": 124,
        "Sent": 60,
        "Received": 64
      },
      "Severity": {
        "ID": 4,
        "Name": "warning"
      },
      "Time": {
        "Generated": "2021-07-27 10:35:33",
        "Start": "2021-07-27 10:26:40",
        "Received": "2021-07-27 10:35:33"
      },
      "EventMap": {
        "Context": "Network",
        "Type": "Session",
        "SubType": "Allow",
        "ID": 60101,
        "Info": "Network Connection Allow"
      },
      "Event": {
        "VendorID": 2,
        "SystemID": 30292,
        "Action": "allow",
        "SubCategory": "end",
        "Reason": "tcp-rst-from-server",
        "Category": "TRAFFIC",
        "TrackID": "2c60f28a"
      },
      "EventSource": {
        "IP": "10.x.x.1",
        "Serial": "001801044448",
        "Vendor": "PaloAlto",
        "Type": "Security System",
        "Category": "Firewall",
        "PrefixID": 3029,
        "HostName": "PA-3020",
        "Description": "PaloAlto",
        "Collector": "alert.flow"
      },
      "Session": {
        "RepeatCount": "1",
        "ID": "137290"
      },
      "Application": {
        "Name": "incomplete"
      },
      "Destination": {
        "IP": "193.x.x.6",
        "City": "Istanbul",
        "Country": "Turkey",
        "Interface": "ethernet1/3.100",
        "Port": 80,
        "Zone": "LogsignServers",
        "NatIP": "10.x.x.10",
        "NatPort": 80,
        "Location": "Unknown",
        "Position": "in"
      },
      "Source": {
        "IP": "183.x.x.14",
        "Country": "China",
        "Interface": "ethernet1/12",
        "Port": 21089,
        "Zone": "untrust",
        "NatIP": "183.x.x.14",
        "NatPort": 21089,
        "Location": "Unknown",
        "Position": "out"
      },
      "URL": {
        "Category": "any"
      },
      "Protocol": {
        "Name": "TCP"
      },
      "Rule": {
        "Name": "IsmailLogsign"
      },
      "Details": {
        "EndReason": "tcp-rst-from-server"
      },
      "DataType": "alert",
      "rawmsg": "Jul 27 10:26:44 PA-3020 1,2021/07/27 10:26:44,001801044448,TRAFFIC,end,2304,2021/07/27 10:26:44,183.136.225.14,193.192.118.6,183.136.225.14,10.10.100.2,IsmailLogsign,,,incomplete,vsys1,untrust,LogsignServers,ethernet1/12,ethernet1/3.100,LogsignSyslogProfile,2021/07/27 10:26:44,137290,1,21089,80,21089,80,0x40401b,tcp,allow,124,60,64,2,2021/07/27 10:26:40,0,any,0,2622656518,0x0,China,Turkey,0,1,1,tcp-rst-from-server,0,0,0,0,,PA-3020,from-policy,,,0,,0,,N/A,0,0,0,0,1cf61932-8145-4fd8-8d01-7a410f782236,0",
      "Internal": {
        "IP": "193.x.118.6"
      },
      "External": {
        "IP": "183.x.225.14"
      },
      "Intelligence": {
        "IP": "183.x.225.14",
        "Info": "Attack Info",
        "ListID": 1029,
        "Type": "Attack"
      },
      "Context": {
        "SourceIP": [
          "Suspicious"
        ]
      },
      "Behavior": {
        "SourceIP": [
          "Threat Intelligence IPs"
        ]
      },
      "_insert_time": 1627371333,
      "Alert": {
        "Category": "Threat Intelligence",
        "Info": "Threat Intelligence Host Allowed Connection to Internal Network",
        "AlertUID": "bd966ac24e90168f14df453a5e1ba6a0",
        "TriggerEventID": "c459a1931fa3409bb59f9c35120021f9",
        "Reason": "EventMap.Type:Session\nEventMap.SubType:Allow\nDestination.Position:in\nBehavior.SourceIP:Threat Intelligence IPs"
      },
      "Action": {
        "Object": "183.x.225.14",
        "CoManaged": "collect",
        "FeedList": "ip"
      },
      "_es_type": "flow@alert@generic_log"
    }
  ]
}

Human Readable Output

Logsign-FetchIncident-HumanReadable-Data

logsign-get-columns-query


Gets column values based on your lucene query

Base Command

logsign-get-columns-query

Input

Argument Name Description Required
query Query Description. Required
grouped_column Group Column . Required
criteria Criteria Value. Possible values are: unique, value. Required
time_frame Time Frame (e.g 1 hour). Required

Context Output

Path Type Description
LogsignSiem.Columns Unknown Column list returned as the result of the query

Command Example

!logsign-get-columns-query criteria="value" grouped_column="Source.IP" query="*" time_frame="1 day"

Context Example

{
    "Logsign SIEM": {
        "Columns": {
            "columns": [
                "192.x.1.35",
                "192.x.1.17",
                "192.x.1.18",
                "192.x.1.5",
                "192.x.1.93"
            ]
        }
    }
}

Human Readable Output

Results

columns
192.168.1.35,
192.168.1.17,
192.168.1.18,
192.168.1.5,
192.168.1.93

logsign-get-count-query


Gets count of column values.

Base Command

logsign-get-count-query

Input

Argument Name Description Required
query Query. Required
grouped_column Grouped Column. Required
criteria Criteria Value. Possible values are: unique, value. Required
time_frame Time Frame. Required

Context Output

Path Type Description
LogsignSiem.Count Number Count number returned as the result of the query

Command Example

!logsign-get-count-query criteria="value" grouped_column="Source.IP" query="*" time_frame="1 day"

Context Example

{
    "Logsign SIEM": {
        "Count": {
            "count": 785554
        }
    }
}

Human Readable Output

Results

count
785554

Configuration parameters

  • url — Server URL (e.g. https://192.168.0.1) (required)
  • apikey — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • first_fetch — First Fetch Time (default 1 hour)
  • max_fetch — Max Fetch
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • isFetch — Fetch incidents
  • query — Query

Commands (2)

  • logsign-get-columns-query

    Query for columns.

  • logsign-get-count-query

    Get Count Query.

from datetime import datetime, timedelta

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

from CommonServerUserPython import *

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


""" CONSTANTS """

DATE_FORMAT: str = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR
DATE_FORMAT_WITH_MICROSECOND = "%Y-%m-%dT%H:%M:%S.%fZ"
LOGSIGN_INC_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
DEFAULT_FETCH_LIMIT = 50
DEFAULT_FIRST_FETCH = "1 hour"
CONTENT_TYPE_JSON = "application/json"
API_VERSION = "v1.0"
INTEGRATION_VERSION = "v1.0"

URL_SUFFIX: dict[str, str] = {
    "FETCH_INCIDENTS": "get_incidents",
    "GET_COLUMN": "get_columns",
    "GET_COUNT": "get_count",
    "TEST_API": "test_api",
}


class Client(BaseClient):
    def __init__(self, url: str, api_key: str, verify: bool, proxy: bool):
        """
        :type url: ``str``
        :param url: Base url of API Endpoint

        :type api_key: ``str``
        :param api_key: API Key

        :type verify: ``bool``
        :param verify: Whether the request should verify the SSL certificate.

        :type proxy: ``bool``
        :param proxy: Whether to run the integration using the system proxy.
        """
        self._api_key = api_key
        self._proxies = proxy
        super().__init__(base_url=url, verify=verify, proxy=self._proxies)

    def get_incidents(self, method: str, last_run: Any, query: str) -> Any:
        """
        Get-Incidents Service

        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.

        :type last_run: ``str``
        :param last_run: The greatest incident created_time we fetched from last fetch

        :type query: ``str``
        :param query: Query

        :return: Depends on the resp_type parameter
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        try:
            last_run = datetime.strftime(last_run, DATE_FORMAT)
        except Exception:
            raise ValueError("last_run type is not datetime format")
        return self._http_request(
            method=method,
            url_suffix=URL_SUFFIX["FETCH_INCIDENTS"],
            params={"api_key": self._api_key, "last_run": last_run, "query": query if query is not None else ""},
        )

    def get_query(self, method: str, query: str, url_suffix: str, grouped_column: str, criteria: str, time_frame: str):
        """
        Get-Query Service

        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.

        :type query: ``str``
        :param query: Elastic search query for LogsignSiem Search Engine.

        :type url_suffix: ``str``
        :param url_suffix: The API endpoint.

        :type grouped_column: ``str``
        :param grouped_column: GroupedColumn (e.g. Source.IP)

        :type criteria: ``str``
        :param criteria: Criteria [value or unique]

        :type time_frame: ``str``
        :param time_frame: TimeFrame [min, hour, day] (e.g 1 day)

        :return: Depends on the resp_type parameter
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        return self._http_request(
            method=method,
            url_suffix=url_suffix,
            params={
                "api_key": self._api_key,
                "query": query,
                "grouped_column": grouped_column,
                "criteria": criteria,
                "time_frame": time_frame,
            },
        )

    def test_api(self, method: str, url_suffix: str) -> Any:
        """
        Test API Service

        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.

        :type url_suffix: ``str``
        :param url_suffix: The API endpoint.

        :return: Depends on the resp_type parameter
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        return self._http_request(method=method, url_suffix=url_suffix, params={"api_key": self._api_key})


def get_datetime_now(first_fetch_time):
    """
    Get Datetime Now ISO8601 format with UTC

    """
    now = datetime.now() - timedelta(hours=int(first_fetch_time.split()[0]))
    return now.strftime(DATE_FORMAT)


def api_check_command(client: Client) -> str:
    """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.test_api("GET", URL_SUFFIX["TEST_API"])
    except Exception:
        raise ValueError("Authorization Error: Make sure Logsign Discovery API Key is correctly set")
    return "ok"


def check_arg(key: str, args: dict[str, Any]) -> Any:
    """
    Check Arg Service

    :type key: ``str``
    :param key: Check arg in dict

    :type args: ``dict``
    :param args: Args dict (e.g {key: value})

    :return: Depends on the key parameter
    :rtype: ``dict`` or ``str`` or ``list``
    """
    tmp = args.get(key, None)
    if not tmp:
        raise ValueError(f"{key} not specified!")
    return tmp


def get_generic_data(data: dict[str, Any], key: str, output_prefix: str) -> CommandResults:
    """
    Get Generic Data Service

    :type data: ``dict``
    :param data: incidents json data

    :type key: ``str``
    :param key: query service type (e.g column, count)

    :type output_prefix: ``str``
    :param output_prefix: output_prefix for HumanCommandResult (e.g Logsign.Incident, Logsign.Count)

    :rtype: ``CommandResults`
    :return CommandResults: use to return results to warroom
    """
    result = {key: check_arg(key, data)}
    return CommandResults(outputs_prefix=output_prefix, outputs=result, raw_response=json.dumps(data))


def fetch_incidents(client: Client, first_fetch: str, max_fetch: int, query: str) -> tuple[dict[str, str], list[dict]]:
    """
    This function is called for fetching incidents.

    :type client: ``Client``
    :param Client: Client object

    :type first_fetch: ``str``
    :param first_fetch: Example: "1 hour"

    :type max_fetch: ``int``
    :param max_fetch: Maximum number of incidents per fetch (Recommended less than 200)

    :type query: ``str``
    :param query: Example: Alert.AlertUID:1 Action.Object:1 ...

    :rtype: ``Tuple[Dict[str, int], List[dict]]``
    :return next_run: This will be last_run in the next fetch-incidents
    :return incidents: Incidents that will be created in Cortex XSOAR
    """
    last_run = demisto.getLastRun()
    last_fetch = last_run.get("last_fetch", None)

    if last_fetch is None:
        last_fetch = datetime.utcnow() - timedelta(hours=int(first_fetch.split()[0]))
    else:
        last_fetch = datetime.strptime(last_fetch, DATE_FORMAT)

    latest_created_time = last_fetch

    data = client.get_incidents(method="GET", last_run=last_fetch, query=query)

    incidents: list[dict[str, Any]] = []
    for incident in data["incidents"]:
        # convert the date to ISO8601
        created_at_str = f"{datetime.strptime(incident['Time']['Generated'], LOGSIGN_INC_DATE_FORMAT).isoformat()}Z"
        created_at_dt = datetime.strptime(created_at_str, DATE_FORMAT)

        inc = {"name": f"Logsign-{created_at_str}", "occured": created_at_str, "rawJSON": json.dumps(incident)}

        incidents.append(inc)
        latest_created_time = created_at_dt

        if len(incidents) >= max_fetch:
            break

    # Save the next_run as a dict with the last_fetch key to be stored
    next_run = {"last_fetch": datetime.strftime(latest_created_time, DATE_FORMAT)}
    return next_run, incidents


def get_query_command(client: Client, url_suffix: str, args: dict[str, Any]) -> CommandResults:
    """
    This function is called for query commands.

    :type client: ``Client``
    :param Client: Client object

    :type url_suffix: ``str``
    :param url_suffix: The API endpoint.

    :type args: ``dict``
    :param args: Command args

    :rtype: ``CommandResults`
    :return CommandResults: use to return results to warroom
    """
    query = check_arg("query", args)
    grouped_column = check_arg("grouped_column", args)
    criteria = check_arg("criteria", args)
    time_frame = check_arg("time_frame", args)

    response = client.get_query("GET", query, url_suffix, grouped_column, criteria, time_frame)

    result = CommandResults()
    if url_suffix == URL_SUFFIX["GET_COUNT"]:
        result = get_generic_data(response, "count", "LogsignSiem.Count")
    elif url_suffix == URL_SUFFIX["GET_COLUMN"]:
        result = get_generic_data(response, "columns", "LogsignSiem.Columns")
    return result


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """

    command = demisto.command()
    demisto.info(f"[Logsign] Command being called is {command}")

    try:
        params = demisto.params()
        base_url = params.get("url")

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

        first_fetch = params.get("first_fetch")
        first_fetch_time = first_fetch if first_fetch else DEFAULT_FIRST_FETCH

        api_key = params.get("apikey")
        query = params.get("query", "")

        max_fetch = params.get("max_fetch")
        max_fetch = DEFAULT_FETCH_LIMIT if not params.get("max_fetch") else int(max_fetch)

        client = Client(url=base_url, api_key=api_key, verify=verify_certificate, proxy=proxy)

        args = demisto.args()

        if command == "fetch-incidents":
            last_run, incidents = fetch_incidents(client, first_fetch_time, max_fetch, query)
            demisto.setLastRun(last_run)
            demisto.incidents(incidents)
        elif command == "logsign-get-columns-query":
            return_results(get_query_command(client, URL_SUFFIX["GET_COLUMN"], args))
        elif command == "logsign-get-count-query":
            return_results(get_query_command(client, URL_SUFFIX["GET_COUNT"], args))
        elif command == "test-module":
            result = api_check_command(client)
            return_results(result)
    except Exception as e:
        demisto.error(traceback.format_exc())
        return_error(f"Error: {e!s}")


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