Exabeam Data Lake

Exabeam Data Lake provides a searchable log management system. Data Lake is used for log collection, storage, processing, and presentation.

Analytics & SIEM · ExabeamDataLake

Details

IDExabeam Data Lake
ProviderExabeam
CategoryAnalytics & SIEM
From Version6.10.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Exabeam Data Lake provides a searchable log management system.
Data Lake is used for log collection, storage, processing, and presentation.
This integration was integrated and tested with version LMS-i40.3 of Exabeam Data Lake.

Configure Exabeam Data Lake in Cortex

Parameter Description Required
Server URL   True
User Name   True
Password   True
Cluster Name The default value is usually ‘local’, suitable for standard setups. For custom cluster deployments, consult Exabeam Support Team. True
Trust any certificate (not secure)    
Use system proxy settings    

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.

exabeam-data-lake-search


Get events from Exabeam Data Lake.

Base Command

exabeam-data-lake-search

Input

Argument Name Description Required
query The search query string to filter the events by. Examples can be found in the syntax documentation section of the integration description. Required
start_time The starting date for the search range. The search range should be at least one day long and can extend up to a maximum of 10 days. Required
end_time The ending date for the search range. This defines the end of the search range, which should be within one to ten days after the start_time. Required
limit The maximal number of results to return. Maximum value is 3000. Optional
page The page number for pagination. Optional
page_size The maximal number of results to return per page. Maximum value is 3000. Optional

Context Output

Path Type Description
ExabeamDataLake.Event._id str The event ID.
ExabeamDataLake.Event._source.Vendor str Vendor of the event.
ExabeamDataLake.Event._source.Product str Product of the event.
ExabeamDataLake.Event._source.@timestamp str The time of the event.
ExabeamDataLake.Event._source.message str The message of the event.

Command example

!exabeam-data-lake-search query="risk_score:3" start_time="2024.02.27" end_time="2024.02.28" limit=2

Context Example

