Skip to content

Commit

Permalink
Integration of Microsoft XDR (#8)
Browse files Browse the repository at this point in the history
- Adding a new platform: Microsoft XDR
- Handle Microsoft Graph API rate limitation
- Implement the search of the rules (sigma and raw)
- Implement the export of the rules (sigma and raw)
- Implement the integrity check for the detection content

---------
  • Loading branch information
WildDogOne authored Aug 28, 2024
1 parent 16214db commit 1d566cc
Show file tree
Hide file tree
Showing 6 changed files with 757 additions and 24 deletions.
26 changes: 11 additions & 15 deletions src/droid/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,19 @@ def is_raw_rule(args, base_config):
else:
return False
if (
(args.platform in ['splunk', 'azure'] or
(args.platform == 'microsoft_defender' and args.sentinel_mde)) and
(args.platform in ['splunk', 'azure']) and
(raw_rule_folder_name in args.rules and args.platform in args.rules)
):
return True
elif args.platform in ['esql', 'eql'] and raw_rule_folder_name in args.rules:
return True
elif args.platform in ['esql', 'eql']:
return False
elif args.platform == 'microsoft_defender' and raw_rule_folder_name in args.rules:
return True
elif (
args.platform in ['splunk', 'azure'] or
(args.platform == 'microsoft_defender' and args.sentinel_mde)
(args.platform == 'microsoft_defender')
):
return False

Expand Down Expand Up @@ -174,15 +175,7 @@ def droid_platform_config(args, config_path):
except Exception:
raise Exception("Something unexpected happened...")

if args.export or args.search:

if (
args.platform == 'microsoft_defender' and
config["search_auth"] != "app" and
config["export_auth"] != "app" and not
args.sentinel_mde
):
exit("Error: DROID only supports Azure App Registration authentication method for MDE.")
if args.export or args.search or args.integrity:

if config["search_auth"] == "app":

Expand Down Expand Up @@ -380,9 +373,12 @@ def main(argv=None) -> None:
else:
export_error = convert_rules(parameters, droid_platform_config(args, config_path), base_config)

elif args.platform == "microsoft_defender" and not args.sentinel_mde:
logger.error("Export mode for MDE is only available via Azure Sentinel backend for now.")
exit(1)
elif args.platform == "microsoft_defender":
if is_raw_rule(args, base_config):
logger.info("Microsoft XDR raw rule selected")
export_error = export_rule_raw(parameters, droid_platform_config(args, config_path))
else:
export_error = convert_rules(parameters, droid_platform_config(args, config_path), base_config)

elif args.platform == 'esql' or args.platform == 'eql':
args.platform == 'elastic'
Expand Down
5 changes: 4 additions & 1 deletion src/droid/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from droid.platforms.splunk import SplunkPlatform
from droid.platforms.sentinel import SentinelPlatform
from droid.platforms.elastic import ElasticPlatform
from droid.platforms.ms_xdr import MicrosoftXDRPlatform
from droid.color import ColorLogger

class Conversion:
Expand Down Expand Up @@ -187,8 +188,10 @@ def convert_rules(parameters, droid_config, base_config):
platform = ElasticPlatform(droid_config, parameters.debug, parameters.json, "esql", raw=False)
elif 'eql' in platform_name:
platform = ElasticPlatform(droid_config, parameters.debug, parameters.json, "eql", raw=False)
elif 'azure' or 'defender' in platform_name:
elif 'azure' in platform_name:
platform = SentinelPlatform(droid_config, parameters.debug, parameters.json)
elif parameters.platform == 'microsoft_defender':
platform = MicrosoftXDRPlatform(droid_config, parameters.debug, parameters.json)

if path.is_dir():
error_i = False
Expand Down
5 changes: 3 additions & 2 deletions src/droid/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from droid.platforms.splunk import SplunkPlatform
from droid.platforms.sentinel import SentinelPlatform
from droid.platforms.elastic import ElasticPlatform
from droid.platforms.ms_xdr import MicrosoftXDRPlatform
from droid.color import ColorLogger

def post_rule_content(rule_content):
Expand Down Expand Up @@ -71,8 +72,8 @@ def export_rule_raw(parameters: dict, export_config: dict):
platform = SplunkPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'azure':
platform = SentinelPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'microsoft_defender' and parameters.sentinel_mde:
platform = SentinelPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'microsoft_defender':
platform = MicrosoftXDRPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'esql':
platform = ElasticPlatform(export_config, parameters.debug, parameters.json, "esql", raw=True)
elif parameters.platform == 'eql':
Expand Down
67 changes: 64 additions & 3 deletions src/droid/integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from droid.platforms.splunk import SplunkPlatform
from droid.platforms.sentinel import SentinelPlatform
from droid.platforms.elastic import ElasticPlatform
from droid.platforms.ms_xdr import MicrosoftXDRPlatform
from droid.color import ColorLogger
from droid.export import post_rule_content

Expand Down Expand Up @@ -142,6 +143,63 @@ def integrity_rule_sentinel(rule_converted, rule_content, platform: SentinelPlat
return error


def integrity_rule_ms_xdr(rule_converted, rule_content, platform: MicrosoftXDRPlatform, rule_file, parameters, logger, error):

try:
saved_search: dict = platform.get_rule(rule_content["id"])
except Exception as e:
logger.error(f"Couldn't check the integrity for the rule {rule_file} - error {e}")
return error

if saved_search:
logger.info(f"Successfully retrieved the rule {rule_file}")
else:
logger.error(f"Rule not found {rule_file}")
error = True
return error

result = {
"description": saved_search['detectionAction']['alertTemplate']['description'],
"query": saved_search["queryCondition"]["queryText"]
}

rule_content["detection"] = rule_converted

mapping = {
"detection": "query",
"description": "description"
}
for key in mapping:

rule_key = key
result_key = mapping[key]

if rule_content.get(rule_key) == result.get(result_key):
if parameters.debug:
logger.debug(f"{rule_key} in rule_content matches {result_key} in result")
else:
logger.error(f"{rule_key} in rule_content does not match {result_key} in result")
error = True


# Check if disabled
is_disabled = rule_content.get('custom', {}).get('disabled')

if is_disabled and not saved_search["isEnabled"]:
logger.info("The rule is disabled as expected")
elif is_disabled and saved_search["isEnabled"]:
logger.error("The rule is not disabled on the platform")
error = True
elif is_disabled is None and not saved_search["isEnabled"]:
logger.error("The rule is not enabled on the platform")
error = True
elif is_disabled is None and saved_search["isEnabled"]:
logger.info("The rule is enabled as expected")

if error:
return error


def integrity_rule_elastic(rule_converted, rule_content, platform: ElasticPlatform, rule_file, parameters, logger, error):
try:
saved_search: dict = platform.get_rule(rule_content["id"])
Expand Down Expand Up @@ -223,7 +281,10 @@ def integrity_rule(parameters, rule_converted, rule_content, platform, rule_file
elif parameters.platform in ["esql", "eql"]:
error = integrity_rule_elastic(rule_converted, rule_content, platform, rule_file, parameters, logger, error)
return error
elif 'azure' or 'defender' in parameters.platform:
elif parameters.platform == 'microsoft_defender': # TODO: Add Integrity check for Microsoft 365 Defender
error = integrity_rule_ms_xdr(rule_converted, rule_content, platform, rule_file, parameters, logger, error)
return error
elif 'azure' in parameters.platform:
error = integrity_rule_sentinel(rule_converted, rule_content, platform, rule_file, parameters, logger, error)
return error

Expand All @@ -237,8 +298,8 @@ def integrity_rule_raw(parameters: dict, export_config: dict, raw_rule=False):
platform = SplunkPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'azure':
platform = SentinelPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'microsoft_defender' and parameters.sentinel_mde:
platform = SentinelPlatform(export_config, parameters.debug, parameters.json)
elif parameters.platform == 'microsoft_defender':
platform = MicrosoftXDRPlatform(export_config, parameters.debug, parameters.json)

if path.is_dir():
error_i = False
Expand Down
Loading

0 comments on commit 1d566cc

Please sign in to comment.