diff --git a/Logos/CognyteLuminar.svg b/Logos/CognyteLuminar.svg new file mode 100644 index 00000000000..8a2cee8630a --- /dev/null +++ b/Logos/CognyteLuminar.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar.zip b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar.zip new file mode 100644 index 00000000000..66b0e8eb40c Binary files /dev/null and b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar.zip differ diff --git a/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/__init__.py b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/__init__.py new file mode 100644 index 00000000000..c3b4e5b2d5f --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/__init__.py @@ -0,0 +1,737 @@ +""" +init.py + +This module provides a set of functions for communicating with the Luminar +API. The main purpose of these functions is +to retrieve and process security indicators and leaked credentials from +Luminar, transform them into a compatible format +, and save them into Azure Sentinel. + +Main components: + +LuminarManager: This class manages interactions with the Luminar API. It is +responsible for requesting and refreshing +access tokens, as well as managing the connection status. + +process_malware, enrich_malware_items, enrich_incident_items: These functions +process malware and incident items and +create enriched data for each. + +create_data, get_static_data: These functions transform raw indicators into a +format compatible with Azure Sentinel. + +luminar_api_fetch: This function fetches data from the Luminar API, processes +the fetched items, and manages +relationships between items. + +main: This function handles Luminar API requests. It initializes the Luminar +manager, requests and refreshes access +tokens, manages Luminar API calls, and handles pagination to ensure all pages +of data are retrieved. A timer trigger +allows it to run at specified intervals. + +This module is designed for use as part of an Azure Function App and requires +certain environment variables to be set. +""" + +import re +import requests +from ipaddress import ip_network +from os import environ +from datetime import datetime, timedelta, timezone +from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union +import logging +import azure.functions as func +from .state_manager import StateManager + +tenant_id = environ.get("TenantID") +client_id = environ.get("ApplicationID") +client_secret = environ.get("ClientSecret") +limit = environ.get("Limit", 10) +luminar_client_id = environ.get("LuminarAPIClientID") +luminar_client_secret = environ.get("LuminarAPIClientSecret") +luminar_account_id = environ.get("LuminarAPIAccountID") +state = StateManager(environ.get("AzureWebJobsStorage")) +session = requests.Session() + + +TIMEOUT = 60.0 +# There's a limit of 100 tiIndicators per request. +MAX_TI_INDICATORS_PER_REQUEST = 100 +ENDPOINT = ( + "https://graph.microsoft.com/beta/security/tiIndicators/submitTiIndicators" +) +LUMINAR_BASE_URL = "https://demo.cyberluminar.com/" +SCOPE = "https://graph.microsoft.com/.default" + + +TOKEN_ENDPOINT = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" +STIX_PARSER = re.compile( + r"([\w-]+?):(\w.+?) (?:[!><]?=|IN|MATCHES|LIKE) '(.*?)' *[" r"OR|AND|FOLLOWEDBY]?" +) +IOC_MAPPING = { + "file:hashes.'SHA-1'": "SHA1", + "file:hashes.MD5": "MD5", + "file:hashes.'SHA-256'": "SHA256", + "file:hashes.'SHA-512'": "SHA512", + "ipv4-addr": "IP", + "file:name": "File_Extension", + "file:size": "File_Size", + "url": "URL", + "email-addr": "EMAIL", + "domain-name": "DOMAIN", + "ipv6-addr": "IP", + "mac-addr": "MAC", + "directory": "DIR", + "mutex": "MUTEX", + "windows-registry-key": "WINDOWS_REGISTRY_KEY", +} + +HEADERS = { + "Content-Type": "application/x-www-form-urlencoded", + "accept": "application/json", +} +PAYLOAD = { + "grant_type": "client_credentials", + "client_id": client_id, + "client_secret": client_secret, + "scope": SCOPE, +} + + +def get_last_saved_timestamp(date_format="%Y-%m-%d %H:%M:%S"): + """ + This function retrieves the last saved timestamp from the state. If no + timestamp is found, it returns 0. + + Parameters: + date_format (str): The format in which the date and time are represented. + Default is '%Y-%m-%d %H:%M:%S' which + represents YYYY-MM-DD HH:MM:SS format. + + Returns: + int: The timestamp of the last successful run of this function as a Unix + timestamp. If the function is being run for + the first time, it returns 0. + """ + last_run_date_time = state.get() + logging.debug("last saved time stamp is %s", last_run_date_time) + + return ( + int(datetime.strptime(last_run_date_time, date_format).timestamp()) + if last_run_date_time + else 0 + ) + + +def save_checkpoint(timestamp: datetime, date_format: str = "%Y-%m-%d %H:%M:%S") -> None: + """ + This function saves the current UTC timestamp to the state. + + Parameters: + date_format (str): The format in which the date and time are represented. + Default is '%Y-%m-%d %H:%M:%S' + which represents the format as YYYY-MM-DD HH:MM:SS. + + Returns: + None + """ + current_date_time = timestamp + state.post(format(current_date_time, date_format)) + + +def get_access_token() -> Optional[str]: + """ + This function fetches the access token from Sentinel. + Returns: + str: The access token if the request was successful, None otherwise. + """ + try: + response = session.post( + TOKEN_ENDPOINT, data=PAYLOAD, headers=HEADERS, timeout=TIMEOUT + ) + response.raise_for_status() # check if we got a HTTP error + return response.json().get("access_token") + except requests.HTTPError as http_err: + logging.error("HTTP error occurred: %s", http_err) + return None + + +def save_sentinel_data(data: dict) -> None: + """ + This function sends the data to Sentinel using an access token. + Parameters: + data (dict): The data to be sent to Sentinel. + Returns: + None + """ + access_token = get_access_token() + if not access_token: + logging.error("Unable to retrieve access token") + return + + headers = {"Authorization": f"Bearer {access_token}", + "Content-Type": "application/json", "accept": "application/json"} + + try: + response = session.post(ENDPOINT, headers=headers, json=data, timeout=TIMEOUT) + response.raise_for_status() # check if we got a HTTP error + except requests.HTTPError as http_err: + logging.error("HTTP error occurred: %s", http_err) + + +def get_static_data( + indicator: Dict[str, Union[str, int, list, Dict[str, Any]]], value: str +) -> Dict[str, Union[str, int, list]]: + """ + Generate static data dictionary based on the input indicator and value. + + Parameters: + indicator (Dict[str, Union[str, int, list, Dict[str, Any]]]): A + dictionary containing + indicator information. + value (str): A string value indicating the type of the indicator. + + Returns: + Dict[str, Union[str, int, list]]: A dictionary of static data. + """ + return { + "action": "alert" if value == "INCIDENT" else "block", + "description": indicator["name"], + "expirationDateTime": indicator.get("valid_until") + if indicator.get("valid_until") + else (datetime.utcnow() + timedelta(days=30)).isoformat() + "Z", + "externalId": indicator["id"], + "threatType": "Phishing" if value == "INCIDENT" else "Malware", + "killChain": [], + "severity": 3 + if value == "INCIDENT" + else (4 if "Known Malicious IPs" in indicator["name"] else 3), + "tags": ["Luminar Leaked Credentials"] + if value == "INCIDENT" + else ["Luminar IOCs"], + "targetProduct": "Azure Sentinel", + "tlpLevel": "green", + "malwareFamilyNames": indicator.get("malware_details", {}).get( + "malwareTypes", [] + ), + "confidence": 80 + if value == "INCIDENT" + else (90 if "Known Malicious IPs" in indicator["name"] else 80), + "SourceSystem": "Cognyte Luminar", + } + + +def get_network_address(value: str) -> str: + """ + Get the network address from a given CIDR value. + + Args: + value: A string representing a CIDR notation (e.g., '192.168.0.0/24'). + + Returns: + The network address as a string. + + If the given value is not a valid CIDR notation, the original value is + returned. + + Example: + >>> get_network_address('192.168.0.0/24') + '192.168.0.0' + >>> get_network_address('192.168.0.1') + '192.168.0.1' + """ + try: + network = ip_network(value) + return str(network.network_address) + except ValueError: + return value # Return the original value if it's not a CIDR + + +def create_data( + indicator: Dict[str, Union[str, int, list, Dict[str, Any]]], indicator_type: str +) -> Optional[Dict[str, Union[str, int, list]]]: + """ + Create a data dictionary based on the given indicator and its type. + + Parameters: + indicator (Dict[str, Union[str, int, list, Dict[str, Any]]]): A + dictionary containing + indicator information. + indicator_type (str): A string indicating the type of the indicator. + + Returns: + Dict[str, Union[str, int, list]]: A dictionary of data corresponding to + the given + indicator and its type. + + Raises: + Exception: Any exception thrown during the processing of the data is + caught and logged. + """ + try: + handlers: Dict[str, Callable[[], Dict[str, str]]] = { + "URL": lambda: {"url": indicator["indicator_value"]}, + "IP": lambda: { + "networkIPv4": get_network_address(indicator["indicator_value"]) + }, + "MD5": lambda: { + "fileHashValue": indicator["indicator_value"], + "fileHashType": "md5", + }, + "SHA1": lambda: { + "fileHashValue": indicator["indicator_value"], + "fileHashType": "sha1", + }, + "SHA256": lambda: { + "fileHashValue": indicator["indicator_value"], + "fileHashType": "sha256", + }, + "SHA512": lambda: { + "fileHashValue": indicator["indicator_value"], + "fileHashType": "sha512", + }, + "EMAIL": lambda: {"emailSenderAddress": indicator["indicator_value"]}, + "INCIDENT": lambda: {"emailSenderAddress": indicator["account_login"]}, + "DOMAIN": lambda: {"domainName": indicator["indicator_value"]}, + "DIR": lambda: {"filePath": indicator["indicator_value"]}, + "File_Size": lambda: {"fileSize": indicator["indicator_value"]}, + "File_Extension": lambda: {"filePath": indicator["indicator_value"]}, + "MUTEX": lambda: {"fileMutexName": indicator["indicator_value"]}, + } + + handler = handlers.get(indicator_type) + if not handler: + logging.warning("No handler for indicator type %s", indicator_type) + return {} + + data = get_static_data(indicator, indicator_type) + data.update(handler()) + return data + + except KeyError as err: + logging.error("Missing key in indicator: %s", err) + except Exception as err: + logging.error( + "Unexpected error occurred while creating azure sentinel IOC " + "Format: %s: %s", + type(err).__name__, + err, + ) + + return {} + + +def chunks(data: List[Any], size: int) -> Generator[List[Any], None, None]: + """ + Splits the provided data into chunks of the specified size. + This is a generator function that yields chunks of data as lists. + + Parameters: + data (List[Any]): The data to be chunked. + size (int): The size of each chunk. + + Yields: + List[Any]: A chunk of the original data. + """ + for i in range(0, len(data), size): + yield data[i : i + size] + + +def luminar_api_fetch(all_objects: List[Dict[str, Any]]) -> None: + """ + Fetches data from Luminar API, identifies relationships between different + objects, + and enriches malware and incident items with additional data based on + these relationships. + + Parameters: + all_objects: A list of dictionary objects fetched from the Luminar API. + Each dictionary represents + an object and contains its various attributes. + + Returns: + None + """ + try: + luminar_expiration_iocs(all_objects) + item_by_id, relationships = process_objects(all_objects) + enrich_items(item_by_id, relationships) + except (ValueError, TypeError, KeyError) as err: + logging.error("Error while fetching and processing data: %s", err) + + +def process_objects( + all_objects: List[Dict[str, Any]] +) -> Tuple[Dict[str, Any], Dict[str, List[str]]]: + """ + Processes all objects and establishes relationships. + + Parameters: + all_objects: A list of dictionary objects fetched from the Luminar API. + + Returns: + Tuple containing dictionary of items indexed by id and relationships + dictionary. + """ + item_by_id = {} + relationships = {} + + for item in all_objects: + item_by_id[item["id"]] = item + if item.get("type") == "relationship": + relationships.setdefault(item["target_ref"], []).append(item["source_ref"]) + + return item_by_id, relationships + + +def enrich_items( + item_by_id: Dict[str, Any], relationships: Dict[str, List[str]] +) -> None: + """ + Enriches malware and incident items. + + Parameters: + item_by_id: Dictionary of items indexed by id. + relationships: Dictionary of relationships. + + Returns: + None + """ + for key, group in relationships.items(): + parent = item_by_id.get(key) + if parent: + children = [ + item_by_id.get(item_id) for item_id in group if item_by_id.get(item_id) + ] + if not children: + continue + if parent.get("type") == "malware": + enrich_malware_items(parent, children) + elif parent.get("type") == "incident": + enrich_incident_items(parent, children) + + +def luminar_expiration_iocs(all_objects: List[Dict[str, Any]]) -> None: + """ + Enriches and processes unique Indicators of Compromise (IoCs) that have + an expiration date + that is greater than or equal to the current date. + + Parameters: + all_objects: A list containing dictionaries of various objects such as + malware, incidents, indicators etc. + + Returns: + None + """ + iocs = [ + x + for x in all_objects + if x.get("type") == "indicator" + and x.get("valid_until") + and datetime.strptime((x.get("valid_until"))[:19], "%Y-%m-%dT%H:%M:%S") + >= datetime.today() + ] + enrich_malware_items({}, iocs) + + +def field_mapping(ind: str, value: str) -> dict: + """ + Assigning associated indicator type and indicator value + :param ind: {str} indicator type + :param value: {str} indicator value + :return: {dict} + """ + return {"indicator_type": ind, "indicator_value": value} + + +IndicatorTypes = { + "file:hashes.'SHA-1'": field_mapping, + "file:hashes.MD5": field_mapping, + "file:hashes.'SHA-256'": field_mapping, + "file:hashes.'SHA-512'": field_mapping, + "ipv4-addr": field_mapping, + "file:name": field_mapping, + "file:size": field_mapping, + "url": field_mapping, + "email-addr": field_mapping, + "domain-name": field_mapping, + "ipv6-addr": field_mapping, + "mac-addr": field_mapping, + "directory": field_mapping, + "mutex": field_mapping, + "windows-registry-key": field_mapping, +} + + +def process_malware(children: Dict[str, Any], parent: Dict[str, Any]) -> None: + """ + Process a malware item and enrich it with parent item's data. + + Parameters: + children (Dict[str, Any]): A dictionary representing a child object. + parent (Dict[str, Any]): A dictionary representing the parent object. + + Returns: + None + """ + pattern = children.get("pattern") + if isinstance(pattern, str): + try: + matches = STIX_PARSER.findall(pattern) + except TypeError as err: + logging.error("Error on pattern: %s and %s", pattern, err) + return + for match in matches: + stix_type, stix_property, value = match + indicator_type = ( + f"{stix_type}:{stix_property}" if stix_type == "file" else stix_type + ) + if indicator_type and IOC_MAPPING.get(indicator_type): + mapping_method = IndicatorTypes.get(indicator_type) + ioc = mapping_method(IOC_MAPPING[indicator_type], value) + children.update( + { + "malware_details": parent, + "indicator_type": ioc["indicator_type"], + "indicator_value": ioc["indicator_value"], + } + ) + parent["name"] = children["name"] + else: + logging.error( + "Unexpected pattern type: %s in children: %s", type(pattern), children + ) + + +def enrich_malware_items( + parent: Dict[str, Any], childrens: List[Dict[str, Any]] +) -> None: + """ + Enrich malware items with the data from the parent object. + + Parameters: + parent (Dict[str, Any]): A dictionary representing the parent object. + childrens (List[Dict[str, Any]]): A list of dictionaries each + representing a child object. + + Returns: + None + """ + batch_data = [] + for children in childrens: + process_malware(children, parent) + try: + if "indicator_type" not in children: + continue + chunk_data = create_data(children, children["indicator_type"]) + if not chunk_data: + continue + batch_data.append(chunk_data) + except (KeyError, TypeError) as err: + logging.error("Error while creating data: %s", err) + save_batch_data(batch_data) + + +def enrich_incident_items( + parent: Dict[str, Any], childrens: List[Dict[str, Any]] +) -> None: + """ + Enrich incident items with the data from the parent object. + + Parameters: + parent (Dict[str, Any]): A dictionary representing the parent object. + childrens (List[Dict[str, Any]]): A list of dictionaries each + representing a child object. + + Returns: + None + """ + batch_data = [] + for children in childrens: + children.update(parent) + try: + chunk_data = create_data(children, "INCIDENT") + if not chunk_data: + continue + batch_data.append(chunk_data) + except (KeyError, TypeError) as err: + logging.error("Error while creating data: %s", err) + save_batch_data(batch_data) + + +def save_batch_data(batch_data: List[Dict[str, Any]]) -> None: + """ + Save batch data in chunks. + + Parameters: + batch_data (List[Dict[str, Any]]): A list of dictionaries each + representing an item of batch data. + + Returns: + None + """ + for chunk in chunks(batch_data, MAX_TI_INDICATORS_PER_REQUEST): + try: + save_sentinel_data({"value": chunk}) + except Exception as err: + logging.error("Error while saving sentinel data: %s", err) + + +class LuminarManager: + """ + Class to manage Luminar API interactions. + """ + + STATUS_MESSAGES = { + 400: "Bad request. The server could not understand the request due to " + "invalid syntax.", + 401: "Unauthorized. The client must authenticate itself to get the " + "requested response.", + 403: "Forbidden. The client does not have access rights to the " "content.", + 404: "Not Found. The server can not find the requested resource.", + 408: "Request Timeout. The server would like to shut down this unused " + "connection.", + 429: "Too Many Requests. The user has sent too many requests in a " + "given amount of time.", + 500: "Internal Server Error. The server has encountered a situation " + "it doesn't know how to handle.", + 502: "Bad Gateway. The server was acting as a gateway or proxy and " + "received an invalid response from the " + "upstream server.", + 503: "Service Unavailable. The server is not ready to handle the " "request.", + } + + def __init__( + self, + cognyte_client_id: str, + cognyte_client_secret: str, + cognyte_account_id: str, + cognyte_base_url: str, + ) -> None: + self.base_url = cognyte_base_url + self.account_id = cognyte_account_id + self.client_id = cognyte_client_id + self.client_secret = cognyte_client_secret + self.payload = { + "client_id": self.client_id, + "client_secret": self.client_secret, + "grant_type": "client_credentials", + } + self.req_headers = HEADERS + + def access_token(self) -> Tuple[Union[bool, str], str]: + """ + Make a request to the Luminar API. + + :return: Tuple[Union[bool, str], str] + The access token (if successful) or False (if unsuccessful), + and a message indicating the status of the + request. + """ + req_url = f"{self.base_url}/externalApi/realm/{self.account_id}/token" + response = None + try: + response = requests.post( + req_url, headers=self.req_headers, data=self.payload, timeout=TIMEOUT + ) + response.raise_for_status() + return ( + response.json().get("access_token", False), + "Luminar API Connected successfully", + ) + except requests.HTTPError: + if response is not None: + return False, self.STATUS_MESSAGES.get( + response.status_code, "An error occurred" + ) + return False, "An error occurred while making HTTP request" + except Exception as err: + return False, f"Failed to connect to Luminar API... Error is {err}" + + +def main(mytimer: func.TimerRequest) -> None: + """ + Main function to handle Luminar API requests. + + This function initializes the Luminar manager, requests and refreshes + access tokens, handles Luminar API calls, + and handles pagination to ensure all pages of data are retrieved. It uses + a timer trigger to run at specified + intervals. + + :param mytimer: func.TimerRequest, timer for triggering the function at + specified intervals. + + Note: + - This function will log an error message and exit if an access token + cannot be retrieved or refreshed. + - If no more indicators are left to ingest, the function will log an + informational message and stop iterating over + the pages. + - The function will save a timestamp after each run. + """ + + if mytimer.past_due: + logging.info("The timer is past due!") + return + luminar_manager = LuminarManager( + luminar_client_id, luminar_client_secret, luminar_account_id, LUMINAR_BASE_URL + ) + + # Define the params with the timestamp + params = {"limit": int(limit), "offset": 0, "timestamp": get_last_saved_timestamp()} + + + # params = {"limit": 100, "offset": 0} + + has_more_data = True + + try: + # Getting access token + access_token, message = luminar_manager.access_token() + + if not access_token: + logging.error("Failed to get access token: %s", message) + return + + headers = {"Authorization": f"Bearer {access_token}"} + + while has_more_data: + logging.info("Inside while loop with params: %s", params) + + response = requests.get( + LUMINAR_BASE_URL + "/externalApi/stix", + params=params, + headers=headers, + timeout=TIMEOUT, + ) + if ( + response.status_code == 401 + ): # Assuming 401 is the status code for an expired token + # If the token has expired, refresh it and continue the loop + access_token, _ = luminar_manager.access_token() + if not access_token: + logging.error("Failed to refresh access token") + break + headers = {"Authorization": f"Bearer {access_token}"} + continue + # Getting Luminar data page wise + params["offset"] += params["limit"] + + response_json = response.json() + all_objects = response_json.get("objects", []) + logging.info("len(all_objects): %s", len(all_objects)) + + if not all_objects or len(all_objects) == 1: + logging.info("No more indicators to ingest.") + has_more_data = False + else: + luminar_api_fetch(all_objects) + + except Exception as err: + logging.error("Error occurred during API call: %s", err) + utc_timestamp = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat() + logging.info("Python timer trigger function ran at %s", utc_timestamp) + save_checkpoint(datetime.utcnow().replace(second=0, microsecond=0)) diff --git a/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/function.json b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/function.json new file mode 100644 index 00000000000..f49222b1106 --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "%Polling%" + } + ] +} \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/readme.md b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/readme.md new file mode 100644 index 00000000000..e08f14d6e24 --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/readme.md @@ -0,0 +1,93 @@ +# Luminar Threat Intelligence Connector - Azure Sentinel + + +## Table of Contents + +1. [Overview](#overview) +2. [Luminar API Account](#prerequisites) +3. [Register an Azure AD App for TI Indicators Graph API Write Access](#graphapiaccess) +4. [Deployment](#deployment) +5. [Detection & Investigation](#investigation) + + + +## Overview +Cognyte is a global leader in security analytics software that empowers governments and enterprises with Actionable Intelligence for a safer world. Our open software fuses, analyzes and visualizes disparate data sets at scale to help security organizations find the needles in the haystacks. Over 1,000 government and enterprise customers in more than 100 countries rely on Cognyte’s solutions to accelerate security investigations and connect the dots to successfully identify, neutralize, and prevent threats to national security, business continuity and cyber security. + +Luminar is an asset-based cybersecurity intelligence platform that empowers enterprise organizations to build and maintain a proactive threat intelligence operation that enables to anticipate and mitigate cyber threats, reduce risk and enhance security resilience. Luminar enables security teams to define a customized, dynamic monitoring plan to uncover malicious activity in its earliest stages on all layers of the Web. + +**Luminar IOCs and Leaked Credentials** connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar. + + + + +## Luminar API Account +To utilize Luminar Threat Intelligence, you'll want to have Luminar Credentials. +- Luminar API Account ID +- Luminar API Client ID +- Luminar API Client Secret +For registration and more details please contact luminar@cognyte.com + + + + +## Deployment +- Go to Microsoft Sentinel +- Go to Content Hub +- Search for Luminar, Click on Install. +- Please follow the onscreen deployment screen and provide the required values. + + + +## Detection & Investigation +Now that we have Luminar Threat Intelligence data, you can use out of the box TI analytics rules for you hunting and investigation (or) write custom analytical rules. + +A sample Azure Sentinel Analytics rule to identify a match in Azure AD SigninLogs from any malicious IP address from luminar threat intelligence + +``` +let ipIndicators = + +ThreatIntelligenceIndicator + +| where NetworkIP != "" + +| project IPAddress = NetworkIP; + +ipIndicators + +| join (SigninLogs) on IPAddress + +``` + +What this query is doing is creating a temporary table (“ipIndicators”) that is composed of just the IPv4 addresses from the ThreatIntelligenceIndicator table. This is then joined to the SigninLogs table using IPAddress as they key for the join (e.g. where the field values match in both tables). + + +Happy hunting! \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/state_manager.py b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/state_manager.py new file mode 100644 index 00000000000..05724abc1eb --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/Cognyte Luminar/state_manager.py @@ -0,0 +1,22 @@ +from azure.storage.fileshare import ShareClient +from azure.storage.fileshare import ShareFileClient +from azure.core.exceptions import ResourceNotFoundError + + +class StateManager: + def __init__(self, connection_string, share_name='funcstatemarkershare', file_path='funcstatemarkerfile'): + self.share_cli = ShareClient.from_connection_string(conn_str=connection_string, share_name=share_name, is_emulated=True) + self.file_cli = ShareFileClient.from_connection_string(conn_str=connection_string, share_name=share_name, file_path=file_path, is_emulated=True) + + def post(self, marker_text: str): + try: + self.file_cli.upload_file(marker_text) + except ResourceNotFoundError: + self.share_cli.create_share() + self.file_cli.upload_file(marker_text) + + def get(self): + try: + return self.file_cli.download_file().readall().decode() + except ResourceNotFoundError: + return None \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data Connectors/CognyteLuminar_FunctionApp.json b/Solutions/CognyteLuminar/Data Connectors/CognyteLuminar_FunctionApp.json new file mode 100644 index 00000000000..80f33f10b1a --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/CognyteLuminar_FunctionApp.json @@ -0,0 +1,144 @@ +{ + "id": "CognyteLuminar", + "title": "Luminar IOCs and Leaked Credentials", + "publisher": "Cognyte Technologies Israel Ltd", + "descriptionMarkdown": "Luminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.", + "graphQueries": [ + { + "metricName": "Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'" + }, + { + "metricName": "Non-Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'" + } + ], + "sampleQueries": [ + { + "description": "Cognyte Luminar Based Indicators Events - All Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | sort by TimeGenerated desc" + }, + { + "description": "Non-Cognyte Luminar Based Indicators Events - All Non-Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | sort by TimeGenerated desc" + } + ], + "dataTypes": [ + { + "name": "ThreatIntelligenceIndicator", + "lastDataReceivedQuery": "ThreatIntelligenceIndicator\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "Report_links_data_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + } + ], + "availability": { + "status": 1, + "isPreview": true + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Azure Subscription", + "description": "Azure Subscription with owner role is required to register an application in azure active directory() and assign role of contributor to app in resource group." + }, + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "REST API Credentials/permissions", + "description": "**Luminar Client ID**, **Luminar Client Secret** and **Luminar Account ID** are required." + } + ] + }, + "instructionSteps":[ + { + "title":"", + "description":">**NOTE:** This connector uses Azure Functions to connect to the Cognyte Luminar API to pull Luminar IOCs and Leaked Credentials into Microsoft Sentinel. This might result in additional costs for data ingestion and for storing data in Azure Blob Storage costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) and [Azure Blob Storage pricing page](https://azure.microsoft.com/pricing/details/storage/blobs/) for details." + }, + { + "title":"", + "description":">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "instructions":[ + { + "parameters":{ + "fillWith":[ + "WorkspaceId" + ], + "label":"Workspace ID" + }, + "type":"CopyableLabel" + }, + { + "parameters":{ + "fillWith":[ + "PrimaryKey" + ], + "label":"Primary Key" + }, + "type":"CopyableLabel" + } + ] + }, + { + "title":"Option 1 - Azure Resource Manager (ARM) Template", + "description":"Use this method for automated deployment of the data connector using an ARM Template.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CognyteLuminar-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Application ID**, **Tenant ID**,**Client Secret**, **Luminar API Client ID**, **Luminar API Account ID**, **Luminar API Client Secret**, **Limit**, **TimeInterval** and deploy.\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy." + }, + { + "title":"Option 2 - Manual Deployment of Azure Functions", + "description":"Use the following step-by-step instructions to deploy the Cognyte Luminar data connector manually with Azure Functions (Deployment via Visual Studio Code)." + }, + { + "title":"", + "description":"**1. Deploy a Function App**\n\n> NOTE:You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-python#prerequisites) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-CognyteLuminar-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CognyteLuminarXXX).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "title":"", + "description":"**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\tApplication ID\n\tTenant ID\n\t\tClient Secret\n\t\tLuminar API Client ID\n\t\tLuminar API Account ID\n\t\tLuminar API Client Secret\n\t\tLimit\n\t\tTimeInterval - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`\n3. Once all application settings have been entered, click **Save**." + } + ] + } diff --git a/Solutions/CognyteLuminar/Data Connectors/azuredeploy_LuminarFuncApp_AzureFunction.json b/Solutions/CognyteLuminar/Data Connectors/azuredeploy_LuminarFuncApp_AzureFunction.json new file mode 100644 index 00000000000..5c0ad65acec --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/azuredeploy_LuminarFuncApp_AzureFunction.json @@ -0,0 +1,247 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "FunctionName": { + "defaultValue": "LuminarFuncApp", + "minLength": 1, + "maxLength": 20, + "type": "string" + }, + "ApplicationID": { + "type": "string", + "defaultValue": "", + "minLength": 1 + }, + "TenantID": { + "type": "string", + "defaultValue": "", + "minLength": 1 + }, + "ClientSecret": { + "type": "securestring", + "defaultValue": "", + "minLength": 1 + }, + "LuminarAPIClientID": { + "type": "string", + "defaultValue": "", + "minLength": 1 + }, + "LuminarAPIAccountID": { + "type": "string", + "defaultValue": "", + "minLength": 1 + }, + "LuminarAPIClientSecret": { + "type": "securestring", + "defaultValue": "", + "minLength": 1 + }, + "Limit": { + "type": "string", + "defaultValue": "10", + "minLength": 1 + }, + "TimeInterval": { + "type": "string", + "allowedValues": [ + "Every 5 min", + "Every 10 min", + "Every 60 min", + "Every 6 hours", + "Every 12 hours", + "Every 24 hours" + ], + "defaultValue": "Every 6 hours", + "metadata": { + "description": "Select the Interval." + } + } + }, + "variables": { + "PollingMap": { + "Every 5 min": "*/5 * * * *", + "Every 10 min": "*/10 * * * *", + "Every 60 min": "0 * * * *", + "Every 6 hours": "0 */6 * * *", + "Every 12 hours": "0 */12 * * *", + "Every 24 hours" : "0 0 * * *" + }, + "FunctionName": "[concat(toLower(parameters('FunctionName')), take(uniqueString(resourceGroup().id), 3))]", + "StorageSuffix": "[environment().suffixes.storage]", + "Polling": "[variables('PollingMap')[parameters('TimeInterval')]]" + + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2015-05-01", + "name": "[variables('FunctionName')]", + "location": "[resourceGroup().location]", + "kind": "web", + "properties": { + "Application_Type": "web", + "ApplicationId": "[variables('FunctionName')]" + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "name": "[tolower(variables('FunctionName'))]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "file": { + "keyType": "Account", + "enabled": true + }, + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('FunctionName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + }, + "deleteRetentionPolicy": { + "enabled": false + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('FunctionName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + } + } + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "name": "[variables('FunctionName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', tolower(variables('FunctionName')))]", + "[resourceId('Microsoft.Insights/components', variables('FunctionName'))]" + ], + "kind": "functionapp,linux", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "name": "[variables('FunctionName')]", + "httpsOnly": true, + "clientAffinityEnabled": true, + "alwaysOn": true, + "reserved": true, + "siteConfig": { + "linuxFxVersion": "python|3.8" + } + }, + "resources": [ + { + "apiVersion": "2018-11-01", + "type": "config", + "name": "appsettings", + "dependsOn": [ + "[concat('Microsoft.Web/sites/', variables('FunctionName'))]" + ], + "properties": { + "FUNCTIONS_EXTENSION_VERSION": "~4", + "FUNCTIONS_WORKER_RUNTIME": "python", + "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components', variables('FunctionName')), '2015-05-01').InstrumentationKey]", + "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('microsoft.insights/components', variables('FunctionName')), '2015-05-01').ConnectionString]", + "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(variables('FunctionName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', toLower(variables('FunctionName'))), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]", + "ApplicationID": "[parameters('ApplicationID')]", + "TenantID":"[parameters('TenantID')]", + "ClientSecret": "[parameters('ClientSecret')]", + "LuminarAPIClientID": "[parameters('LuminarAPIClientID')]", + "LuminarAPIAccountID":"[parameters('LuminarAPIAccountID')]", + "LuminarAPIClientSecret": "[parameters('LuminarAPIClientSecret')]", + "timeInterval": "[parameters('TimeInterval')]", + "Limit": "[parameters('Limit')]", + "Polling": "[variables('Polling')]", + "WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-CognyteLuminar-functionapp" + } + } + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(variables('FunctionName'), '/default/azure-webjobs-hosts')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(variables('FunctionName'), '/default/azure-webjobs-secrets')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2019-06-01", + "name": "[concat(variables('FunctionName'), '/default/', tolower(variables('FunctionName')))]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('FunctionName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('FunctionName'))]" + ], + "properties": { + "shareQuota": 5120 + } + } + + ] +} diff --git a/Solutions/CognyteLuminar/Data Connectors/host.json b/Solutions/CognyteLuminar/Data Connectors/host.json new file mode 100644 index 00000000000..bc2e69c5289 --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/host.json @@ -0,0 +1,16 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[3.*, 4.0.0)" + }, + "functionTimeout": "00:10:00" +} \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data Connectors/proxies.json b/Solutions/CognyteLuminar/Data Connectors/proxies.json new file mode 100644 index 00000000000..13ca746ccf8 --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} +} \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data Connectors/requirements.txt b/Solutions/CognyteLuminar/Data Connectors/requirements.txt new file mode 100644 index 00000000000..1aaf517e5d5 --- /dev/null +++ b/Solutions/CognyteLuminar/Data Connectors/requirements.txt @@ -0,0 +1,3 @@ +azure-functions==1.6.0 +azure-storage-file-share==12.3.0 +requests==2.22.0 \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Data/Solution_Cognyte_Luminar.json b/Solutions/CognyteLuminar/Data/Solution_Cognyte_Luminar.json new file mode 100644 index 00000000000..2f193aa4b41 --- /dev/null +++ b/Solutions/CognyteLuminar/Data/Solution_Cognyte_Luminar.json @@ -0,0 +1,17 @@ +{ + "Name": "CognyteLuminar", + "Author": "Cognyte", + "Logo": "", + "Description": "Luminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.", + "Workbooks": [], + "Playbooks": [], + "Data Connectors": [ + "Data Connectors/CognyteLuminar_FunctionApp.json" + ], + "Hunting Queries": [], + "BasePath": "D:/GitHub/Azure-Sentinel/Solutions/CognyteLuminar", + "Version": "3.0.0", + "Metadata": "SolutionMetadata.json", + "TemplateSpec": true, + "Is1PConnector": false +} \ No newline at end of file diff --git a/Solutions/CognyteLuminar/Package/3.0.0.zip b/Solutions/CognyteLuminar/Package/3.0.0.zip new file mode 100644 index 00000000000..1160c71fef1 Binary files /dev/null and b/Solutions/CognyteLuminar/Package/3.0.0.zip differ diff --git a/Solutions/CognyteLuminar/Package/createUiDefinition.json b/Solutions/CognyteLuminar/Package/createUiDefinition.json new file mode 100644 index 00000000000..70a401947a2 --- /dev/null +++ b/Solutions/CognyteLuminar/Package/createUiDefinition.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "config": { + "isWizard": false, + "basics": { + "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nLuminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.\n\n**Data Connectors:** 1\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "subscription": { + "resourceProviders": [ + "Microsoft.OperationsManagement/solutions", + "Microsoft.OperationalInsights/workspaces/providers/alertRules", + "Microsoft.Insights/workbooks", + "Microsoft.Logic/workflows" + ] + }, + "location": { + "metadata": { + "hidden": "Hiding location, we get it from the log analytics workspace" + }, + "visible": false + }, + "resourceGroup": { + "allowExisting": true + } + } + }, + "basics": [ + { + "name": "getLAWorkspace", + "type": "Microsoft.Solutions.ArmApiControl", + "toolTip": "This filters by workspaces that exist in the Resource Group selected", + "condition": "[greater(length(resourceGroup().name),0)]", + "request": { + "method": "GET", + "path": "[concat(subscription().id,'/providers/Microsoft.OperationalInsights/workspaces?api-version=2020-08-01')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.DropDown", + "label": "Workspace", + "placeholder": "Select a workspace", + "toolTip": "This dropdown will list only workspace that exists in the Resource Group selected", + "constraints": { + "allowedValues": "[map(filter(basics('getLAWorkspace').value, (filter) => contains(toLower(filter.id), toLower(resourceGroup().name))), (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.name, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "steps": [ + { + "name": "dataconnectors", + "label": "Data Connectors", + "bladeTitle": "Data Connectors", + "elements": [ + { + "name": "dataconnectors1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Solution installs the data connector for CognyteLuminar. You can get CognyteLuminar custom log data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." + } + }, + { + "name": "dataconnectors-link2", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more about connecting data sources", + "uri": "https://docs.microsoft.com/azure/sentinel/connect-data-sources" + } + } + } + ] + } + ], + "outputs": { + "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", + "location": "[location()]", + "workspace": "[basics('workspace')]" + } + } +} diff --git a/Solutions/CognyteLuminar/Package/mainTemplate.json b/Solutions/CognyteLuminar/Package/mainTemplate.json new file mode 100644 index 00000000000..446eb180a19 --- /dev/null +++ b/Solutions/CognyteLuminar/Package/mainTemplate.json @@ -0,0 +1,487 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "author": "Cognyte", + "comments": "Solution template for CognyteLuminar" + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + } + }, + "variables": { + "_solutionName": "CognyteLuminar", + "_solutionVersion": "3.0.0", + "solutionId": "cognytetechnologiesltd.microsoft-sentinel-solution-cognyteluminar", + "_solutionId": "[variables('solutionId')]", + "uiConfigId1": "CognyteLuminar", + "_uiConfigId1": "[variables('uiConfigId1')]", + "dataConnectorContentId1": "CognyteLuminar", + "_dataConnectorContentId1": "[variables('dataConnectorContentId1')]", + "dataConnectorId1": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "_dataConnectorId1": "[variables('dataConnectorId1')]", + "dataConnectorTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentId1'))),variables('dataConnectorVersion1')))]", + "dataConnectorVersion1": "1.0.0", + "_dataConnectorcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','dc','-', uniqueString(concat(variables('_solutionId'),'-','DataConnector','-',variables('_dataConnectorContentId1'),'-', variables('dataConnectorVersion1'))))]", + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('dataConnectorTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "CognyteLuminar data connector with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorVersion1')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId1'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "GenericUI", + "properties": { + "connectorUiConfig": { + "id": "[variables('_uiConfigId1')]", + "title": "Luminar IOCs and Leaked Credentials (using Azure Functions)", + "publisher": "Cognyte Technologies Israel Ltd", + "descriptionMarkdown": "Luminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.", + "graphQueries": [ + { + "metricName": "Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'" + }, + { + "metricName": "Non-Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'" + } + ], + "sampleQueries": [ + { + "description": "Cognyte Luminar Based Indicators Events - All Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | sort by TimeGenerated desc" + }, + { + "description": "Non-Cognyte Luminar Based Indicators Events - All Non-Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | sort by TimeGenerated desc" + } + ], + "dataTypes": [ + { + "name": "ThreatIntelligenceIndicator", + "lastDataReceivedQuery": "ThreatIntelligenceIndicator\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "Report_links_data_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Azure Subscription", + "description": "Azure Subscription with owner role is required to register an application in azure active directory() and assign role of contributor to app in resource group." + }, + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "REST API Credentials/permissions", + "description": "**Luminar Client ID**, **Luminar Client Secret** and **Luminar Account ID** are required." + } + ] + }, + "instructionSteps": [ + { + "description": ">**NOTE:** This connector uses Azure Functions to connect to the Cognyte Luminar API to pull Luminar IOCs and Leaked Credentials into Microsoft Sentinel. This might result in additional costs for data ingestion and for storing data in Azure Blob Storage costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) and [Azure Blob Storage pricing page](https://azure.microsoft.com/pricing/details/storage/blobs/) for details." + }, + { + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "instructions": [ + { + "parameters": { + "fillWith": [ + "WorkspaceId" + ], + "label": "Workspace ID" + }, + "type": "CopyableLabel" + }, + { + "parameters": { + "fillWith": [ + "PrimaryKey" + ], + "label": "Primary Key" + }, + "type": "CopyableLabel" + } + ] + }, + { + "description": "Use this method for automated deployment of the data connector using an ARM Template.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CognyteLuminar-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Application ID**, **Tenant ID**,**Client Secret**, **Luminar API Client ID**, **Luminar API Account ID**, **Luminar API Client Secret**, **Limit**, **TimeInterval** and deploy.\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy.", + "title": "Option 1 - Azure Resource Manager (ARM) Template" + }, + { + "description": "Use the following step-by-step instructions to deploy the Cognyte Luminar data connector manually with Azure Functions (Deployment via Visual Studio Code).", + "title": "Option 2 - Manual Deployment of Azure Functions" + }, + { + "description": "**1. Deploy a Function App**\n\n> NOTE:You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-python#prerequisites) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-CognyteLuminar-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CognyteLuminarXXX).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "description": "**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\tApplication ID\n\tTenant ID\n\t\tClient Secret\n\t\tLuminar API Client ID\n\t\tLuminar API Account ID\n\t\tLuminar API Client Secret\n\t\tLimit\n\t\tTimeInterval - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`\n3. Once all application settings have been entered, click **Save**." + } + ] + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId1'),'/'))))]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "contentId": "[variables('_dataConnectorContentId1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion1')]", + "source": { + "kind": "Solution", + "name": "CognyteLuminar", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Cognyte" + }, + "support": { + "name": "Cognyte Luminar", + "email": "luminar@congyte.com", + "tier": "Partner", + "link": "https://www.cognyte.com/contact/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_dataConnectorContentId1')]", + "contentKind": "DataConnector", + "displayName": "Luminar IOCs and Leaked Credentials (using Azure Functions)", + "contentProductId": "[variables('_dataConnectorcontentProductId1')]", + "id": "[variables('_dataConnectorcontentProductId1')]", + "version": "[variables('dataConnectorVersion1')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId1'),'/'))))]", + "dependsOn": [ + "[variables('_dataConnectorId1')]" + ], + "location": "[parameters('workspace-location')]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "contentId": "[variables('_dataConnectorContentId1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion1')]", + "source": { + "kind": "Solution", + "name": "CognyteLuminar", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Cognyte" + }, + "support": { + "name": "Cognyte Luminar", + "email": "luminar@congyte.com", + "tier": "Partner", + "link": "https://www.cognyte.com/contact/" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId1'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "GenericUI", + "properties": { + "connectorUiConfig": { + "title": "Luminar IOCs and Leaked Credentials (using Azure Functions)", + "publisher": "Cognyte Technologies Israel Ltd", + "descriptionMarkdown": "Luminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.", + "graphQueries": [ + { + "metricName": "Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem contains 'Luminar'" + }, + { + "metricName": "Non-Cognyte Luminar Threat Indicators data received", + "legend": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'", + "baseQuery": "ThreatIntelligenceIndicator | where SourceSystem !contains 'Luminar'" + } + ], + "dataTypes": [ + { + "name": "ThreatIntelligenceIndicator", + "lastDataReceivedQuery": "ThreatIntelligenceIndicator\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "Report_links_data_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + }, + { + "type": "IsConnectedQuery", + "value": [ + "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(30d)" + ] + } + ], + "sampleQueries": [ + { + "description": "Cognyte Luminar Based Indicators Events - All Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem contains 'Luminar'\n | sort by TimeGenerated desc" + }, + { + "description": "Non-Cognyte Luminar Based Indicators Events - All Non-Cognyte Luminar indicators in Microsoft Sentinel Threat Intelligence.", + "query": "ThreatIntelligenceIndicator\n | where SourceSystem !contains 'Luminar'\n | sort by TimeGenerated desc" + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Azure Subscription", + "description": "Azure Subscription with owner role is required to register an application in azure active directory() and assign role of contributor to app in resource group." + }, + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "REST API Credentials/permissions", + "description": "**Luminar Client ID**, **Luminar Client Secret** and **Luminar Account ID** are required." + } + ] + }, + "instructionSteps": [ + { + "description": ">**NOTE:** This connector uses Azure Functions to connect to the Cognyte Luminar API to pull Luminar IOCs and Leaked Credentials into Microsoft Sentinel. This might result in additional costs for data ingestion and for storing data in Azure Blob Storage costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) and [Azure Blob Storage pricing page](https://azure.microsoft.com/pricing/details/storage/blobs/) for details." + }, + { + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "instructions": [ + { + "parameters": { + "fillWith": [ + "WorkspaceId" + ], + "label": "Workspace ID" + }, + "type": "CopyableLabel" + }, + { + "parameters": { + "fillWith": [ + "PrimaryKey" + ], + "label": "Primary Key" + }, + "type": "CopyableLabel" + } + ] + }, + { + "description": "Use this method for automated deployment of the data connector using an ARM Template.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-CognyteLuminar-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Application ID**, **Tenant ID**,**Client Secret**, **Luminar API Client ID**, **Luminar API Account ID**, **Luminar API Client Secret**, **Limit**, **TimeInterval** and deploy.\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy.", + "title": "Option 1 - Azure Resource Manager (ARM) Template" + }, + { + "description": "Use the following step-by-step instructions to deploy the Cognyte Luminar data connector manually with Azure Functions (Deployment via Visual Studio Code).", + "title": "Option 2 - Manual Deployment of Azure Functions" + }, + { + "description": "**1. Deploy a Function App**\n\n> NOTE:You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-python#prerequisites) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-CognyteLuminar-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CognyteLuminarXXX).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "description": "**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\tApplication ID\n\tTenant ID\n\t\tClient Secret\n\t\tLuminar API Client ID\n\t\tLuminar API Account ID\n\t\tLuminar API Client Secret\n\t\tLimit\n\t\tTimeInterval - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`\n3. Once all application settings have been entered, click **Save**." + } + ], + "id": "[variables('_uiConfigId1')]" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.0", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "CognyteLuminar", + "publisherDisplayName": "Cognyte Luminar", + "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

