Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit71c74ca

Browse files
committed
added mitre-attack parsing + mitre-attack render to platforms
1 parent7bd8d38 commit71c74ca

File tree

15 files changed

+241
-23
lines changed

15 files changed

+241
-23
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
importjson
2+
importos
3+
importurllib.request
4+
importssl
5+
fromurllib.errorimportHTTPError
6+
7+
fromapp.converter.tools.singleton_metaimportSingletonMeta
8+
fromconstimportROOT_PROJECT_PATH
9+
10+
11+
classMitreConfig(metaclass=SingletonMeta):
12+
config_url:str='https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json'
13+
mitre_source_types:tuple= ('mitre-attack', )
14+
tactics:dict= {}
15+
techniques:dict= {}
16+
17+
@staticmethod
18+
def__revoked_or_deprecated(entry:dict)->bool:
19+
ifentry.get("revoked")orentry.get("x_mitre_deprecated"):
20+
returnTrue
21+
returnFalse
22+
23+
def__get_mitre_json(self)->dict:
24+
ctx=ssl.create_default_context()
25+
ctx.check_hostname=False
26+
ctx.verify_mode=ssl.CERT_NONE
27+
28+
try:
29+
withurllib.request.urlopen(self.config_url,context=ctx)ascti_json:
30+
returnjson.loads(cti_json.read().decode())
31+
exceptHTTPError:
32+
return {}
33+
defupdate_mitre_config(self)->None:
34+
ifnot (mitre_json:=self.__get_mitre_json()):
35+
self.__load_mitre_configs_from_files()
36+
return
37+
38+
tactic_map= {}
39+
technique_map= {}
40+
41+
# Map the tatics
42+
forentryinmitre_json["objects"]:
43+
ifnotentry["type"]=="x-mitre-tactic"orself.__revoked_or_deprecated(entry):
44+
continue
45+
forrefinentry["external_references"]:
46+
ifref["source_name"]=='mitre-attack':
47+
tactic_map[entry["x_mitre_shortname"]]=entry["name"]
48+
self.tactics[entry["name"].replace(' ','_').lower()]= {
49+
"external_id":ref["external_id"],
50+
"url":ref["url"],
51+
"tactic":entry["name"]
52+
}
53+
break
54+
55+
# Map the techniques
56+
forentryinmitre_json["objects"]:
57+
ifnotentry["type"]=="attack-pattern"orself.__revoked_or_deprecated(entry):
58+
continue
59+
ifentry.get("x_mitre_is_subtechnique"):
60+
continue
61+
forrefinentry["external_references"]:
62+
ifref["source_name"]inself.mitre_source_types:
63+
technique_map[ref["external_id"]]=entry["name"]
64+
sub_tactics= []
65+
# Get Mitre Tactics (Kill-Chains)
66+
fortacticinentry["kill_chain_phases"]:
67+
iftactic["kill_chain_name"]inself.mitre_source_types:
68+
# Map the short phase_name to tactic name
69+
sub_tactics.append(tactic_map[tactic["phase_name"]])
70+
self.techniques[ref["external_id"].lower()]= {
71+
"technique_id":ref["external_id"],
72+
"technique":entry["name"],
73+
"url":ref["url"],
74+
"tactic":sub_tactics
75+
}
76+
break
77+
78+
## Map the sub-techniques
79+
forentryinmitre_json["objects"]:
80+
ifnotentry["type"]=="attack-pattern"orself.__revoked_or_deprecated(entry):
81+
continue
82+
ifentry.get("x_mitre_is_subtechnique"):
83+
forrefinentry["external_references"]:
84+
ifref["source_name"]inself.mitre_source_types:
85+
sub_technique_id=ref["external_id"]
86+
sub_technique_name=entry["name"]
87+
parent_technique_name=technique_map[sub_technique_id.split(".")[0]]
88+
sub_technique_name="{} : {}".format(parent_technique_name,sub_technique_name)
89+
self.techniques[ref["external_id"].lower()]= {
90+
"technique_id":ref["external_id"],
91+
"technique":sub_technique_name,
92+
"url":ref["url"],
93+
}
94+
break
95+
96+
def__load_mitre_configs_from_files(self)->None:
97+
withopen(os.path.join(ROOT_PROJECT_PATH,'app/dictionaries/tactics.json'),'r')asfile:
98+
self.tactics=json.load(file)
99+
100+
withopen(os.path.join(ROOT_PROJECT_PATH,'app/dictionaries/techniques.json'),'r')asfile:
101+
self.techniques=json.load(file)
102+
103+
defget_tactic(self,tactic:str)->dict:
104+
tactic=tactic.replace('.','_')
105+
returnself.tactics.get(tactic, {})
106+
107+
defget_technique(self,technique_id:str)->dict:
108+
returnself.techniques.get(technique_id, {})

