Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.filigran.io/connectors/microsoft-defender-intel_config.schema.json",
"type": "object",
"properties": {
"OPENCTI_URL": {
"description": "The base URL of the OpenCTI instance.",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
"type": "string"
},
"OPENCTI_TOKEN": {
"description": "The API token to connect to OpenCTI.",
"type": "string"
},
"CONNECTOR_NAME": {
"default": "MicrosoftDefenderIntel",
"description": "The name of the connector.",
"type": "string"
},
"CONNECTOR_SCOPE": {
"description": "The scope of the connector, e.g. 'flashpoint'.",
"items": {
"type": "string"
},
"type": "array"
},
"CONNECTOR_LOG_LEVEL": {
"default": "error",
"description": "The minimum level of logs to display.",
"enum": [
"debug",
"info",
"warn",
"warning",
"error"
],
"type": "string"
},
"CONNECTOR_TYPE": {
"const": "STREAM",
"default": "STREAM",
"type": "string"
},
"CONNECTOR_LIVE_STREAM_ID": {
"default": "live",
"description": "The ID of the live stream to connect to.",
"type": "string"
},
"CONNECTOR_LIVE_STREAM_LISTEN_DELETE": {
"default": true,
"description": "Whether to listen for delete events on the live stream.",
"type": "boolean"
},
"CONNECTOR_LIVE_STREAM_NO_DEPENDENCIES": {
"default": true,
"description": "Whether to ignore dependencies when processing events from the live stream.",
"type": "boolean"
},
"MICROSOFT_DEFENDER_INTEL_TENANT_ID": {
"description": "Your Azure App Tenant ID, see the screenshot to help you find this information.",
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_CLIENT_ID": {
"description": "Your Azure App Client ID, see the screenshot to help you find this information.",
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_CLIENT_SECRET": {
"description": "Your Azure App Client secret, See the screenshot to help you find this information.",
"format": "password",
"type": "string",
"writeOnly": true
},
"MICROSOFT_DEFENDER_INTEL_LOGIN_URL": {
"default": "https://login.microsoft.com",
"description": "Login URL for Microsoft which is `https://login.microsoft.com`",
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_BASE_URL": {
"default": "https://api.securitycenter.microsoft.com",
"description": "The resource the API will use which is `https://api.securitycenter.microsoft.com`",
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_RESOURCE_PATH": {
"default": "/api/indicators",
"description": "The request URL that will be used which is `/api/indicators`",
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_EXPIRE_TIME": {
"default": 30,
"description": "Number of days for your indicator to expire in Sentinel. Suggestion of `30` as a default",
"type": "integer"
},
"MICROSOFT_DEFENDER_INTEL_ACTION": {
"default": "Alert",
"description": "The action to apply if the indicator is matched from within the targetProduct security tool. Possible values are: `Warn`, `Block`, `Audit`, `Alert`, `AlertAndBlock`, `BlockAndRemediate`, `Allowed`. `BlockAndRemediate` is not compatible with network indicators (see: https://learn.microsoft.com/en-us/defender-endpoint/indicator-manage)",
"enum": [
"Warn",
"Block",
"Audit",
"Alert",
"AlertAndBlock",
"BlockAndRemediate",
"Allowed"
],
"type": "string"
},
"MICROSOFT_DEFENDER_INTEL_PASSIVE_ONLY": {
"default": false,
"description": "Determines if the indicator should trigger an event that is visible to an end-user. When set to `True` security tools will not notify the end user that a \u00e2\u20ac\u02dchit\u00e2\u20ac\u2122 has occurred. This is most often treated as audit or silent mode by security products where they will simply log that a match occurred but will not perform the action. Default value is `False`.",
"type": "boolean"
}
},
"required": [
"OPENCTI_URL",
"OPENCTI_TOKEN",
"CONNECTOR_SCOPE",
"MICROSOFT_DEFENDER_INTEL_TENANT_ID",
"MICROSOFT_DEFENDER_INTEL_CLIENT_ID",
"MICROSOFT_DEFENDER_INTEL_CLIENT_SECRET"
],
"additionalProperties": true
}
2 changes: 1 addition & 1 deletion stream/microsoft-defender-intel/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ services:
- MICROSOFT_DEFENDER_INTEL_TENANT_ID=ChangeMe
- MICROSOFT_DEFENDER_INTEL_CLIENT_ID=ChangeMe
- MICROSOFT_DEFENDER_INTEL_CLIENT_SECRET=ChangeMe
restart: unless-stopped
restart: unless-stopped
2 changes: 1 addition & 1 deletion stream/microsoft-defender-intel/src/config.yml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ connector:
microsoft_defender_intel:
tenant_id: 'ChangeMe'
client_id: 'ChangeMe'
client_secret: 'ChangeMe'
client_secret: 'ChangeMe'
11 changes: 9 additions & 2 deletions stream/microsoft-defender-intel/src/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import traceback

