Add Azure Sentinel backend
The web interface expects ARM templates.
This commit is contained in:
@@ -3,6 +3,7 @@ order: 20
|
||||
backends:
|
||||
- ala
|
||||
- ala-rule
|
||||
- sentinel-rule
|
||||
fieldmappings:
|
||||
ComputerName: Computer
|
||||
Event-ID: EventID
|
||||
|
||||
@@ -19,6 +19,8 @@ import re
|
||||
import json
|
||||
import xml.etree.ElementTree as xml
|
||||
|
||||
from datetime import timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from sigma.config.mapping import (
|
||||
SimpleFieldMapping, MultiFieldMapping, ConditionalFieldMapping
|
||||
@@ -423,6 +425,28 @@ class AzureAPIBackend(AzureLogAnalyticsBackend):
|
||||
|
||||
return tactics, technics
|
||||
|
||||
def timeframeToDelta(self, timeframe):
|
||||
time_unit = timeframe[-1:]
|
||||
duration = int(timeframe[:-1])
|
||||
return (
|
||||
time_unit == "s" and timedelta(seconds=duration) or
|
||||
time_unit == "m" and timedelta(minutes=duration) or
|
||||
time_unit == "h" and timedelta(hours=duration) or
|
||||
time_unit == "d" and timedelta(days=duration) or
|
||||
None
|
||||
)
|
||||
|
||||
def iso8601_duration(self, delta):
|
||||
if not delta:
|
||||
return "PT0S"
|
||||
if not delta.seconds:
|
||||
return "P%dD" % (delta.days)
|
||||
days = delta.days and "%dD" % (delta.days) or ""
|
||||
hours = delta.seconds // 3600 % 24 and "%dH" % (delta.seconds // 3600 % 24) or ""
|
||||
minutes = delta.seconds // 60 % 60 and "%dM" % (delta.seconds // 60 % 60) or ""
|
||||
seconds = delta.seconds % 60 and "%dS" % (delta.seconds % 60) or ""
|
||||
return "P%sT%s%s%s" % (days, hours, minutes, seconds)
|
||||
|
||||
def create_rule(self, config):
|
||||
tags = config.get("tags", [])
|
||||
|
||||
@@ -430,17 +454,21 @@ class AzureAPIBackend(AzureLogAnalyticsBackend):
|
||||
tactics, technics = self.skip_tactics_or_techniques(technics, tactics)
|
||||
tactics = list(map(lambda s: s.replace(" ", ""), tactics))
|
||||
|
||||
timeframe = self.timeframeToDelta(config["detection"].setdefault("timeframe", "30m"))
|
||||
queryDuration = self.iso8601_duration(timeframe)
|
||||
suppressionDuration = self.iso8601_duration(timeframe * 5)
|
||||
|
||||
rule = {
|
||||
"displayName": "{} by {}".format(config.get("title"), config.get('author')),
|
||||
"description": "{} {}".format(config.get("description"), "Technique: {}.".format(",".join(technics))),
|
||||
"severity": self.parse_severity(config.get("level", "medium")),
|
||||
"enabled": True,
|
||||
"query": config.get("translation"),
|
||||
"queryFrequency": "12H",
|
||||
"queryPeriod": "12H",
|
||||
"queryFrequency": queryDuration,
|
||||
"queryPeriod": queryDuration,
|
||||
"triggerOperator": "GreaterThan",
|
||||
"triggerThreshold": 0,
|
||||
"suppressionDuration": "12H",
|
||||
"suppressionDuration": suppressionDuration,
|
||||
"suppressionEnabled": True,
|
||||
"tactics": tactics
|
||||
}
|
||||
@@ -455,3 +483,66 @@ class AzureAPIBackend(AzureLogAnalyticsBackend):
|
||||
return rule
|
||||
else:
|
||||
raise NotSupportedError("No table could be determined from Sigma rule")
|
||||
|
||||
class SentinelBackend(AzureAPIBackend):
|
||||
"""Converts Sigma rule into Azure Sentinel scheduled alert rule ARM template."""
|
||||
identifier = "sentinel-rule"
|
||||
active = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def generate(self, sigmaparser):
|
||||
translation = super().generate(sigmaparser)
|
||||
if translation:
|
||||
configs = sigmaparser.parsedyaml
|
||||
configs.update({"translation": translation})
|
||||
rule = self.create_sentinel_rule(configs)
|
||||
return json.dumps(rule)
|
||||
|
||||
def create_sentinel_rule(self, config):
|
||||
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/child-resource-name-type#outside-parent-resource
|
||||
# https://docs.microsoft.com/en-us/azure/templates/microsoft.operationalinsights/workspaces?tabs=json
|
||||
# https://docs.microsoft.com/en-us/rest/api/securityinsights/alert-rules/create-or-update#scheduledalertrule
|
||||
properties = json.loads(config.get("translation"))
|
||||
properties.update({
|
||||
"incidentConfiguration": {
|
||||
"createIncident": True,
|
||||
"groupingConfiguration": {
|
||||
"enabled": False,
|
||||
"reopenClosedIncident": False,
|
||||
"lookbackDuration": properties['suppressionDuration'],
|
||||
"matchingMethod": "AllEntities",
|
||||
"groupByEntities": [],
|
||||
"groupByAlertDetails": [],
|
||||
"groupByCustomDetails": [],
|
||||
},
|
||||
},
|
||||
"eventGroupingSettings": {
|
||||
"aggregationKind": "SingleAlert",
|
||||
},
|
||||
"alertDetailsOverride": None,
|
||||
"customDetails": None,
|
||||
"templateVersion": "1.0.0",
|
||||
})
|
||||
rule_uuid = config.get("id", str(uuid4()))
|
||||
return {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"workspace": {
|
||||
"type": "String",
|
||||
},
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/" + rule_uuid + "')]",
|
||||
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/" + rule_uuid + "')]",
|
||||
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
|
||||
"apiVersion": "2021-03-01-preview",
|
||||
|
||||
"kind": "Scheduled",
|
||||
"properties": properties,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user