‎siem-converter/app/converter/core/mixins/rule.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
importjson
2+
fromtypingimportList
23

34
importyaml
45

56
fromapp.converter.core.exceptions.coreimportInvalidYamlStructure,InvalidJSONStructure
7+
fromapp.converter.core.mitreimportMitreConfig
68

79

810
classJsonRuleMixin:
@@ -15,9 +17,28 @@ def load_rule(self, text):
1517

1618

1719
classYamlRuleMixin:
20+
mitre_config:MitreConfig=MitreConfig()
1821

1922
defload_rule(self,text):
2023
try:
2124
returnyaml.safe_load(text)
2225
exceptyaml.YAMLErroraserr:
2326
raiseInvalidYamlStructure(error=str(err))
27+
28+
defparse_mitre_attack(self,tags:List[str])->dict[str,list]:
29+
result= {
30+
'tactics': [],
31+
'techniques': []
32+
}
33+
fortagintags:
34+
tag=tag.lower()
35+
iftag.startswith('attack.'):
36+
tag=tag[7::]
37+
iftag.startswith('t'):
38+
iftechnique:=self.mitre_config.get_technique(tag):
39+
result['techniques'].append(technique)
40+
else:
41+
iftactic:=self.mitre_config.get_tactic(tag):
42+
result['tactics'].append(tactic)
43+
44+
returnresult

