diff --git a/Makefile b/Makefile index a9a5a0f6f..8439b5dd7 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ test-sigmac: $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t carbonblack -c tools/config/carbon-black.yml rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t qualys -c tools/config/qualys.yml rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t netwitness -c tools/config/netwitness.yml rules/ > /dev/null + $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t netwitness-epl -c netwitness-epl rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t sumologic -O rulecomment -c tools/config/sumologic.yml rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t humio -O rulecomment -c tools/config/humio.yml rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t crowdstrike -O rulecomment -c tools/config/crowdstrike.yml rules/ > /dev/null @@ -64,6 +65,7 @@ test-sigmac: $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t sqlite -c sysmon rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t csharp -c sysmon rules/ > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t logiq -c sysmon rules/ > /dev/null + $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t sysmon -c sysmon -rvd rules/windows/driver_load rules/windows/file_event rules/windows/image_load rules/windows/network_connection rules/windows/process_access rules/windows/process_creation rules/windows/registry_event rules/windows/sysmon > /dev/null $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t splunk -c tools/config/splunk-windows-index.yml -f 'level>=high,level<=critical,status=stable,logsource=windows,tag=attack.execution' rules/ > /dev/null ! $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t splunk -c tools/config/splunk-windows-index.yml -f 'level>=high,level<=critical,status=xstable,logsource=windows' rules/ > /dev/null ! $(COVERAGE) run -a --include=$(COVSCOPE) tools/sigmac -rvdI -t splunk -c tools/config/splunk-windows-index.yml -f 'level>=high,level<=xcritical,status=stable,logsource=windows' rules/ > /dev/null diff --git a/rules/windows/builtin/win_dcsync.yml b/rules/windows/builtin/win_dcsync.yml index 5babb1379..cfe2bd114 100644 --- a/rules/windows/builtin/win_dcsync.yml +++ b/rules/windows/builtin/win_dcsync.yml @@ -3,8 +3,8 @@ id: 611eab06-a145-4dfa-a295-3ccc5c20f59a description: Detects Mimikatz DC sync security events status: experimental date: 2018/06/03 -modified: 2019/10/08 -author: Benjamin Delpy, Florian Roth +modified: 2020/09/11 +author: Benjamin Delpy, Florian Roth, Scott Dermott references: - https://twitter.com/gentilkiwi/status/1003236624925413376 - https://gist.github.com/gentilkiwi/dcc132457408cf11ad2061340dcb53c2 @@ -28,6 +28,7 @@ detection: SubjectUserName: - 'NT AUTHORITY*' - '*$' + - 'MSOL_*' condition: selection and not filter1 and not filter2 falsepositives: - Valid DC Sync that is not covered by the filters; please report diff --git a/rules/windows/builtin/win_disable_event_logging.yml b/rules/windows/builtin/win_disable_event_logging.yml index da1f92fe0..52ef34e3f 100644 --- a/rules/windows/builtin/win_disable_event_logging.yml +++ b/rules/windows/builtin/win_disable_event_logging.yml @@ -6,7 +6,7 @@ references: tags: - attack.defense_evasion - attack.t1054 # an old one - - attack.t1562.006 + - attack.t1562.002 author: '@neu5ron' date: 2017/11/19 logsource: diff --git a/rules/windows/other/win_defender_psexec_wmi_asr.yml b/rules/windows/other/win_defender_psexec_wmi_asr.yml index 850023895..6761ba143 100644 --- a/rules/windows/other/win_defender_psexec_wmi_asr.yml +++ b/rules/windows/other/win_defender_psexec_wmi_asr.yml @@ -10,9 +10,8 @@ date: 2020/07/14 tags: - attack.execution - attack.lateral_movement - - attack.t1570 - attack.t1047 - - attack.t1569 + - attack.t1035 # an old one - attack.t1569.002 logsource: product: windows_defender diff --git a/rules/windows/process_access/sysmon_invoke_phantom.yml b/rules/windows/process_access/sysmon_invoke_phantom.yml index a389a9a56..bbcf116ae 100755 --- a/rules/windows/process_access/sysmon_invoke_phantom.yml +++ b/rules/windows/process_access/sysmon_invoke_phantom.yml @@ -10,7 +10,7 @@ references: - https://twitter.com/timbmsft/status/900724491076214784 tags: - attack.defense_evasion - - attck.t1562.002 + - attack.t1562.002 - attack.t1089 # an old one logsource: category: process_access diff --git a/rules/windows/process_creation/win_etw_trace_evasion.yml b/rules/windows/process_creation/win_etw_trace_evasion.yml index 84c4fa7b8..71bb05e64 100644 --- a/rules/windows/process_creation/win_etw_trace_evasion.yml +++ b/rules/windows/process_creation/win_etw_trace_evasion.yml @@ -11,7 +11,7 @@ date: 2019/03/22 tags: - attack.defense_evasion - attack.t1070 - - attack.t1562 + - attack.t1562.006 - car.2016-04-002 level: high logsource: diff --git a/rules/windows/process_creation/win_susp_covenant.yml b/rules/windows/process_creation/win_susp_covenant.yml index 40fa8950f..d2440ff5c 100644 --- a/rules/windows/process_creation/win_susp_covenant.yml +++ b/rules/windows/process_creation/win_susp_covenant.yml @@ -8,7 +8,9 @@ author: Florian Roth date: 2020/06/04 tags: - attack.execution + - attack.defense_evasion - attack.t1059.001 + - attack.t1564.003 - attack.t1086 # an old one logsource: category: process_creation diff --git a/rules/windows/process_creation/win_susp_process_creations.yml b/rules/windows/process_creation/win_susp_process_creations.yml deleted file mode 100644 index dd177e58c..000000000 --- a/rules/windows/process_creation/win_susp_process_creations.yml +++ /dev/null @@ -1,74 +0,0 @@ -title: Suspicious Process Creation -id: 5f0f47a5-cb16-4dbe-9e31-e8d976d73de3 -status: experimental -description: Detects suspicious process starts on Windows systems based on keywords -author: Florian Roth, Daniil Yugoslavskiy, oscd.community (update) -date: 2018/01/01 -modified: 2019/11/01 -references: - - https://www.swordshield.com/2015/07/getting-hashes-from-ntds-dit-file/ - - https://www.youtube.com/watch?v=H3t_kHQG1Js&feature=youtu.be&t=15m35s - - https://winscripting.blog/2017/05/12/first-entry-welcome-and-uac-bypass/ - - https://twitter.com/subTee/status/872244674609676288 - - https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/remote-tool-examples - - https://tyranidslair.blogspot.ca/2017/07/dg-on-windows-10-s-executing-arbitrary.html - - https://www.trustedsec.com/2017/07/new-tool-release-nps_payload/ - - https://subt0x10.blogspot.ca/2017/04/bypassing-application-whitelisting.html - - https://gist.github.com/subTee/7937a8ef07409715f15b84781e180c46#file-rat-bat - - https://twitter.com/vector_sec/status/896049052642533376 - - http://security-research.dyndns.org/pub/slides/FIRST-TC-2018/FIRST-TC-2018_Tom-Ueltschi_Sysmon_PUBLIC.pdf -logsource: - category: process_creation - product: windows -detection: - selection: - CommandLine: - - '* sekurlsa:*' - - net localgroup administrators * /add - - net group "Domain Admins" * /ADD /DOMAIN - - certutil.exe *-urlcache* http* - - certutil.exe *-urlcache* ftp* - - netsh advfirewall firewall *\AppData\\* - - attrib +S +H +R *\AppData\\* - - schtasks* /create *\AppData\\* - - schtasks* /sc minute* - - '*\Regasm.exe *\AppData\\*' - - '*\Regasm *\AppData\\*' - - '*\bitsadmin* /transfer*' - - '*\certutil.exe * -decode *' - - '*\certutil.exe * -decodehex *' - - '*\certutil.exe -ping *' - - icacls * /grant Everyone:F /T /C /Q - - '* wbadmin.exe delete catalog -quiet*' - - '*\wscript.exe *.jse' - - '*\wscript.exe *.js' - - '*\wscript.exe *.vba' - - '*\wscript.exe *.vbe' - - '*\cscript.exe *.jse' - - '*\cscript.exe *.js' - - '*\cscript.exe *.vba' - - '*\cscript.exe *.vbe' - - '*\fodhelper.exe' - - '*waitfor*/s*' - - '*waitfor*/si persist*' - - '*remote*/s*' - - '*remote*/c*' - - '*remote*/q*' - - '*AddInProcess*' - - '* /stext *' - - '* /scomma *' - - '* /stab *' - - '* /stabular *' - - '* /shtml *' - - '* /sverhtml *' - - '* /sxml *' - condition: selection -fields: - - ComputerName - - User - - CommandLine -falsepositives: - - False positives depend on scripts and administrative tools used in the monitored environment -level: medium -tags: - - car.2013-07-001 \ No newline at end of file diff --git a/rules/windows/registry_event/sysmon_susp_lsass_dll_load.yml b/rules/windows/registry_event/sysmon_susp_lsass_dll_load.yml index bf440234b..e7ff37013 100644 --- a/rules/windows/registry_event/sysmon_susp_lsass_dll_load.yml +++ b/rules/windows/registry_event/sysmon_susp_lsass_dll_load.yml @@ -19,9 +19,9 @@ detection: condition: selection tags: - attack.execution + - attack.persistence - attack.t1177 # an old one - attack.t1547.008 falsepositives: - Unknown level: high - diff --git a/tools/config/netwitness-epl.yml b/tools/config/netwitness-epl.yml new file mode 100644 index 000000000..1709092b5 --- /dev/null +++ b/tools/config/netwitness-epl.yml @@ -0,0 +1,92 @@ +title: NetWitness +order: 20 +backends: + - netwitness-epl +logsources: + linux: + product: linux + conditions: + device.class: rhlinux + linux-sshd: + product: linux + service: sshd + conditions: + device.class: rhlinux + client: sshd + linux-auth: + product: linux + service: auth + conditions: + device.class: rhlinux + linux-clamav: + product: linux + service: clamav + conditions: + device.class: rhlinux + windows-sys: + product: windows + service: sysmon + conditions: + device.type: winevent_nic + event.source: microsoft-windows-security-auditing + windows-power: + product: windows + service: powershell + conditions: + device.type: winevent_nic + windows-dhcp: + product: windows + service: dhcp + conditions: + device.type: winevent_nic + event.source: microsoft-windows-dhcp-server + windows-sec: + product: windows + service: security + conditions: + device.type: winevent_nic + event.source: microsoft-windows-security-auditing + windows-system: + product: windows + service: system + conditions: + device.type: winevent_nic +fieldmappings: + dst: + - ip.dst + dst_ip: + - ip.dst + src: + - ip.src + src_ip: + - ip.src + DestinationPort: + - ip.dstport + EventID: + - reference.id + NewProcessName: + - process + LogonType: + - logon.type + AccountName: + - user.dst + c-uri-extension: + - extension + c-useragent: + - user.agent + r-dns: + - alias.host + DestinationHostname: + - alias.host + cs-host: + - alias.host + c-uri-query: + - web.page + c-uri: + - web.page + cs-method: + - action + cs-cookie: + - web.cookie + SubjectUserName: + - user.dst diff --git a/tools/sigma/backends/netwitness-epl.py b/tools/sigma/backends/netwitness-epl.py new file mode 100644 index 000000000..e580b259c --- /dev/null +++ b/tools/sigma/backends/netwitness-epl.py @@ -0,0 +1,163 @@ +# NetWitness EPL output backend for sigmac +# Copyright 2019 Tarik BOUDJEMAA (@snake-jump) +# Inspired from John Tuckner (@tuckner) NetWitness output backend for sigmac + +# NetWitness EPL backend for sigmac uses netwitness-epl.yml config file + + +# RSA Alerts are generated by Event Processing Language (EPL) , that uses Esper Engine (https://www.espertech.com/esper/) +# For more details see :https://community.rsa.com/docs/DOC-110246 and https://community.rsa.com/docs/DOC-80068 + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + + +import re +import sigma +from .base import SingleTextQueryBackend +from .mixins import MultiRuleOutputMixin +from sigma.parser.modifiers.base import SigmaTypeModifier +from sigma.parser.modifiers.type import SigmaRegularExpressionModifier + +template=""" +module_XXXXX; +@Name('RuleName') +@RSAAlert(oneInSeconds=0) +SELECT * FROM Event( + +EXPRESSION +);""" + +class NetWitnessEplBackend(SingleTextQueryBackend): + """Converts Sigma rule into RSA NetWitness EPL . Contributed by @snake-jump""" + identifier = "netwitness-epl" + config_required = False + default_config = ["sysmon","netwitness-epl"] + active = True + reEscape = re.compile('(")') + #reEscape = re.compile("([\\|()\[\]{}.^$+])") + reClear = None + andToken = " AND " + orToken = " OR " + notToken = "NOT" + subExpression = "(%s)" + listExpression = "(%s)" + listSeparator = ", " + valueExpression = "\'%s\'" + keyExpression = "%s" + nullExpression = "%s exists" + notNullExpression = "%s exists" + mapExpression = "(%s=%s)" + mapListsSpecialHandling = True + + def generateMapItemNode(self, node): + key, value = node + if type(key) != int: + key = key.replace(".","_") ## replace . by _ in meta name (RSA EPL) + key = key.lower() + if self.mapListsSpecialHandling == False and type(value) in (str, int, list) or self.mapListsSpecialHandling == True and type(value) in (str, int): + if type(value) == str and "*" in value[1:-1]: + value = re.sub('([".^$]|\\\\(?![*?]))', '\\\\\g<1>', value) + value = re.sub('\\*', '.*', value) + value = re.sub('\\?', '.', value) + return "(%s REGEXP %s)" %(key, self.generateValueNode(value)) + elif type(value) == str and "*" in value: + value = re.sub("(\*\\\\)|(\*)", "", value) + value = self.generateValueNode("%"+value+"%") # add "%" to construct the like expression ex: process like %psexesvc% + return "(%s LIKE %s)" % (key,value) + elif type(value) in (str, int): + return self.mapExpression % (key, self.generateValueNode(value)) + else: + return self.mapExpression % (key, self.generateNode(value)) + elif type(value) == list: + return self.generateMapItemListNode(key, value) + elif value is None: + return self.nullExpression % (key, ) + + elif type(value) == SigmaRegularExpressionModifier: ## if value is regex + regex = str(value) + ## in RSA netwitness EPL regex each backslash must be escaped by backslash + ## ex : c:\temp\ to regex -> c:\\temp\\ to RSA EPL regex --> c:\\\\temp\\\\ + regex = regex.replace("\\","\\\\") + # Regular Expressions have to match the full value in RSA Netwitness EPL + if not (regex.startswith('^') or regex.startswith('.*')): + regex = '.*' + regex + if not (regex.endswith('$') or regex.endswith('.*')): + regex = regex + '.*' + return "(%s REGEXP %s)" %(key, self.generateValueNode(regex)) + else: + raise TypeError("Backend does not support map values of type " + str(type(value))) + + def generateMapItemListNode(self, key, value): + equallist = list() + containlist = list() + regexlist = list() + for item in value: + if type(item) == str and "*" in item[1:-1]: + item = re.sub('([".^$]|\\\\(?![*?]))', '\\\\\g<1>', item) + item = re.sub('\\*', '.*', item) + item = re.sub('\\?', '.', item) + regexlist.append(self.generateValueNode(item)) + elif type(item) == str and (item.endswith("*") or item.startswith("*")): + item_temp=item + item = re.sub("(\*\\\\)|(\*)", "", item) + if item_temp.endswith("*") and item_temp.startswith("*"): # pattern begins with "*" and ends with "*" + containlist.append(self.generateValueNode('%'+item+'%')) # add "%" to construct the like expression ex: process like %psexesvc% + elif item_temp.startswith("*"): # pattern don't end with "*" + containlist.append(self.generateValueNode('%'+item)) + else: # item_temp.endswith("*") pattern don't begin with "*" + containlist.append(self.generateValueNode(item+'%')) + else: + equallist.append(self.generateValueNode(item)) + fmtitems = list() + if equallist: + if len(equallist) == 1: + fmtitems.append("%s = %s" % (key, ", ".join(equallist))) + else: + # add "(" and ")" to the first and the last item from the list to have meta_key IN ('value1','value2') + equallist[0]=("("+equallist[0]) + equallist[-1]=(equallist[-1]+")") + fmtitems.append("%s IN %s" % (key, ", ".join(equallist))) + + if containlist: + fmtitems.append("%s LIKE %s" % (key, (" OR "+key+" LIKE ").join(containlist))) + if regexlist: + fmtitems.append("%s REGEXP %s" % (key, "|".join(regexlist))) + fmtquery = "("+" OR ".join(filter(None, fmtitems)) + # Delete the " ' " from the begin or the end of each regex pattern ex : '.*('patern1'|'patern2').*' --> '.*(patern1|patern2).*' + fmtquery = re.sub('\'\.\*\(\'','\'.*(',fmtquery) + fmtquery = re.sub('\'\)\.\*\'',').*\'',fmtquery) + fmtquery = re.sub('\'\|\'','|',fmtquery) + fmtquery = fmtquery+')' + return fmtquery + + def generateValueNode(self, node): + return self.valueExpression % (str(node)) + + def generate(self, sigmaparser): + """Method is called for each sigma rule and receives the parsed rule (SigmaParser)""" + for parsed in sigmaparser.condparsed: + query = self.generateQuery(parsed, sigmaparser) + query=query.replace('INDEX','`index`') # index is reserved keyword in Esper and must be escaped + query=template.replace('EXPRESSION', query) + try: + query=query.replace('RuleName', sigmaparser.parsedyaml["title"].replace(" ","")) # add rule name + query=query.replace('module_XXXXX', "module "+sigmaparser.parsedyaml["title"].replace(" ","")) # add rule name + except: + print("Error when replacing RuleName by Title from yaml") + pass + return query + + def generateQuery(self, parsed, sigmaparser): + result = self.generateNode(parsed.parsedSearch) + return result diff --git a/tools/sigma/backends/sysmon.py b/tools/sigma/backends/sysmon.py new file mode 100644 index 000000000..12171dc34 --- /dev/null +++ b/tools/sigma/backends/sysmon.py @@ -0,0 +1,244 @@ +import re + +import sigma +from sigma.backends.base import SingleTextQueryBackend +from sigma.backends.mixins import MultiRuleOutputMixin + +from .exceptions import NotSupportedError + + +class SysmonConfigBackend(SingleTextQueryBackend, MultiRuleOutputMixin): + identifier = "sysmon" + active = True + andToken = " AND " + orToken = " OR " + notToken = "NOT " + subExpression = "(%s)" + config_required = False + INCLUDE = "include" + EXCLUDE = "exclude" + conditionDict = { + "startswith": "begin with", + "endswith": "end with", + } + + def __init__(self, *args, **kwargs): + self.table = None + self.logsource = None + self.allowedSource = { + "process_creation": "ProcessCreate" + } + self.eventidTagMapping = { + 1: "ProcessCreate", + 4799: "ProcessCreate", + 2: "FileCreateTime", + 3: "NetworkConnect", + 5: "ProcessTerminate", + 6: "DriverLoad", + 7: "ImageLoad", + 8: "CreateRemoteThread", + 9: "RawAccessRead", + 10: "ProcessAccess", + 11: "FileCreate", + 12: "RegistryEvent", + 13: "RegistryEvent", + 14: "RegistryEvent", + 15: "FileCreateStreamHash", + 17: "PipeEvent", + 18: "PipeEvent", + 19: "WmiEvent", + 20: "WmiEvent", + 21: "WmiEvent", + 22: "DNSQuery", + 257: "DNSQuery", + 23: "FileDelete" + } + self.allowedCondCombinations = { + 'single': [ + [4], + [1, 4], + [2, 4], + ], + 'multi': [ + [1, 2, 4], + ], + "exclude": [ + [1, 3, 4], + [2, 3, 4] + ], + # "multi-exclude": [ + # [1, 2, 3, 4] + # ] + } + return super().__init__(*args, **kwargs) + + def cleanValue(self, value): + val = re.sub("[*]", "", value) + return val + + def mapFiledValue(self, field, value): + condition = None + if "|" in field: + field, *pipes = field.split("|") + if len(pipes) == 1: + condition = pipes[0] + else: + raise NotImplementedError("not implemented condition") + if isinstance(value, list) and len(value) > 1: + condition = "contains any" + value = ";".join(value) + elif "*" in value: + if value.startswith("*") and value.endswith("*"): + condition = "contains" + elif value.startswith("*"): + condition = "end with" + elif value.endswith("*"): + condition = "begin with" + else: + condition = "contains" + + if condition: + field_str = '<{field} condition="{condition}">{value}'.format(field=field, + condition=condition, + value=self.cleanValue(value)) + else: + field_str = '<{field}>{value}'.format(field=field, value=self.cleanValue(value)) + + return field_str + + def createRule(self, selections): + fields_list = [] + table = None + for field, value in selections.items(): + if isinstance(value, list) and len(value) == 1: + value = value[0] + if field == "EventID": + try: + table = self.eventidTagMapping[value] + except KeyError: + table = self.eventidTagMapping[1] + else: + created_field_value = self.mapFiledValue(field, value) + fields_list.append(created_field_value) + fields_list_filtered = [item for item in fields_list if item] + if any(fields_list_filtered): + rule = '''\n\t\t\n\t\t\t{fields}\n\t\t'''.format(rule_name=self.rule_name, fields="\n\t\t\t".join(["{}".format(item) for item in fields_list_filtered])) + t = table if table else self.table + return rule, t + else: + return None, None + + def createRuleGroup(self, condition_objects, condition, match_type="include"): + rules = None + rules_selections = [item for item in condition_objects if item.type == 4] + if len(rules_selections) == 1: + rule, table = self.createRule(self.detection.get(rules_selections[0].matched)) + rules = {match_type: {table: rule}} + else: + if "or" in condition.lower(): + result = {} + for selection_object in rules_selections: + rule, table = self.createRule(self.detection.get(selection_object.matched)) + if result.get(table): + result[table].append(rule) + else: + result[table] = [rule] + result = {table_name: "\n\t\t".join(rules_list) for table_name, rules_list in result.items()} + rules = {match_type: result} + elif "and" in condition.lower(): + rules_dict = {} + for selection_object in rules_selections: + rules_dict.update(self.detection.get(selection_object.matched)) + rule, table = self.createRule(rules_dict) + rules = {match_type: {table: rule}} + if rules: + rules_result = [] + for match, tables in rules.items(): + for table, rules in tables.items(): + category_comment = '\n\n{}'.format(table, match, + "".join(rules)) + rules_result.append(category_comment) + return "".join(rules_result) + else: + raise NotSupportedError("Couldn't create rule with current condition.") + + def createMultiRuleGroup(self, conditions): + conditions_id = "".join([str(item.type) for item in conditions]) + or_index = conditions_id.index("2") + sorted_conditions = [conditions[:or_index], conditions[or_index+1:]] + if sorted_conditions: + result = "" + for rule_condition in sorted_conditions: + rule = self.createRuleGroup(condition_objects=rule_condition, condition=" ".join([item.matched for item in rule_condition])) + result += "{}\n".format(rule) + return result + else: + raise NotSupportedError("Not implemented condition.") + + def createExcludeRuleGroup(self, conditions): + conditions_id = "".join([str(item.type) for item in conditions]) + condition = self.detection.get("condition") + sorted_conditions = None + if "and not" in condition.lower(): + andnot_index = conditions_id.index("13") + sorted_conditions = [(conditions[:andnot_index], self.INCLUDE), ([item for item in conditions if item.type != 3], self.EXCLUDE)] + elif "or not" in condition.lower(): + ornot_index = conditions_id.index("23") + sorted_conditions = [(conditions[:ornot_index], self.INCLUDE), (conditions[ornot_index + 2:], self.EXCLUDE)] + if sorted_conditions: + result = "" + for rule_condition in sorted_conditions: + rule = self.createRuleGroup(condition_objects=rule_condition[0], condition=" ".join([item.matched for item in rule_condition[0]]), match_type=rule_condition[1]) + result += "{}\n".format(rule) + return result + + def checkRuleCondition(self, condtokens): + if len(condtokens) == 1: + conditions = [item for item in condtokens[0].tokens] + conditions_combination = list(set([item.type for item in conditions])) + for rule_type, combinations in self.allowedCondCombinations.items(): + for combination in combinations: + if sorted(conditions_combination) == sorted(combination): + return rule_type, conditions + else: + raise NotSupportedError("Not supported condition.") + else: + raise NotSupportedError("Not supported condition.") + + def createTableFromLogsource(self): + if self.logsource.get("product", "") != "windows": + raise NotSupportedError( + "Not supported logsource. Should be product `windows`.") + for item in self.logsource.values(): + if item.lower() in self.allowedSource.keys(): + self.table = self.allowedSource.get(item.lower()) + break + else: + self.table = "ProcessCreate" + + def checkDetection(self): + for selection_name, value in self.detection.items(): + if isinstance(value, list): + raise NotSupportedError("Keywords are not supported in sysmon backend.") + + + def generate(self, sigmaparser): + sysmon_rule = None + title = sigmaparser.parsedyaml.get("title", "") + author = sigmaparser.parsedyaml.get("author", {}) + self.rule_name = "{} by {}".format(title, author) + self.detection = sigmaparser.parsedyaml.get("detection", {}) + self.checkDetection() + self.logsource = sigmaparser.parsedyaml["logsource"] + self.createTableFromLogsource() + rule_type, conditions = self.checkRuleCondition(sigmaparser.condtoken) + if rule_type == "single": + sysmon_rule = self.createRuleGroup(conditions, self.detection.get("condition")) + elif rule_type == "multi": + sysmon_rule = self.createMultiRuleGroup(conditions) + elif rule_type == "exclude": + sysmon_rule = self.createExcludeRuleGroup(conditions) + + if sysmon_rule: + rulegroup_comment = '' + return "{}\n{}".format(rulegroup_comment, sysmon_rule) \ No newline at end of file