{
    "ExabeamDataLake": {
        "Event": [
            {
                "_id": "some_id",
                "_index": "exabeam-2024.02.28",
                "_routing": "SfA86vqw",
                "_score": null,
                "_source": {
                    "@timestamp": "2024-02-28T16:15:50.614Z",
                    "@version": "1",
                    "Product": "Exabeam AA",
                    "Vendor": "Exabeam",
                    "data_type": "exabeam-security-alert",
                    "exa_activity_type": [
                        "alert/security",
                        "alert"
                    ],
                    "exa_adjustedEventTime": "2024-02-28T16:15:29.000Z",
                    "exa_category": "Exabeam Alerts",
                    "exa_device_type": [
                        "security"
                    ],
                    "exa_rawEventTime": "2024-02-28T16:15:29.000Z",
                    "indexTime": "2024-02-28T16:15:51.626Z",
                    "is_ransomware_src_ip": false,
                    "is_threat_src_ip": false,
                    "is_tor_src_ip": false,
                    "log_type": "dlp-alert",
                    "message": "<86>1 2024-02-28T16:15:50.609Z exabeam-analytics-master Exabeam - - - timestamp=\"2024-02-28T16:15:29.192Z\" score=\"3\" user=\"ghardin\" event_time=\"2024-02-28 14:35:35\" event_type=\"dlp-alert\" domain=\"kenergy\" time=\"1709130935833\" source=\"ObserveIT\" vendor=\"ObserveIT\" lockout_id=\"NA\" session_id=\"ghardin-20240228143533\" session_order=\"2\" account=\"ghardin\" getvalue('zone_info', src)=\"new york office\" alert_name=\" rule violation\" local_asset=\"lt-ghardin-888\" alert_type=\"DATA EXFILTRATION\" os=\"Win\" rule_name=\"Abnormal DLP alert name for user\" rule_description=\"Exabeam noted that this alert name has been triggered for this user in the past yet it is still considered abnormal activity. This activity may be an early indication of compromise of a user by malware or other malicious actors.\" rule_reason=\"Abnormal DLP alert with name  rule violation for user\" ",
                    "port": 41590,
                    "risk_score": "3",
                    "rule_description": "Exabeam noted that this alert name has been triggered for this user in the past yet it is still considered abnormal activity. This activity may be an early indication of compromise of a user by malware or other malicious actors.",
                    "rule_name": "Abnormal DLP alert name for user",
                    "score": "3",
                    "session_id": "ghardin-20240228143533",
                    "time": "2024-02-28T16:15:29.000Z",
                    "user": "ghardin"
                },
                "_type": "logs",
                "sort": [
                    1709136950614
                ]
            },
            {
                "_id": "another_id",
                "_index": "exabeam-2024.02.27",
                "_routing": "XUXxevyv",
                "_score": null,
                "_source": {
                    "@timestamp": "2024-02-27T16:21:45.721Z",
                    "@version": "1",
                    "Product": "Exabeam AA",
                    "Vendor": "Exabeam",
                    "data_type": "exabeam-security-alert",
                    "event_code": "4768",
                    "exa_activity_type": [
                        "alert/security",
                        "alert"
                    ],
                    "exa_adjustedEventTime": "2024-02-24T16:16:29.000Z",
                    "exa_category": "Exabeam Alerts",
                    "exa_device_type": [
                        "security"
                    ],
                    "exa_rawEventTime": "2024-02-24T16:16:29.000Z",
                    "host": "exabeamdemodc1",
                    "indexTime": "2024-02-27T16:23:56.271Z",
                    "is_ransomware_dest_ip": false,
                    "is_threat_dest_ip": false,
                    "is_tor_dest_ip": false,
                    "log_type": "kerberos-logon",
                    "message": "<86>1 2024-02-27T16:21:45.539Z exabeam-analytics-master Exabeam - - - timestamp=\"2024-02-24T16:16:29.975Z\" id=\"ghardin-20240224140716\" score=\"3\" user=\"ghardin\" event_time=\"2024-02-24 14:34:42\" event_type=\"kerberos-logon\" host=\"exabeamdemodc1\" domain=\"ktenergy\" time=\"1708785282052\" source=\"DC\" lockout_id=\"NA\" session_id=\"ghardin-20240224140716\" session_order=\"4\" account=\"ghardin\" ticket_options_encryption=\"0x40810010:0x12\" nonmachine_user=\"ghardin\" event_code=\"4768\" ticket_encryption_type=\"0x12\" ticket_options=\"0x40810010\" rule_name=\"IT presence without badge access\" rule_description=\"This user is logged on to the company network but did not use their badge to access a physical location. It is unusual to have IT access without badge access.\" rule_reason=\"IT presence without badge access\" ",
                    "port": 56920,
                    "risk_score": "3",
                    "rule_description": "This user is logged on to the company network but did not use their badge to access a physical location. It is unusual to have IT access without badge access.",
                    "rule_name": "IT presence without badge access",
                    "score": "3",
                    "session_id": "ghardin-20240224140716",
                    "time": "2024-02-24T16:16:29.000Z",
                    "user": "ghardin"
                },
                "_type": "logs",
                "sort": [
                    1709050905721
                ]
            }
        ]
    }
}

Human Readable Output

Logs

Created_at Id Message Product Vendor
2024-02-28T16:15:50.614Z some_id <86>1 2024-02-28T16:15:50.609Z exabeam-analytics-master Exabeam - - - timestamp=”2024-02-28T16:15:29.192Z” id=”ghardin-20240228143533” score=”3” user=”ghardin” event_time=”2024-02-28 14:35:35” event_type=”dlp-alert” domain=”kenergy” time=”1709130935833” source=”ObserveIT” vendor=”ObserveIT” lockout_id=”NA” session_id=”ghardin-20240228143533” session_order=”2” account=”ghardin” getvalue(‘zone_info’, src)=”new york office” alert_name=” rule violation” local_asset=”lt-ghardin-888” alert_type=”DATA EXFILTRATION” os=”Win” rule_name=”Abnormal DLP alert name for user” rule_description=”Exabeam noted that this alert name has been triggered for this user in the past yet it is still considered abnormal activity. This activity may be an early indication of compromise of a user by malware or other malicious actors.” rule_reason=”Abnormal DLP alert with name rule violation for user” Exabeam AA Exabeam
2024-02-27T16:21:45.721Z another_id <86>1 2024-02-27T16:21:45.539Z exabeam-analytics-master Exabeam - - - timestamp=”2024-02-24T16:16:29.975Z” id=”ghardin-20240224140716” score=”3” user=”ghardin” event_time=”2024-02-24 14:34:42” event_type=”kerberos-logon” host=”exabeamdemodc1” domain=”ktenergy” time=”1708785282052” source=”DC” lockout_id=”NA” session_id=”ghardin-20240224140716” session_order=”4” account=”ghardin” ticket_options_encryption=”0x40810010:0x12” nonmachine_user=”ghardin” event_code=”4768” ticket_encryption_type=”0x12” ticket_options=”0x40810010” rule_name=”IT presence without badge access” rule_description=”This user is logged on to the company network but did not use their badge to access a physical location. It is unusual to have IT access without badge access.” rule_reason=”IT presence without badge access” Exabeam AA Exabeam