‎siem-converter/app/converter/core/models/parser_output.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def __init__(self, *,
1717
license_:str=None,
1818
severity:str=None,
1919
references:List[str]=None,
20-
tags:List[str]=None,
21-
mitre_attack:List[str]=None,
20+
tags:list[str]=None,
21+
mitre_attack:dict[str,list]=None,
2222
status:str=None,
2323
false_positives:List[str]=None,
2424
source_mapping_ids:List[str]=None

‎siem-converter/app/converter/platforms/chronicle/const.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
version = "0.01"
88
rule_id = "<rule_id_place_holder>"
99
status = "<status_place_holder>"
10-
severity = "<severity_place_holder>"
10+
tags = "<tags_place_holder>"
1111
falsepositives = "<falsepositives_place_holder>"
12+
severity = "<severity_place_holder>"
13+
created = "<created_place_holder>"
1214
1315
events:
1416
<query_placeholder>

‎siem-converter/app/converter/platforms/chronicle/renders/chronicle_rule.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,6 @@ def finalize_query(self, prefix: str, query: str, functions: str, meta_info: Met
9696
rule=rule.replace("<severity_place_holder>",meta_info.severity)
9797
rule=rule.replace("<status_place_holder>",meta_info.status)
9898
rule=rule.replace("<falsepositives_place_holder>",', '.join(meta_info.false_positives))
99+
rule=rule.replace("<tags_place_holder>",", ".join(meta_info.tags))
100+
rule=rule.replace("<created_place_holder>",str(meta_info.date))
99101
returnrule

‎siem-converter/app/converter/platforms/elasticsearch/renders/detection_rule.py‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
importcopy
2121
importjson
22+
fromtypingimportUnion
2223

2324
fromapp.converter.platforms.elasticsearch.constimportELASTICSEARCH_DETECTION_RULE,elasticsearch_rule_details
2425
fromapp.converter.platforms.elasticsearch.mappingimportElasticSearchMappings,elasticsearch_mappings
@@ -27,6 +28,7 @@
2728
fromapp.converter.core.models.platform_detailsimportPlatformDetails
2829
fromapp.converter.core.models.parser_outputimportMetaInfoContainer
2930
fromapp.converter.tools.utilsimportconcatenate_str,get_mitre_attack_str
31+
fromapp.converter.core.mitreimportMitreConfig
3032

3133

3234
classElasticSearchRuleFieldValue(ElasticSearchFieldValue):
@@ -36,6 +38,7 @@ class ElasticSearchRuleFieldValue(ElasticSearchFieldValue):
3638
classElasticSearchRuleRender(ElasticSearchQueryRender):
3739
details:PlatformDetails=elasticsearch_rule_details
3840
mappings:ElasticSearchMappings=elasticsearch_mappings
41+
mitre:MitreConfig=MitreConfig()
3942

4043
or_token="OR"
4144
and_token="AND"
@@ -44,6 +47,46 @@ class ElasticSearchRuleRender(ElasticSearchQueryRender):
4447
field_value_map=ElasticSearchRuleFieldValue(or_token=or_token)
4548
query_pattern="{prefix} {query} {functions}"
4649

50+
def__create_mitre_threat(self,mitre_attack:dict)->Union[list,list[dict]]:
51+
ifnotmitre_attack.get('techniques'):
52+
return []
53+
threat= []
54+
55+
ifnotmitre_attack.get('tactics'):
56+
fortechniqueinmitre_attack.get('techniques'):
57+
technique_name=technique['technique']
58+
if'.'intechnique_name:
59+
technique_name=technique_name[:technique_name.index('.')]
60+
threat.append(technique_name)
61+
returnthreat
62+
63+
fortacticinmitre_attack['tactics']:
64+
tactic_render= {
65+
'id':tactic['external_id'],
66+
'name':tactic['tactic'],
67+
'reference':tactic['url']
68+
}
69+
sub_threat= {
70+
'tactic':tactic_render,
71+
'framework':'MITRE ATT&CK',
72+
'technique': []
73+
}
74+
fortechniqueinmitre_attack['techniques']:
75+
technique_id=technique['technique_id'].lower()
76+
if'.'intechnique_id:
77+
technique_id=technique_id[:technique['technique_id'].index('.')]
78+
main_technique=self.mitre.get_technique(technique_id)
79+
iftactic['tactic']inmain_technique['tactic']:
80+
sub_threat['technique'].append({
81+
"id":main_technique['technique_id'],
82+
"name":main_technique['technique'],
83+
"reference":main_technique['url']
84+
})
85+
iflen(sub_threat['technique'])>0:
86+
threat.append(sub_threat)
87+
88+
returnthreat
89+
4790
deffinalize_query(self,prefix:str,query:str,functions:str,meta_info:MetaInfoContainer,
4891
source_mapping:SourceMapping=None,not_supported_functions:list=None):
4992
query=super().finalize_query(prefix=prefix,query=query,functions=functions,meta_info=meta_info)
@@ -61,7 +104,8 @@ def finalize_query(self, prefix: str, query: str, functions: str, meta_info: Met
61104
"severity":meta_info.severity,
62105
"references":meta_info.references,
63106
"license":meta_info.license,
64-
"tags":meta_info.mitre_attack,
107+
"tags":meta_info.tags,
108+
"threat":self.__create_mitre_threat(meta_info.mitre_attack),
65109
"false_positives":meta_info.false_positives
66110
})
67111
rule_str=json.dumps(rule,indent=4,sort_keys=False,ensure_ascii=False)

‎siem-converter/app/converter/platforms/logscale/renders/logscale_alert.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
fromapp.converter.core.mappingimportSourceMapping
2626
fromapp.converter.core.models.platform_detailsimportPlatformDetails
2727
fromapp.converter.core.models.parser_outputimportMetaInfoContainer
28-
fromapp.converter.tools.utilsimportget_rule_description_str
28+
fromapp.converter.tools.utilsimportget_rule_description_str,get_mitre_attack_str
2929

3030

3131
_AUTOGENERATED_TITLE="Autogenerated Falcon LogScale Alert"
@@ -45,10 +45,14 @@ def finalize_query(self, prefix: str, query: str, functions: str, meta_info: Met
4545
rule=copy.deepcopy(DEFAULT_LOGSCALE_ALERT)
4646
rule['query']['queryString']=query
4747
rule['name']=meta_info.titleor_AUTOGENERATED_TITLE
48+
mitre_attack= []
49+
ifmeta_info.mitre_attack:
50+
mitre_attack= [f"ATTACK.{i['tactic']}"foriinmeta_info.mitre_attack.get('tactics', [])]
51+
mitre_attack.extend([f"ATTACK.{i['technique_id']}"foriinmeta_info.mitre_attack.get('techniques', [])])
4852
rule['description']=get_rule_description_str(
4953
description=meta_info.description,
5054
license=meta_info.license,
51-
mitre_attack=meta_info.mitre_attack,
55+
mitre_attack=mitre_attack,
5256
author=meta_info.author
5357
)
5458

‎siem-converter/app/converter/platforms/microsoft/renders/microsoft_sentinel_rule.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ class MicrosoftSentinelRuleRender(MicrosoftSentinelQueryRender):
4040
or_token="or"
4141
field_value_map=MicrosoftSentinelRuleFieldValue(or_token=or_token)
4242

43+
def__create_mitre_threat(self,meta_info:MetaInfoContainer)->tuple[list,list]:
44+
tactics= []
45+
techniques= []
46+
47+
fortacticinmeta_info.mitre_attack.get('tactics'):
48+
tactics.append(tactic['tactic'])
49+
50+
fortechniqueinmeta_info.mitre_attack.get('techniques'):
51+
techniques.append(technique['technique_id'])
52+
53+
returntactics,techniques
54+
4355
deffinalize_query(self,prefix:str,query:str,functions:str,meta_info:MetaInfoContainer,
4456
source_mapping:SourceMapping=None,not_supported_functions:list=None):
4557
query=super().finalize_query(prefix=prefix,query=query,functions=functions,meta_info=meta_info)
@@ -52,7 +64,9 @@ def finalize_query(self, prefix: str, query: str, functions: str, meta_info: Met
5264
license=meta_info.license
5365
)
5466
rule["severity"]=meta_info.severity
55-
rule["techniques"]= [el.upper()forelinmeta_info.mitre_attack]
67+
mitre_tactics,mitre_techniques=self.__create_mitre_threat(meta_info=meta_info)
68+
rule['tactics']=mitre_tactics
69+
rule['techniques']=mitre_techniques
5670
json_rule=json.dumps(rule,indent=4,sort_keys=False)
5771
ifnot_supported_functions:
5872
rendered_not_supported=self.render_not_supported_functions(not_supported_functions)

‎siem-converter/app/converter/platforms/roota/parsers/roota.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1616
-----------------------------------------------------------------
1717
"""
18+
importre
1819

1920
fromapp.converter.core.exceptions.coreimportUnsupportedRootAParser,RootARuleValidationException
2021
fromapp.converter.core.mixins.ruleimportYamlRuleMixin
@@ -27,16 +28,19 @@ class RootAParser(YamlRuleMixin):
2728
parsers=parser_manager
2829
mandatory_fields= {"name","details","author","severity","mitre-attack","detection","references","license"}
2930

30-
@staticmethod
31-
def__update_meta_info(meta_info:MetaInfoContainer,rule:dict)->MetaInfoContainer:
31+
def__update_meta_info(self,meta_info:MetaInfoContainer,rule:dict)->MetaInfoContainer:
3232
mitre_attack=rule.get("mitre-attack")or []
33-
mitre_attack= [i.strip("")foriinmitre_attack.split(",")]ifisinstance(mitre_attack,str)elsemitre_attack
33+
mitre_tags= [i.strip("")foriinmitre_attack.split(",")]ifisinstance(mitre_attack,str)elsemitre_attack
34+
mitre_attack=self.parse_mitre_attack(mitre_tags)
35+
rule_tags=rule.get('tags', [])
36+
rule_tags+=mitre_tags
37+
3438
meta_info.title=rule.get("name")
3539
meta_info.description=rule.get("details")
3640
meta_info.id=rule.get("uuid",meta_info.id)
3741
meta_info.references=rule.get("references")
3842
meta_info.license=rule.get("license",meta_info.license)
39-
meta_info.tags=rule.get("tags",meta_info.tags)
43+
meta_info.tags=rule_tagsormeta_info.tags
4044
meta_info.mitre_attack=mitre_attack
4145
meta_info.date=rule.get("date",meta_info.date)
4246
meta_info.author=rule.get("author",meta_info.author)

‎siem-converter/app/converter/platforms/sigma/parsers/sigma.py‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ class SigmaParser(YamlRuleMixin):
3838
mappings:SigmaMappings=sigma_mappings
3939
mandatory_fields= {"title","description","references","logsource","detection"}
4040

41-
@staticmethod
42-
def__parse_mitre_attack(tags:List[str])->List[str]:
43-
result= []
44-
fortagintags:
45-
ifsearch:=re.search(r"[tT]\d{4}(?:\.\d{3})?",tag):
46-
result.append(search.group())
47-
48-
returnresult
49-
5041
@staticmethod
5142
def__parse_false_positives(false_positives:Union[str,List[str],None])->list:
5243
ifisinstance(false_positives,str):
@@ -62,9 +53,10 @@ def _get_meta_info(self, rule: dict, source_mapping_ids: List[str]) -> MetaInfoC
6253
date=rule.get("date"),
6354
references=rule.get("references", []),
6455
license_=rule.get("license"),
65-
mitre_attack=self.__parse_mitre_attack(rule.get("tags", [])),
56+
mitre_attack=self.parse_mitre_attack(rule.get("tags", [])),
6657
severity=rule.get("level"),
6758
status=rule.get("status"),
59+
tags=rule.get("tags"),
6860
false_positives=self.__parse_false_positives(rule.get("falsepositives")),
6961
source_mapping_ids=source_mapping_ids
7062
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp