mirror of
https://github.com/valitydev/atomic-threat-coverage.git
synced 2024-11-06 01:25:21 +00:00
fix #189
This commit is contained in:
parent
18e780c398
commit
83aab8a5e9
@ -130,8 +130,8 @@ if __name__ == '__main__':
|
||||
dn=args.dataneeded, dr=args.detectionrule,
|
||||
tg=args.triggers, en=args.enrichment,
|
||||
ra=args.responseactions, rp=args.responseplaybook,
|
||||
cu=args.customers, hp=args.hardeningpolicy,
|
||||
init=args.init)
|
||||
rs=args.responsestage, cu=args.customers,
|
||||
hp=args.hardeningpolicy, init=args.init)
|
||||
|
||||
elif args.react_stix:
|
||||
GenerateSTIX()
|
||||
|
@ -11,6 +11,7 @@ from triggers import Triggers
|
||||
from enrichment import Enrichment
|
||||
from responseaction import ResponseAction
|
||||
from responseplaybook import ResponsePlaybook
|
||||
from responsestage import ResponseStage
|
||||
from customer import Customer
|
||||
from attack_mapping import te_mapping # , ta_mapping
|
||||
|
||||
@ -30,11 +31,11 @@ class PopulateConfluence:
|
||||
"""Desc"""
|
||||
|
||||
def __init__(self, auth, lp=False, dn=False, dr=False, en=False, tg=False,
|
||||
ra=False, rp=False, cu=False, ms=False, mp=False, hp=False,
|
||||
ra=False, rp=False, rs=False, cu=False, ms=False, mp=False, hp=False,
|
||||
auto=False, art_dir=False, atc_dir=False, lp_path=False,
|
||||
dn_path=False, dr_path=False, en_path=False, tg_path=False,
|
||||
ra_path=False, rp_path=False, cu_path=False, hp_path=False,
|
||||
ms_path=False, mp_path=False, init=False):
|
||||
ra_path=False, rp_path=False, rs_path=False, cu_path=False,
|
||||
hp_path=False, ms_path=False, mp_path=False, init=False):
|
||||
"""Desc"""
|
||||
|
||||
self.auth = auth
|
||||
@ -79,8 +80,10 @@ class PopulateConfluence:
|
||||
self.data_needed(dn_path)
|
||||
self.enrichment(en_path)
|
||||
self.triggers(tg_path)
|
||||
self.response_stage(rs_path)
|
||||
self.response_action(ra_path)
|
||||
self.response_playbook(rp_path)
|
||||
self.response_stage(rs_path)
|
||||
self.detection_rule(dr_path)
|
||||
self.customer(cu_path)
|
||||
|
||||
@ -106,11 +109,18 @@ class PopulateConfluence:
|
||||
self.detection_rule(dr_path)
|
||||
|
||||
if ra:
|
||||
print("[*] We need to create Response Stages first...")
|
||||
self.response_stage(rs_path)
|
||||
self.response_action(ra_path)
|
||||
print("[*] Updating Response Stages...")
|
||||
self.response_stage(rs_path)
|
||||
|
||||
if rp:
|
||||
self.response_playbook(rp_path)
|
||||
|
||||
if rs:
|
||||
self.response_stage(rs_path)
|
||||
|
||||
if tg:
|
||||
self.triggers(tg_path)
|
||||
|
||||
@ -127,7 +137,7 @@ class PopulateConfluence:
|
||||
def triggers(self, tg_path):
|
||||
"""Populate Triggers"""
|
||||
|
||||
print("Populating Triggers..")
|
||||
print("[*] Populating Triggers...")
|
||||
if tg_path:
|
||||
tg_list = glob.glob(tg_path + '*.yml')
|
||||
else:
|
||||
@ -160,12 +170,12 @@ class PopulateConfluence:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
|
||||
print("Triggers populated!")
|
||||
print("[+] Triggers populated!")
|
||||
|
||||
def hardening_policy(self, hp_path):
|
||||
"""Populate Hardening Policies"""
|
||||
|
||||
print("Populating Hardening Policies..")
|
||||
print("[*] Populating Hardening Policies...")
|
||||
if hp_path:
|
||||
hp_list = glob.glob(hp_path + '*.yml')
|
||||
else:
|
||||
@ -195,12 +205,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Hardening Policies populated!")
|
||||
print("[+] Hardening Policies populated!")
|
||||
|
||||
def mitigation_system(self, ms_path):
|
||||
"""Populate Mitigation Systems"""
|
||||
|
||||
print("Populating Mitigation Systems..")
|
||||
print("[*] Populating Mitigation Systems...")
|
||||
if ms_path:
|
||||
ms_list = glob.glob(ms_path + '*.yml')
|
||||
else:
|
||||
@ -230,12 +240,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Mitigation Systems populated!")
|
||||
print("[+] Mitigation Systems populated!")
|
||||
|
||||
def mitigation_policy(self, mp_path):
|
||||
"""Populate Mitigation Policies"""
|
||||
|
||||
print("Populating Mitigation Policies..")
|
||||
print("[*] Populating Mitigation Policies...")
|
||||
if mp_path:
|
||||
mp_list = glob.glob(mp_path + '*.yml')
|
||||
else:
|
||||
@ -266,12 +276,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Mitigation Policies populated!")
|
||||
print("[+] Mitigation Policies populated!")
|
||||
|
||||
def logging_policy(self, lp_path):
|
||||
"""Desc"""
|
||||
|
||||
print("Populating Logging Policies..")
|
||||
print("[*] Populating Logging Policies...")
|
||||
if lp_path:
|
||||
lp_list = glob.glob(lp_path + '*.yml')
|
||||
else:
|
||||
@ -302,12 +312,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Logging Policies populated!")
|
||||
print("[+] Logging Policies populated!")
|
||||
|
||||
def data_needed(self, dn_path):
|
||||
"""Desc"""
|
||||
|
||||
print("Populating Data Needed..")
|
||||
print("[*] Populating Data Needed...")
|
||||
if dn_path:
|
||||
dn_list = glob.glob(dn_path + '*.yml')
|
||||
else:
|
||||
@ -338,12 +348,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Data Needed populated!")
|
||||
print("[+] Data Needed populated!")
|
||||
|
||||
def detection_rule(self, dr_path):
|
||||
"""Desc"""
|
||||
|
||||
print("Populating Detection Rules..")
|
||||
print("[*] Populating Detection Rules...")
|
||||
if dr_path:
|
||||
dr_list = glob.glob(dr_path + '*.yml')
|
||||
else:
|
||||
@ -382,12 +392,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Detection Rules populated!")
|
||||
print("[+] Detection Rules populated!")
|
||||
|
||||
def enrichment(self, en_path):
|
||||
"""Nothing here yet"""
|
||||
|
||||
print("Populating Enrichments..")
|
||||
print("[*] Populating Enrichments...")
|
||||
if en_path:
|
||||
en_list = glob.glob(en_path + '*.yml')
|
||||
else:
|
||||
@ -419,12 +429,12 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Enrichments populated!")
|
||||
print("[+] Enrichments populated!")
|
||||
|
||||
def response_action(self, ra_path):
|
||||
"""Nothing here yet"""
|
||||
|
||||
print("Populating Response Actions..")
|
||||
print("[*] Populating Response Actions...")
|
||||
if ra_path:
|
||||
ra_list = glob.glob(ra_path + '*.yml')
|
||||
else:
|
||||
@ -456,12 +466,12 @@ class PopulateConfluence:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
|
||||
print("Response Actions populated!")
|
||||
print("[+] Response Actions populated!")
|
||||
|
||||
def response_playbook(self, rp_path):
|
||||
"""Nothing here yet"""
|
||||
|
||||
print("Populating Response Playbooks..")
|
||||
print("[*] Populating Response Playbooks...")
|
||||
if rp_path:
|
||||
rp_list = glob.glob(rp_path + '*.yml')
|
||||
else:
|
||||
@ -496,12 +506,52 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Response Playbooks populated!")
|
||||
print("[+] Response Playbooks populated!")
|
||||
|
||||
def response_stage(self, rs_path):
|
||||
"""Nothing here yet"""
|
||||
|
||||
print("[*] Populating Response Stages...")
|
||||
if rs_path:
|
||||
rs_list = glob.glob(rs_path + '*.yml')
|
||||
else:
|
||||
rs_dir = ATCconfig.get('response_stages_dir')
|
||||
rs_list = glob.glob(rs_dir + '/*.yml')
|
||||
|
||||
for rs_file in rs_list:
|
||||
try:
|
||||
rs = ResponseStage(rs_file, apipath=self.apipath,
|
||||
auth=self.auth, space=self.space)
|
||||
rs.render_template("confluence")
|
||||
|
||||
base = os.path.basename(rs_file)
|
||||
|
||||
confluence_data = {
|
||||
"title": rs.rs_parsed_file['title'],
|
||||
"spacekey": self.space,
|
||||
"parentid": str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth, self.space,
|
||||
"Response Stages")),
|
||||
"confluencecontent": rs.content,
|
||||
}
|
||||
|
||||
res = ATCutils.push_to_confluence(confluence_data, self.apipath,
|
||||
self.auth)
|
||||
if res == 'Page updated':
|
||||
print("==> updated page: RS '" + base + "'")
|
||||
# print("Done: ", rp.rp_parsed_file['title'])
|
||||
except Exception as err:
|
||||
print(rs_file + " failed")
|
||||
print("Err message: %s" % err)
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("[+] Response Stages populated!")
|
||||
|
||||
def customer(self, cu_path):
|
||||
"""Nothing here yet"""
|
||||
|
||||
print("Populating Customers..")
|
||||
print("[+] Populating Customers...")
|
||||
if cu_path:
|
||||
cu_list = glob.glob(cu_path + '*.yml')
|
||||
else:
|
||||
@ -534,4 +584,4 @@ class PopulateConfluence:
|
||||
print('-' * 60)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print('-' * 60)
|
||||
print("Customers populated!")
|
||||
print("[+] Customers populated!")
|
||||
|
@ -7,11 +7,13 @@ from react_scripts.react_mapping import rs_mapping
|
||||
|
||||
import os
|
||||
|
||||
|
||||
# ########################################################################### #
|
||||
# ########################### Response Action ############################### #
|
||||
# ########################################################################### #
|
||||
|
||||
ATCconfig = ATCutils.load_config("config.yml")
|
||||
env = Environment(loader=FileSystemLoader('templates'))
|
||||
|
||||
|
||||
class ResponseAction:
|
||||
@ -22,9 +24,6 @@ class ResponseAction:
|
||||
|
||||
# Init vars
|
||||
self.yaml_file = yaml_file
|
||||
# The name of the directory containing future markdown LogginPolicy
|
||||
self.parent_title = "Response_Actions"
|
||||
|
||||
self.apipath = apipath
|
||||
self.auth = auth
|
||||
self.space = space
|
||||
@ -40,85 +39,66 @@ class ResponseAction:
|
||||
def render_template(self, template_type):
|
||||
"""Description
|
||||
template_type:
|
||||
- "markdown"
|
||||
- "confluence"
|
||||
"""
|
||||
|
||||
if template_type not in ["markdown", "confluence"]:
|
||||
if template_type not in ["confluence"]:
|
||||
raise Exception(
|
||||
"Bad template_type. Available values:" +
|
||||
" [\"markdown\", \"confluence\"]")
|
||||
|
||||
# Point to the templates directory
|
||||
env = Environment(loader=FileSystemLoader('templates'))
|
||||
"Bad template_type. Available value:" +
|
||||
" \"confluence\"]")
|
||||
|
||||
# Get proper template
|
||||
if template_type == "markdown":
|
||||
template = env.get_template(
|
||||
'markdown_responseaction_template.md.j2'
|
||||
|
||||
template = env.get_template(
|
||||
'confluence_responseaction_template.html.j2')
|
||||
|
||||
new_title = self.ra_parsed_file.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(self.ra_parsed_file.get('title'))
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'title': new_title}
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')})
|
||||
|
||||
##
|
||||
## Add link to a stage
|
||||
##
|
||||
|
||||
stage = self.ra_parsed_file.get('stage')
|
||||
rs_list = []
|
||||
for rs_id, rs_name in rs_mapping.items():
|
||||
if ATCutils.normalize_rs_name(stage) == rs_name:
|
||||
if self.apipath and self.auth and self.space:
|
||||
rs_confluence_page_id = str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth, self.space, rs_name)
|
||||
)
|
||||
rs_list.append((rs_id, rs_name, rs_confluence_page_id))
|
||||
else:
|
||||
rs_confluence_page_id = ""
|
||||
rs_list.append((rs_id, rs_name, rs_confluence_page_id))
|
||||
break
|
||||
|
||||
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'stage': rs_list}
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'title': ATCutils.normalize_react_title(self.ra_parsed_file
|
||||
.get('title'))}
|
||||
)
|
||||
# Category
|
||||
self.ra_parsed_file.update(
|
||||
{'category': ATCutils.get_ra_category(self.ra_parsed_file
|
||||
.get('id'))}
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'description': self.ra_parsed_file
|
||||
.get('description').strip()}
|
||||
)
|
||||
self.ra_parsed_file.update(
|
||||
{'description': self.ra_parsed_file.get('description').strip()}
|
||||
)
|
||||
|
||||
elif template_type == "confluence":
|
||||
template = env.get_template(
|
||||
'confluence_responseaction_template.html.j2'
|
||||
)
|
||||
|
||||
new_title = self.ra_parsed_file.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(self.ra_parsed_file.get('title'))
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'title': new_title }
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')})
|
||||
|
||||
linked_ra = self.ra_parsed_file.get("linked_ra")
|
||||
|
||||
if linked_ra:
|
||||
linked_ra_with_id = []
|
||||
for ra in linked_ra:
|
||||
if self.apipath and self.auth and self.space:
|
||||
linked_ra_id = str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth, self.space, ra)
|
||||
)
|
||||
else:
|
||||
linked_ra_id = ""
|
||||
ra = (ra, linked_ra_id)
|
||||
linked_ra_with_id.append(ra)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'linkedra': linked_ra_with_id}
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'description': self.ra_parsed_file.get('description').strip()}
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'workflow': self.ra_parsed_file.get('workflow')}
|
||||
)
|
||||
self.ra_parsed_file.update(
|
||||
{'workflow': self.ra_parsed_file.get('workflow')}
|
||||
)
|
||||
|
||||
self.content = template.render(self.ra_parsed_file)
|
||||
|
||||
def save_markdown_file(self, atc_dir=ATCconfig.get('md_name_of_root_directory')):
|
||||
"""Write content (md template filled with data) to a file"""
|
||||
|
||||
base = os.path.basename(self.yaml_file)
|
||||
title = os.path.splitext(base)[0]
|
||||
|
||||
file_path = atc_dir + self.parent_title + "/" + \
|
||||
title + ".md"
|
||||
|
||||
return ATCutils.write_file(file_path, self.content)
|
||||
|
@ -24,8 +24,6 @@ class ResponsePlaybook:
|
||||
|
||||
# Init vars
|
||||
self.yaml_file = yaml_file
|
||||
# The name of the directory containing future markdown LogginPolicy
|
||||
self.parent_title = "Response_Playbooks"
|
||||
|
||||
self.apipath = apipath
|
||||
self.auth = auth
|
||||
@ -46,288 +44,177 @@ class ResponsePlaybook:
|
||||
- "confluence"
|
||||
"""
|
||||
|
||||
if template_type not in ["markdown", "confluence"]:
|
||||
if template_type not in ["confluence"]:
|
||||
raise Exception(
|
||||
"Bad template_type. Available values:" +
|
||||
" [\"markdown\", \"confluence\"]")
|
||||
" \"confluence\"]")
|
||||
|
||||
# Point to the templates directory
|
||||
env = Environment(loader=FileSystemLoader('templates'))
|
||||
|
||||
# Get proper template
|
||||
if template_type == "markdown":
|
||||
template = env.get_template(
|
||||
'markdown_responseplaybook_template.md.j2'
|
||||
)
|
||||
|
||||
self.rp_parsed_file.update(
|
||||
{'title': ATCutils.normalize_react_title(self.rp_parsed_file
|
||||
.get('title'))}
|
||||
)
|
||||
template = env.get_template(
|
||||
'confluence_responseplaybook_template.html.j2'
|
||||
)
|
||||
|
||||
# MITRE ATT&CK Tactics and Techniques
|
||||
tactic = []
|
||||
tactic_re = re.compile(r'attack\.\w\D+$')
|
||||
technique = []
|
||||
technique_re = re.compile(r'attack\.t\d{1,5}$')
|
||||
# AM!TT Tactics and Techniques
|
||||
amitt_tactic = []
|
||||
amitt_tactic_re = re.compile(r'amitt\.\w\D+$')
|
||||
amitt_technique = []
|
||||
amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$')
|
||||
new_title = self.rp_parsed_file.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(self.rp_parsed_file.get('title'))
|
||||
|
||||
self.rp_parsed_file.update(
|
||||
{'title': new_title }
|
||||
)
|
||||
|
||||
other_tags = []
|
||||
self.rp_parsed_file.update(
|
||||
{'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')})
|
||||
|
||||
for tag in self.rp_parsed_file.get('tags'):
|
||||
if tactic_re.match(tag):
|
||||
tactic.append(ta_mapping.get(tag))
|
||||
elif technique_re.match(tag):
|
||||
te = tag.upper()[7:]
|
||||
technique.append((te_mapping.get(te), te))
|
||||
elif amitt_tactic_re.match(tag):
|
||||
amitt_tactic.append(amitt_tactic_mapping.get(tag))
|
||||
elif amitt_technique_re.match(tag):
|
||||
te = tag.upper()[6:]
|
||||
amitt_technique.append((amitt_technique_mapping.get(te), te))
|
||||
else:
|
||||
other_tags.append(tag)
|
||||
# MITRE ATT&CK Tactics and Techniques
|
||||
tactic = []
|
||||
tactic_re = re.compile(r'attack\.\w\D+$')
|
||||
technique = []
|
||||
technique_re = re.compile(r'attack\.t\d{1,5}$')
|
||||
# AM!TT Tactics and Techniques
|
||||
amitt_tactic = []
|
||||
amitt_tactic_re = re.compile(r'amitt\.\w\D+$')
|
||||
amitt_technique = []
|
||||
amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$')
|
||||
|
||||
# Add MITRE ATT&CK Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'tactics': tactic})
|
||||
self.rp_parsed_file.update({'techniques': technique})
|
||||
# Add AM!TT Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'amitt_tactics': amitt_tactic})
|
||||
self.rp_parsed_file.update({'amitt_techniques': amitt_technique})
|
||||
self.rp_parsed_file.update({'other_tags': other_tags})
|
||||
other_tags = []
|
||||
|
||||
preparation = []
|
||||
identification = []
|
||||
containment = []
|
||||
eradication = []
|
||||
recovery = []
|
||||
lessons_learned = []
|
||||
detect = []
|
||||
deny = []
|
||||
disrupt = []
|
||||
degrade = []
|
||||
deceive = []
|
||||
destroy = []
|
||||
deter = []
|
||||
for tag in self.rp_parsed_file.get('tags'):
|
||||
if tactic_re.match(tag):
|
||||
tactic.append(ta_mapping.get(tag))
|
||||
elif technique_re.match(tag):
|
||||
te = tag.upper()[7:]
|
||||
technique.append((te_mapping.get(te), te))
|
||||
elif amitt_tactic_re.match(tag):
|
||||
amitt_tactic.append(amitt_tactic_mapping.get(tag))
|
||||
elif amitt_technique_re.match(tag):
|
||||
te = tag.upper()[6:]
|
||||
amitt_technique.append((amitt_technique_mapping.get(te), te))
|
||||
else:
|
||||
other_tags.append(tag)
|
||||
|
||||
stages = [
|
||||
('preparation', preparation), ('identification', identification),
|
||||
('containment', containment), ('eradication', eradication),
|
||||
('recovery', recovery), ('lessons_learned', lessons_learned),
|
||||
('detect', detect), ('deny', deny), ('disrupt', disrupt),
|
||||
('degrade', degrade), ('deceive', deceive), ('destroy', destroy),
|
||||
('deter', deter)
|
||||
]
|
||||
# Add MITRE ATT&CK Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'tactics': tactic})
|
||||
self.rp_parsed_file.update({'techniques': technique})
|
||||
# Add AM!TT Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'amitt_tactics': amitt_tactic})
|
||||
self.rp_parsed_file.update({'amitt_techniques': amitt_technique})
|
||||
self.rp_parsed_file.update({'other_tags': other_tags})
|
||||
|
||||
# grab workflow per action in each IR stages
|
||||
# error handling for playbooks with empty stages
|
||||
for stage_name, stage_list in stages:
|
||||
try:
|
||||
for task in self.rp_parsed_file.get(stage_name):
|
||||
action = ATCutils.read_yaml_file(
|
||||
ATCconfig.get('response_actions_dir')
|
||||
+ '/' + task + '.yml'
|
||||
)
|
||||
# get links to response action
|
||||
|
||||
action_title = action.get('id')\
|
||||
+ ":"\
|
||||
+ ATCutils.normalize_react_title(action.get('title'))
|
||||
|
||||
preparation = []
|
||||
identification = []
|
||||
containment = []
|
||||
eradication = []
|
||||
recovery = []
|
||||
lessons_learned = []
|
||||
detect = []
|
||||
deny = []
|
||||
disrupt = []
|
||||
degrade = []
|
||||
deceive = []
|
||||
destroy = []
|
||||
deter = []
|
||||
|
||||
stages = [
|
||||
('preparation', preparation), ('identification', identification),
|
||||
('containment', containment), ('eradication', eradication),
|
||||
('recovery', recovery), ('lessons_learned', lessons_learned),
|
||||
('detect', detect), ('deny', deny), ('disrupt', disrupt),
|
||||
('degrade', degrade), ('deceive', deceive), ('destroy', destroy),
|
||||
('deter', deter)
|
||||
]
|
||||
|
||||
for stage_name, stage_list in stages:
|
||||
try:
|
||||
for task in self.rp_parsed_file.get(stage_name):
|
||||
action = ATCutils.read_yaml_file(
|
||||
ATCconfig.get('response_actions_dir')
|
||||
+ '/' + task + '.yml'
|
||||
)
|
||||
|
||||
action_title = action.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(action.get('title'))
|
||||
|
||||
if self.apipath and self.auth and self.space:
|
||||
stage_list.append(
|
||||
(action_title, task, action.get('description'), action.get('workflow'))
|
||||
)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# change stages name to more pretty format
|
||||
stages = [(stage_name.replace('_', ' ').capitalize(),
|
||||
stage_list) for stage_name, stage_list in stages]
|
||||
|
||||
self.rp_parsed_file.update({'stages': stages})
|
||||
|
||||
self.rp_parsed_file.update(
|
||||
{'description': self.rp_parsed_file
|
||||
.get('description').strip()}
|
||||
)
|
||||
|
||||
elif template_type == "confluence":
|
||||
template = env.get_template(
|
||||
'confluence_responseplaybook_template.html.j2'
|
||||
)
|
||||
|
||||
new_title = self.rp_parsed_file.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(self.rp_parsed_file.get('title'))
|
||||
|
||||
self.rp_parsed_file.update(
|
||||
{'title': new_title }
|
||||
)
|
||||
|
||||
self.rp_parsed_file.update(
|
||||
{'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')})
|
||||
|
||||
# MITRE ATT&CK Tactics and Techniques
|
||||
tactic = []
|
||||
tactic_re = re.compile(r'attack\.\w\D+$')
|
||||
technique = []
|
||||
technique_re = re.compile(r'attack\.t\d{1,5}$')
|
||||
# AM!TT Tactics and Techniques
|
||||
amitt_tactic = []
|
||||
amitt_tactic_re = re.compile(r'amitt\.\w\D+$')
|
||||
amitt_technique = []
|
||||
amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$')
|
||||
|
||||
other_tags = []
|
||||
|
||||
for tag in self.rp_parsed_file.get('tags'):
|
||||
if tactic_re.match(tag):
|
||||
tactic.append(ta_mapping.get(tag))
|
||||
elif technique_re.match(tag):
|
||||
te = tag.upper()[7:]
|
||||
technique.append((te_mapping.get(te), te))
|
||||
elif amitt_tactic_re.match(tag):
|
||||
amitt_tactic.append(amitt_tactic_mapping.get(tag))
|
||||
elif amitt_technique_re.match(tag):
|
||||
te = tag.upper()[6:]
|
||||
amitt_technique.append((amitt_technique_mapping.get(te), te))
|
||||
else:
|
||||
other_tags.append(tag)
|
||||
|
||||
# Add MITRE ATT&CK Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'tactics': tactic})
|
||||
self.rp_parsed_file.update({'techniques': technique})
|
||||
# Add AM!TT Tactics and Techniques to J2
|
||||
self.rp_parsed_file.update({'amitt_tactics': amitt_tactic})
|
||||
self.rp_parsed_file.update({'amitt_techniques': amitt_technique})
|
||||
self.rp_parsed_file.update({'other_tags': other_tags})
|
||||
|
||||
# get links to response action
|
||||
|
||||
preparation = []
|
||||
identification = []
|
||||
containment = []
|
||||
eradication = []
|
||||
recovery = []
|
||||
lessons_learned = []
|
||||
detect = []
|
||||
deny = []
|
||||
disrupt = []
|
||||
degrade = []
|
||||
deceive = []
|
||||
destroy = []
|
||||
deter = []
|
||||
|
||||
stages = [
|
||||
('preparation', preparation), ('identification', identification),
|
||||
('containment', containment), ('eradication', eradication),
|
||||
('recovery', recovery), ('lessons_learned', lessons_learned),
|
||||
('detect', detect), ('deny', deny), ('disrupt', disrupt),
|
||||
('degrade', degrade), ('deceive', deceive), ('destroy', destroy),
|
||||
('deter', deter)
|
||||
]
|
||||
|
||||
for stage_name, stage_list in stages:
|
||||
try:
|
||||
for task in self.rp_parsed_file.get(stage_name):
|
||||
action = ATCutils.read_yaml_file(
|
||||
ATCconfig.get('response_actions_dir')
|
||||
+ '/' + task + '.yml'
|
||||
)
|
||||
|
||||
action_title = action.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(action.get('title'))
|
||||
|
||||
if self.apipath and self.auth and self.space:
|
||||
stage_list.append(
|
||||
(action_title,
|
||||
str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth,
|
||||
self.space, action_title)
|
||||
)
|
||||
(action_title,
|
||||
str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth,
|
||||
self.space, action_title)
|
||||
)
|
||||
)
|
||||
else:
|
||||
stage_list.append((action_title, ""))
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# change stages name to more pretty format
|
||||
stages = [(stage_name.replace('_', ' ').capitalize(), stage_list)
|
||||
for stage_name, stage_list in stages]
|
||||
|
||||
self.rp_parsed_file.update({'stages_with_id': stages})
|
||||
|
||||
# get descriptions for response actions
|
||||
|
||||
preparation = []
|
||||
identification = []
|
||||
containment = []
|
||||
eradication = []
|
||||
recovery = []
|
||||
lessons_learned = []
|
||||
detect = []
|
||||
deny = []
|
||||
disrupt = []
|
||||
degrade = []
|
||||
deceive = []
|
||||
destroy = []
|
||||
deter = []
|
||||
|
||||
stages = [
|
||||
('preparation', preparation), ('identification', identification),
|
||||
('containment', containment), ('eradication', eradication),
|
||||
('recovery', recovery), ('lessons_learned', lessons_learned),
|
||||
('detect', detect), ('deny', deny), ('disrupt', disrupt),
|
||||
('degrade', degrade), ('deceive', deceive), ('destroy', destroy),
|
||||
('deter', deter)
|
||||
]
|
||||
|
||||
# grab workflow per action in each IR stages
|
||||
# error handling for playbooks with empty stages
|
||||
for stage_name, stage_list in stages:
|
||||
try:
|
||||
for task in self.rp_parsed_file.get(stage_name):
|
||||
action = ATCutils.read_yaml_file(
|
||||
ATCconfig.get('response_actions_dir')
|
||||
+ '/' + task + '.yml')
|
||||
stage_list.append(
|
||||
(action.get('description'),
|
||||
action.get('workflow'))
|
||||
)
|
||||
)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
stage_list.append((action_title, ""))
|
||||
|
||||
# change stages name to more pretty format
|
||||
stages = [(stage_name.replace('_', ' ').capitalize(), stage_list)
|
||||
for stage_name, stage_list in stages]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.rp_parsed_file.update({'stages': stages})
|
||||
self.rp_parsed_file.update(
|
||||
{'workflow':
|
||||
self.rp_parsed_file.get('workflow')
|
||||
}
|
||||
)
|
||||
self.rp_parsed_file.update(
|
||||
{'description': self.rp_parsed_file
|
||||
.get('description').strip()}
|
||||
)
|
||||
# change stages name to more pretty format
|
||||
stages = [(stage_name.replace('_', ' ').capitalize(), stage_list)
|
||||
for stage_name, stage_list in stages]
|
||||
|
||||
self.rp_parsed_file.update({'stages_with_id': stages})
|
||||
|
||||
# get descriptions for response actions
|
||||
|
||||
preparation = []
|
||||
identification = []
|
||||
containment = []
|
||||
eradication = []
|
||||
recovery = []
|
||||
lessons_learned = []
|
||||
detect = []
|
||||
deny = []
|
||||
disrupt = []
|
||||
degrade = []
|
||||
deceive = []
|
||||
destroy = []
|
||||
deter = []
|
||||
|
||||
stages = [
|
||||
('preparation', preparation), ('identification', identification),
|
||||
('containment', containment), ('eradication', eradication),
|
||||
('recovery', recovery), ('lessons_learned', lessons_learned),
|
||||
('detect', detect), ('deny', deny), ('disrupt', disrupt),
|
||||
('degrade', degrade), ('deceive', deceive), ('destroy', destroy),
|
||||
('deter', deter)
|
||||
]
|
||||
|
||||
# grab workflow per action in each IR stages
|
||||
# error handling for playbooks with empty stages
|
||||
for stage_name, stage_list in stages:
|
||||
try:
|
||||
for task in self.rp_parsed_file.get(stage_name):
|
||||
action = ATCutils.read_yaml_file(
|
||||
ATCconfig.get('response_actions_dir')
|
||||
+ '/' + task + '.yml')
|
||||
stage_list.append(
|
||||
(action.get('description'),
|
||||
action.get('workflow'))
|
||||
)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# change stages name to more pretty format
|
||||
stages = [(stage_name.replace('_', ' ').capitalize(), stage_list)
|
||||
for stage_name, stage_list in stages]
|
||||
|
||||
self.rp_parsed_file.update({'stages': stages})
|
||||
self.rp_parsed_file.update(
|
||||
{'workflow':
|
||||
self.rp_parsed_file.get('workflow')
|
||||
}
|
||||
)
|
||||
self.rp_parsed_file.update(
|
||||
{'description': self.rp_parsed_file
|
||||
.get('description').strip()}
|
||||
)
|
||||
|
||||
# Render
|
||||
self.content = template.render(self.rp_parsed_file)
|
||||
|
||||
def save_markdown_file(self, atc_dir=ATCconfig.get('md_name_of_root_directory')):
|
||||
"""Write content (md template filled with data) to a file"""
|
||||
|
||||
base = os.path.basename(self.yaml_file)
|
||||
title = os.path.splitext(base)[0]
|
||||
|
||||
file_path = atc_dir + self.parent_title + "/" + \
|
||||
title + ".md"
|
||||
|
||||
return ATCutils.write_file(file_path, self.content)
|
||||
|
@ -1,24 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from scripts.atcutils import ATCutils
|
||||
from atcutils import ATCutils
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from scripts.react_mapping import rs_mapping
|
||||
from react_scripts.react_mapping import rs_mapping
|
||||
import os
|
||||
|
||||
|
||||
ATCconfig = ATCutils.load_config("scripts/config.yml")
|
||||
ATCconfig = ATCutils.load_config("config.yml")
|
||||
env = Environment(loader=FileSystemLoader('templates'))
|
||||
|
||||
|
||||
class ResponseStage:
|
||||
"""Class for the Playbook Stage entity"""
|
||||
|
||||
def __init__(self, yaml_file):
|
||||
def __init__(self, yaml_file, apipath=None, auth=None, space=None):
|
||||
"""Init method"""
|
||||
|
||||
# Init vars
|
||||
self.apipath = apipath
|
||||
self.auth = auth
|
||||
self.space = space
|
||||
self.yaml_file = yaml_file
|
||||
# The name of the directory containing future markdown Response_Stages
|
||||
self.parent_title = "Response_Stages"
|
||||
|
||||
# Init methods
|
||||
self.parse_into_fields(self.yaml_file)
|
||||
@ -26,7 +29,7 @@ class ResponseStage:
|
||||
def parse_into_fields(self, yaml_file):
|
||||
"""Description"""
|
||||
|
||||
self.ra_parsed_file = ATCutils.read_yaml_file(yaml_file)
|
||||
self.rs_parsed_file = ATCutils.read_yaml_file(yaml_file)
|
||||
|
||||
def render_template(self, template_type):
|
||||
"""Description
|
||||
@ -34,28 +37,29 @@ class ResponseStage:
|
||||
- "markdown"
|
||||
"""
|
||||
|
||||
if template_type not in ["markdown"]:
|
||||
if template_type not in ["confluence"]:
|
||||
raise Exception(
|
||||
"Bad template_type. Available values:" +
|
||||
" [\"markdown\"]")
|
||||
|
||||
# Point to the templates directory
|
||||
env = Environment(loader=FileSystemLoader('scripts/templates'))
|
||||
" \"confluence\"]")
|
||||
|
||||
template = env.get_template(
|
||||
'markdown_responsestage_template.md.j2'
|
||||
'confluence_responsestage_template.html.j2'
|
||||
)
|
||||
|
||||
self.ra_parsed_file.update(
|
||||
{'description': self.ra_parsed_file
|
||||
self.rs_parsed_file.update(
|
||||
{'description': self.rs_parsed_file
|
||||
.get('description').strip()}
|
||||
)
|
||||
|
||||
ras, ra_paths = ATCutils.load_yamls_with_paths(ATCconfig.get('response_actions_dir'))
|
||||
ra_filenames = [ra_path.split('/')[-1].replace('.yml', '') for ra_path in ra_paths]
|
||||
ras, ra_paths = ATCutils.load_yamls_with_paths(
|
||||
ATCconfig.get('response_actions_dir'))
|
||||
ra_filenames = [ra_path.split('/')[-1].replace('.yml', '')
|
||||
for ra_path in ra_paths]
|
||||
|
||||
rs_id = self.rs_parsed_file.get('id')
|
||||
|
||||
rs_id = self.ra_parsed_file.get('id')
|
||||
self.rs_parsed_file.update(
|
||||
{'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')})
|
||||
|
||||
stage_list = []
|
||||
|
||||
@ -65,20 +69,28 @@ class ResponseStage:
|
||||
ra_filename = ra_filenames[i]
|
||||
ra_title = ATCutils.normalize_react_title(ras[i].get('title'))
|
||||
ra_description = ras[i].get('description').strip()
|
||||
stage_list.append((ra_id, ra_filename, ra_title, ra_description))
|
||||
ra_confluence_page_name = ra_id + ": " + ra_title
|
||||
print(ra_confluence_page_name)
|
||||
|
||||
if self.apipath and self.auth and self.space:
|
||||
ra_confluence_page_id = str(ATCutils.confluence_get_page_id(
|
||||
self.apipath, self.auth, self.space, ra_confluence_page_name)
|
||||
)
|
||||
else:
|
||||
ra_confluence_page_id = ""
|
||||
|
||||
self.ra_parsed_file.update({'stage_list': sorted(stage_list)})
|
||||
print(ra_confluence_page_id)
|
||||
stage_list.append(
|
||||
(ra_id, ra_filename, ra_title, ra_description, ra_confluence_page_id))
|
||||
|
||||
self.content = template.render(self.ra_parsed_file)
|
||||
new_title = self.rs_parsed_file.get('id')\
|
||||
+ ": "\
|
||||
+ ATCutils.normalize_react_title(self.rs_parsed_file.get('title'))
|
||||
|
||||
def save_markdown_file(self,
|
||||
atc_dir=ATCconfig.get('md_name_of_root_directory')):
|
||||
"""Write content (md template filled with data) to a file"""
|
||||
self.rs_parsed_file.update(
|
||||
{'title': new_title}
|
||||
)
|
||||
|
||||
base = os.path.basename(self.yaml_file)
|
||||
title = os.path.splitext(base)[0]
|
||||
self.rs_parsed_file.update({'stage_list': sorted(stage_list)})
|
||||
|
||||
file_path = atc_dir + self.parent_title + "/" + \
|
||||
title + ".md"
|
||||
|
||||
return ATCutils.write_file(file_path, self.content)
|
||||
self.content = template.render(self.rs_parsed_file)
|
||||
|
@ -26,9 +26,15 @@
|
||||
<td class="confluenceTd">{{ creation_date|e }}<br /></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th class="confluenceTh">Category</th>
|
||||
<td class="confluenceTd">{{ category|e }}<br /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="confluenceTh">Stage</th>
|
||||
<td class="confluenceTd">{{ stage|e }}<br /></td>
|
||||
{%- for rs_id, rs_name, rs_confluence_page_id in stage %}
|
||||
<td class="confluenceTd"><a href="{{confluence_viewpage_url}}{{rs_confluence_page_id}}">{{ rs_id }}: {{ rs_name }}</a><br /></td>
|
||||
{% endfor -%}
|
||||
</tr>
|
||||
{% if automation is defined and automation|length %}
|
||||
<tr>
|
||||
|
29
scripts/templates/confluence_responsestage_template.html.j2
Executable file
29
scripts/templates/confluence_responsestage_template.html.j2
Executable file
@ -0,0 +1,29 @@
|
||||
<b>ID:</b>{{ id|e }}
|
||||
<pre><br/></pre>
|
||||
{{ description }}
|
||||
{%- if stage_list is not none and stage_list|length -%}
|
||||
<h2>Response Actions</h2>
|
||||
<table data-layout="default"><colgroup><col /><col /><col /></colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<p style="text-align: center;"><strong>ID</strong></p></th>
|
||||
<th>
|
||||
<p style="text-align: center;"><strong>Name</strong></p></th>
|
||||
<th>
|
||||
<p><strong>Description</strong></p>
|
||||
</th>
|
||||
</tr>
|
||||
{%- for ra_id, ra_filename, ra_title, ra_description, ra_confluence_page_id in stage_list -%}
|
||||
<tr>
|
||||
<td>
|
||||
<p><a href="{{confluence_viewpage_url}}{{ra_confluence_page_id}}">{{ ra_id }}</a></p></td>
|
||||
<td>
|
||||
<p><a href="{{confluence_viewpage_url}}{{ra_confluence_page_id}}">{{ ra_title }}</a></p></td>
|
||||
<td>
|
||||
<p>{{ ra_description }}</p></td>
|
||||
</tr>
|
||||
{%- endfor -%}
|
||||
</tbody></table>
|
||||
<p />
|
||||
{%- endif -%}
|
@ -1,11 +0,0 @@
|
||||
| Title | {{ title }} |
|
||||
|:---------------------------:|:--------------------|
|
||||
| **ID** | {{ id }} |
|
||||
| **Description** | {{ description }} |
|
||||
| **Author** | {{ author }} |
|
||||
| **Creation Date** | {{ creation_date }} |
|
||||
| **Stage** | {{ stage }} |{% if automation is not none and automation|length %}{{ '\n' }}| **Automation** |<ul>{% for auto in automation %}<li>{{ auto }}</li>{% endfor %}</ul>|{% endif %}{% if references is not none and references|length %}{{ '\n' }}| **References** |<ul>{% for ref in references %}<li>[{{ ref }}]({{ ref }})</li>{% endfor %}</ul>|{% endif %}{% if requirements is not none and requirements|length %}{{ '\n' }}| **Requirements** |<ul>{% for requirement in requirements %}<li>{{ requirement }}</li>{% endfor %}</ul>|{% endif %}
|
||||
|
||||
### Workflow
|
||||
|
||||
{{ workflow }}
|
@ -1,43 +0,0 @@
|
||||
| Title | {{ title }} |
|
||||
|:-----------------:|:-----------------------------------------------------------------------------------------------------------------|
|
||||
| **ID** | {{ id }} |
|
||||
| **Description** | {{ description }} |
|
||||
| **Author** | {{ author }} |
|
||||
| **Creation Date** | {{ creation_date }} |
|
||||
| **Severity** | {{ severity }} |
|
||||
| **TLP** | {{ tlp }} |
|
||||
| **PAP** | {{ pap }} |
|
||||
{%- if tactics is not none and tactics|length -%}
|
||||
{{ '\n' }}| **ATT&CK Tactic** |<ul>{%- for tactic_name, tactic_id in tactics %}<li>[{{ tactic_id }}: {{tactic_name}}](https://attack.mitre.org/tactics/{{tactic_id}})</li>{% endfor -%}</ul>|
|
||||
{%- endif -%}
|
||||
{%- if techniques is not none and tactics|length -%}
|
||||
{{ '\n' }}| **ATT&CK Technique** |<ul>{% for technique_name, technique_id in techniques %}<li>[{{ technique_id }}: {{technique_name}}](https://attack.mitre.org/tactics/{{technique_id}})</li>{% endfor -%}</ul>|
|
||||
{%- endif -%}
|
||||
{%- if amitt_tactics is not none and amitt_tactics|length -%}
|
||||
{{ '\n' }}| **AM!TT Tactic** |<ul>{%- for tactic_name, tactic_id in amitt_tactics %}<li>[{{ tactic_id }}: {{tactic_name}}](https://vvx7.xyz/tactics/{{tactic_id}})</li>{% endfor %}</ul>|
|
||||
{%- endif -%}
|
||||
{%- if amitt_techniques is not none and amitt_techniques|length -%}
|
||||
{{ '\n' }}| **AM!TT Technique** |<ul>{% for technique_name, technique_id in amitt_techniques %}<li>[{{ technique_id }}: {{technique_name}}](https://vvx7.xyz/tactics/{{technique_id}})</li>{% endfor %}</ul>|
|
||||
{%- endif -%}
|
||||
{%- if other_tags is not none and other_tags|length -%}
|
||||
{{ '\n' }}| **Tags** | <ul>{% for tag in other_tags %}<li>{{ tag }}</li>{% endfor %}</ul> |
|
||||
{%- endif -%}
|
||||
{%- for stage_name, stage_actions in stages -%}
|
||||
{%- if stage_actions is not none and stage_actions|length -%}
|
||||
{{'\n'}}| **{{stage_name}}** |<ul>{% for action_title, action_filename, action_description, action_workflow in stage_actions %}<li>[{{ action_title }}](../Response_Actions/{{action_filename}}.md)</li>{% endfor %}</ul>|
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
|
||||
### Workflow
|
||||
|
||||
{{ workflow }}
|
||||
|
||||
{% for stage_name, stage_actions in stages %}
|
||||
{% if stage_actions is not none and stage_actions|length %}#### {{ stage_name }}{% endif %}
|
||||
{% for action_title, action_filename, action_description, action_workflow in stage_actions%}
|
||||
##### {{ action_description }}
|
||||
|
||||
{{ action_workflow }}
|
||||
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
@ -1,15 +0,0 @@
|
||||
# {{ title }}
|
||||
|
||||
**ID**: {{ id }}
|
||||
|
||||
{{ description }}
|
||||
|
||||
{%- if stage_list is not none and stage_list|length -%}
|
||||
{{ '\n' }}## Response Actions
|
||||
|
||||
| ID | Name | Description |
|
||||
|:-----:|:--------:|-------------|
|
||||
{%- for ra_id, ra_filename, ra_title, ra_description in stage_list -%}
|
||||
{{ '\n' }}| [{{ ra_id }}](../Response_Actions/{{ra_filename}}.md) | [{{ ra_title }}](../Response_Actions/{{ra_filename}}.md) | {{ra_description}} |
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
Loading…
Reference in New Issue
Block a user