Configuration parameters

  • url — Server URL (required)
  • credentials — User Name (required)
  • cluster_name — Cluster Name (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • exabeam-data-lake-search

    Get events from Exabeam Data Lake.

import demistomock as demisto
import urllib3
from CommonServerPython import *

from CommonServerUserPython import *

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """

ISO_8601_FORMAT = "%Y-%m-%d"


""" CLIENT CLASS """


class Client(BaseClient):
    """
    Client to use in the Exabeam DataLake integration. Overrides BaseClient
    """

    def __init__(self, base_url: str, username: str, password: str, verify: bool, proxy: bool):
        super().__init__(base_url=f"{base_url}", verify=verify, proxy=proxy, timeout=20)
        self.username = username
        self.password = password

        self._login()

    def _login(self):
        """
        Logs in to the Exabeam API using the provided username and password.
        This function must be called before any other API calls.
        """
        data = {"username": self.username, "password": self.password}
        self._http_request(
            "POST",
            full_url=f"{self._base_url}/api/auth/login",
            headers={"Accept": "application/json", "Csrf-Token": "nocheck"},
            data=data,
        )

    def _logout(self) -> None:
        """
        The _logout method initiates a logout request, utilizing a GET HTTP request to the specified endpoint for
        user session termination.
        """
        self._http_request("GET", full_url=f"{self._base_url}/api/auth/logout")

    def test_module_request(self):
        """
        Performs basic get request to check if the server is reachable.
        """
        self._http_request("GET", full_url=f"{self._base_url}/api/auth/check", resp_type="text")

    def query_datalake_request(
        self, args: dict, from_param: int, size_param: int, cluster_name: str, dates_in_format: list
    ) -> dict:
        """
        Queries the Exabeam Data Lake API with the provided search query and returns the response.
        """
        search_query = {
            "sortBy": [{"field": "@timestamp", "order": "desc", "unmappedType": "date"}],
            "query": args.get("query", "*"),
            "from": from_param,
            "size": size_param,
            "clusterWithIndices": [
                {
                    "clusterName": cluster_name,
                    "indices": dates_in_format,
                }
            ],
        }
        return self._http_request(
            "POST",
            full_url=f"{self._base_url}/dl/api/es/search",
            data=json.dumps(search_query),
            headers={"Content-Type": "application/json", "Csrf-Token": "nocheck"},
        )


""" HELPER FUNCTIONS """


def _parse_entry(entry: dict) -> dict:
    """
    Parse a single entry from the API response to a dictionary.

    Args:
        entry: The entry from the API response.

    Returns:
        dict: The parsed entry dictionary.
    """
    source: dict = entry.get("_source", {})
    return {
        "Id": entry.get("_id"),
        "Vendor": source.get("Vendor"),
        "Created_at": source.get("@timestamp"),
        "Product": source.get("Product"),
        "Message": source.get("message"),
    }


def dates_in_range(start_time: Any, end_time: Any) -> list[str]:
    """
     Generate a list of dates within a specified range.

    Args:
        start_time: The start date of the range in the format "YYYY-MM-DD".
        end_time: The end date of the range in the format "YYYY-MM-DD".

    Raises:
        DemistoException: If the start time is not before the end time, or if the difference between start time and end time is
        greater than 10 days.

    Returns:
        list: A list of dates within the specified range, formatted as strings in the format "YYYY.MM.DD".
    """
    start_time = datetime.strptime(start_time, "%Y-%m-%d")
    end_time = datetime.strptime(end_time, "%Y-%m-%d")

    if start_time >= end_time:
        raise DemistoException("Start time must be before end time.")

    if (end_time - start_time).days > 10:
        raise DemistoException("Difference between start time and end time must be less than or equal to 10 days.")

    dates = []
    current_date = start_time
    while current_date <= end_time:
        dates.append(current_date.strftime("%Y.%m.%d"))
        current_date += timedelta(days=1)

    return dates


def get_date(time: str, arg_name: str):
    """
    Get the date from a given time string.

    Args:
        time (str): The time string to extract the date from.

    Returns:
        str: The date extracted from the time string formatted in ISO 8601 format (YYYY-MM-DD),
        or None if the time string is invalid.
    """
    date_time = arg_to_datetime(arg=time, arg_name=arg_name, required=True)
    if not date_time:
        raise DemistoException(f"There was an issue parsing the {arg_name} provided.")
    date = date_time.strftime(ISO_8601_FORMAT)
    return date


def get_limit(args: dict, arg_name: str) -> int:
    """
    Get the limit value specified in the arguments.

    Args:
        args: A dictionary containing the 'limit' argument.

    Returns:
        int: The limit value if specified and less than or equal to 3000; otherwise, returns 3000 as the maximum limit.
        If the 'limit' argument is not present in the dictionary or is None, returns 50 as the default limit.
    """
    if limit := args.get(arg_name):
        return min(int(limit), 3000)

    return 50


def calculate_page_parameters(args: dict) -> tuple[int, int]:
    """
      Calculate the page parameters for pagination.

    Args:
        args: A dictionary containing the arguments passed to the function.

    Raises:
        DemistoException: If invalid combinations of arguments are provided. You can only provide 'limit'
        alone or 'page' and 'page_size' together.

    Returns:
        tuple: A tuple containing two integers representing the 'from' and 'size' parameters for pagination.
        'from' is the index of the first item to retrieve, and 'size' is the number of items to retrieve.
    """
    page_arg = args.get("page")
    page_size_arg = args.get("page_size")
    limit_arg = args.get("limit")

    if (limit_arg and (page_arg or page_size_arg)) or ((not (page_arg and page_size_arg)) and (page_arg or page_size_arg)):
        raise DemistoException("You can only provide 'limit' alone or 'page' and 'page_size' together.")

    if page_arg and page_size_arg:
        page = arg_to_number(args.get("page", "1"))
        page_size = get_limit(args, "page_size")
        if page == 0 or page_size == 0:
            raise DemistoException("Both 'page' and 'page_size' must be greater than 0.")
        if page and page_size:
            if page < 0 or page_size < 0:
                raise DemistoException("Both 'page' and 'page_size' must be greater than 0.")
            from_param = page * page_size - page_size
            size_param = page_size
        else:
            from_param = 0
            size_param = 0
            demisto.debug(f"{from_param=} {size_param=}")
    else:
        from_param = 0
        size_param = get_limit(args, "limit")

    return from_param, size_param


""" COMMAND FUNCTIONS """


def query_data_lake_command(client: Client, args: dict, cluster_name: str) -> CommandResults:
    """
    Query the datalake command and return the results in a formatted table.

    Args:
        client: The client object for interacting with the API.
        args: The arguments passed to the command.

    Returns:
        CommandResults: The command results object containing outputs and readable output.
    """
    from_param, size_param = calculate_page_parameters(args)

    start_time = get_date(args.get("start_time", "7 days ago"), "start_time")
    end_time = get_date(args.get("end_time", "today"), "end_time")
    dates = dates_in_range(start_time, end_time)
    dates_in_format = ["exabeam-" + date for date in dates]

    response = client.query_datalake_request(args, from_param, size_param, cluster_name, dates_in_format).get("responses", [{}])

    data_response = response[0].get("hits", {}).get("hits", [])

    human_readable = [_parse_entry(entry) for entry in data_response]

    return CommandResults(
        outputs_prefix="ExabeamDataLake.Event",
        outputs=data_response,
        readable_output=tableToMarkdown(
            name="Logs", t=human_readable, headers=["Id", "Vendor", "Product", "Created_at", "Message"]
        ),
    )


def test_module(client: Client):  # pragma: no cover
    """test function

    Args:
        client: Client

    Returns:
        ok if successful
    """
    client.test_module_request()
    return "ok"


""" MAIN FUNCTION """


def main() -> None:  # pragma: no cover
    params = demisto.params()
    args = demisto.args()
    command = demisto.command()

    credentials: dict = params.get("credentials", {})
    username = credentials.get("identifier", "")
    password = credentials.get("password", "")
    base_url: str = params.get("url", "")
    verify_certificate = not params.get("insecure", False)
    proxy = params.get("proxy", False)
    cluster_name = params.get("cluster_name", "local")
    client = None

    try:
        client = Client(base_url.rstrip("/"), username=username, password=password, verify=verify_certificate, proxy=proxy)

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

        if command == "test-module":
            return_results(test_module(client))
        elif command == "exabeam-data-lake-search":
            return_results(query_data_lake_command(client, args, cluster_name))
        else:
            raise NotImplementedError(f"Command {command} is not supported")

    except Exception as e:
        demisto.info(str(e))
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}")

    finally:
        if client:
            client._logout()


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