Luminar IOCs and Leaked Credentials connector allows integration of intelligence-based IOC data and customer-related leaked records identified by Luminar.

\n

Data Connectors: 1

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "CognyteLuminar", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Cognyte" + }, + "support": { + "name": "Cognyte Luminar", + "email": "luminar@congyte.com", + "tier": "Partner", + "link": "https://www.cognyte.com/contact/" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentId1')]", + "version": "[variables('dataConnectorVersion1')]" + } + ] + }, + "providers": [ + "Cognyte Technologies Israel Ltd" + ], + "categories": { + "domains": [ + "Security - Automation (SOAR)", + "Security - Threat Intelligence" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + } + ], + "outputs": {} +} diff --git a/Solutions/CognyteLuminar/ReleaseNotes.md b/Solutions/CognyteLuminar/ReleaseNotes.md new file mode 100644 index 00000000000..1c6a0e00c75 --- /dev/null +++ b/Solutions/CognyteLuminar/ReleaseNotes.md @@ -0,0 +1,3 @@ +| **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | +|-------------|--------------------------------|---------------------------------------------| +| 3.0.0 | 22-09-2023 | Initial solution release | diff --git a/Solutions/CognyteLuminar/SolutionMetadata.json b/Solutions/CognyteLuminar/SolutionMetadata.json new file mode 100644 index 00000000000..d2b4f4f6f44 --- /dev/null +++ b/Solutions/CognyteLuminar/SolutionMetadata.json @@ -0,0 +1,19 @@ +{ + "publisherId": "cognytetechnologiesltd", + "offerId": "microsoft-sentinel-solution-cognyteluminar", + "firstPublishDate": "2023-15-09", + "providers": [ + "Cognyte Technologies Israel Ltd" + ], + "categories": { + "domains": [ + "Security - Automation (SOAR)", "Security - Threat Intelligence" + ] + }, + "support": { + "name": "Cognyte Luminar", + "email": "luminar@congyte.com", + "tier": "Partner", + "link": "https://www.cognyte.com/contact/" + } +} \ No newline at end of file