from microsoft_defender_intel_connector import MicrosoftDefenderIntelConnector
from microsoft_defender_intel_connector import (
ConnectorSettings,
MicrosoftDefenderIntelConnector,
)
from pycti import OpenCTIConnectorHelper

if __name__ == "__main__":
"""
Expand All @@ -13,7 +17,10 @@
It signals to the operating system and any calling processes that the program did not complete successfully.
"""
try:
connector = MicrosoftDefenderIntelConnector()
settings = ConnectorSettings()
helper = OpenCTIConnectorHelper(config=settings.to_helper_config())

connector = MicrosoftDefenderIntelConnector(config=settings, helper=helper)
connector.run()
except Exception:
traceback.print_exc()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .connector import MicrosoftDefenderIntelConnector

__all__ = ["MicrosoftDefenderIntelConnector"]
__all__ = ["MicrosoftDefenderIntelConnector", "ConnectorSettings"]
from microsoft_defender_intel_connector.connector import MicrosoftDefenderIntelConnector
from microsoft_defender_intel_connector.settings import ConnectorSettings
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import json
from json import JSONDecodeError

from microsoft_defender_intel_connector.settings import ConnectorSettings
from pycti import OpenCTIConnectorHelper

from .api_handler import DefenderApiHandler, DefenderApiHandlerError
from .config_variables import ConfigConnector
from .utils import (
FILE_HASH_TYPES_MAPPER,
is_observable,
is_stix_indicator,
)
from .utils import FILE_HASH_TYPES_MAPPER, is_observable, is_stix_indicator


class MicrosoftDefenderIntelConnector:
Expand Down Expand Up @@ -39,15 +35,13 @@ class MicrosoftDefenderIntelConnector:

"""

def __init__(self):
def __init__(self, config: ConnectorSettings, helper: OpenCTIConnectorHelper):
"""
Initialize the Connector with necessary configurations
"""

# Load configuration file and connection helper
self.config = ConfigConnector()
self.helper = OpenCTIConnectorHelper(self.config.load)
self.api = DefenderApiHandler(self.helper, self.config)
self.config = config
self.helper = helper
self.api = DefenderApiHandler(self.helper, self.config.microsoft_defender_intel)

def _check_stream_id(self) -> None:
"""
Expand All @@ -73,7 +67,6 @@ def _convert_indicator_to_observables(self, data) -> list[dict]:
"observable_values", data
)
if parsed_observables:
# Iterate over the parsed observables
for observable in parsed_observables:
observable_data = {}
observable_data.update(data)
Expand All @@ -92,7 +85,6 @@ def _convert_indicator_to_observables(self, data) -> list[dict]:
observable_data["type"] = "file"
observable_data["hashes"] = file
observables.append(observable_data)

return observables
except:
indicator_opencti_id = OpenCTIConnectorHelper.get_attribute_in_extension(
Expand All @@ -117,13 +109,11 @@ def _create_defender_indicator(self, observable_data):
"[CREATE] Indicator created",
{"defender_id": result["id"], "opencti_id": observable_opencti_id},
)
# Update OpenCTI SDO external references
external_reference = self.helper.api.external_reference.create(
source_name="Microsoft Defender",
external_id=result["id"],
description="Intel within the Microsoft platform.",
)
# If observable was built from an OpenCTI Indicator
if "pattern" in observable_data:
self.helper.api.stix_domain_object.add_external_reference(
id=observable_opencti_id,
Expand Down Expand Up @@ -184,10 +174,7 @@ def _handle_update_event(self, data):
did_update = True
self.helper.connector_logger.info(
"[UPDATE] Indicator updated",
{
"defender_id": result[0]["id"],
"opencti_id": opencti_id,
},
{"defender_id": result[0]["id"], "opencti_id": opencti_id},
)
elif is_observable(data):
result = self.api.find_indicators(data["value"])
Expand All @@ -196,10 +183,7 @@ def _handle_update_event(self, data):
did_update = True
self.helper.connector_logger.info(
"[UPDATE] Indicator updated",
{
"defender_id": result[0]["id"],
"opencti_id": opencti_id,
},
{"defender_id": result[0]["id"], "opencti_id": opencti_id},
)
if not did_update:
self.helper.connector_logger.info(
Expand Down Expand Up @@ -265,23 +249,14 @@ def _handle_delete_event(self, data):
did_delete = True
self.helper.connector_logger.info(
"[DELETE] Indicator deleted",
{
"defender_id": indicator_result["id"],
"opencti_id": opencti_id,
},
{"defender_id": indicator_result["id"], "opencti_id": opencti_id},
)
external_reference = self.helper.api.external_reference.read(
filters={
"mode": "and",
"filters": [
{
"key": "source_name",
"values": ["Microsoft Defender"],
},
{
"key": "external_id",
"values": [indicator_result["id"]],
},
{"key": "source_name", "values": ["Microsoft Defender"]},
{"key": "external_id", "values": [indicator_result["id"]]},
],
"filterGroups": [],
}
Expand Down Expand Up @@ -319,20 +294,16 @@ def process_message(self, msg) -> None:
"""
try:
self._check_stream_id()

parsed_msg = self.validate_json(msg)
data = parsed_msg["data"]

if msg.event == "create":
self._handle_create_event(data)
if msg.event == "update":
self._handle_update_event(data)
if msg.event == "delete":
self._handle_delete_event(data)

except DefenderApiHandlerError as err:
self.helper.connector_logger.error(err.msg, err.metadata)

except Exception as err:
self.helper.connector_logger.error(
"[ERROR] Failed processing data {" + str(err) + "}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from typing import Literal

from connectors_sdk import (
BaseConfigModel,
BaseConnectorSettings,
BaseStreamConnectorConfig,
)
from pydantic import Field, SecretStr


class StreamConnectorConfig(BaseStreamConnectorConfig):
"""
Override the `BaseStreamConnectorConfig` to add parameters and/or defaults
to the configuration for connectors of type `STREAM`.
"""

name: str = Field(
description="The name of the connector.",
default="MicrosoftDefenderIntel",
)
live_stream_id: str = Field(
description="The ID of the live stream to connect to.",
default="live", # listen the global stream (not filtered)
)


class MicrosoftDefenderIntelConfig(BaseConfigModel):
"""
Define parameters and/or defaults for the configuration specific to the `MicrosoftDefenderIntelConnector`.
"""

tenant_id: str = Field(
description="Your Azure App Tenant ID, see the screenshot to help you find this information.",
)
client_id: str = Field(
description="Your Azure App Client ID, see the screenshot to help you find this information.",
)
client_secret: SecretStr = Field(
description="Your Azure App Client secret, See the screenshot to help you find this information.",
)
login_url: str = Field(
description="Login URL for Microsoft which is `https://login.microsoft.com`",
default="https://login.microsoft.com",
)
base_url: str = Field(
description="The resource the API will use which is `https://api.securitycenter.microsoft.com`",
default="https://api.securitycenter.microsoft.com",
)
resource_path: str = Field(
description="The request URL that will be used which is `/api/indicators`",
default="/api/indicators",
)
expire_time: int = Field(
description="Number of days for your indicator to expire in Sentinel. Suggestion of `30` as a default",
default=30,
)
action: Literal[
"Warn",
"Block",
"Audit",
"Alert",
"AlertAndBlock",
"BlockAndRemediate",
"Allowed",
] = Field(
description="The action to apply if the indicator is matched from within the targetProduct security tool. Possible values are: `Warn`, `Block`, `Audit`, `Alert`, `AlertAndBlock`, `BlockAndRemediate`, `Allowed`. `BlockAndRemediate` is not compatible with network indicators (see: https://learn.microsoft.com/en-us/defender-endpoint/indicator-manage)",
default="Alert",
)
passive_only: bool = Field(
description="Determines if the indicator should trigger an event that is visible to an end-user. When set to `True` security tools will not notify the end user that a ‘hit’ has occurred. This is most often treated as audit or silent mode by security products where they will simply log that a match occurred but will not perform the action. Default value is `False`.",
default=False,
)


class ConnectorSettings(BaseConnectorSettings):
"""
Override `BaseConnectorSettings` to include `StreamConnectorConfig` and `MicrosoftDefenderIntelConfig`.
"""

connector: StreamConnectorConfig = Field(default_factory=StreamConnectorConfig)
microsoft_defender_intel: MicrosoftDefenderIntelConfig = Field(
default_factory=MicrosoftDefenderIntelConfig
)
Loading