Merge branch 'develop' into 'master'

Develop

See merge request krakow2600/atomic-threat-coverage!94
This commit is contained in:
Daniil Yugoslavskiy 2019-12-23 02:27:58 +00:00
commit a774201a63
4 changed files with 871 additions and 508 deletions

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ description: >
loggingpolicy:
- LP_0004_windows_audit_logon
references:
- https://github.com/MicrosoftDocs/windows-itpro-docs/blob/95b9d7c01805839c067e352d1d16702604b15f11/windows/security/threat-protection/auditing/event-4688.md
- https://github.com/MicrosoftDocs/windows-itpro-docs/blob/95b9d7c01805839c067e352d1d16702604b15f11/windows/security/threat-protection/auditing/event-4624.md
category: OS Logs
platform: Windows
type: Windows Log

View File

@ -111,7 +111,7 @@ If no local configuration is found on the argument path, a warning will be shown
"""Open the yaml file and load it to the variable.
Return created list"""
with open(path) as f:
yaml_fields = yaml.load_all(f.read())
yaml_fields = yaml.load_all(f.read(), Loader=yaml.FullLoader)
buff_results = [x for x in yaml_fields]
if len(buff_results) > 1:
@ -149,6 +149,16 @@ class ATCutils:
return rule_text
@staticmethod
def get_normalized_field(field):
# return everything before field modifier (pipe)
regex = '^(.*?)\|'
try:
return re.search(regex, field).group(1)
except:
return field
@staticmethod
def read_yaml_file(path):
"""Open the yaml file and load it to the variable.
@ -162,7 +172,7 @@ class ATCutils:
return ATCConfig(path).config
with open(path) as f:
yaml_fields = yaml.load_all(f.read())
yaml_fields = yaml.load_all(f.read(), Loader=yaml.FullLoader)
buff_results = [x for x in yaml_fields]
if len(buff_results) > 1:
@ -240,7 +250,7 @@ class ATCutils:
url = apipath + "content"
space_page_url = url + '?spaceKey=' + space + '&title=' \
+ title + '&expand=space'
# print(space_page_url)
response = requests.request(
"GET",
space_page_url,
@ -255,8 +265,6 @@ class ATCutils:
else:
response = response.json()
# print(response)
# Check if response contains proper information and return it if so
if response.get('results'):
if isinstance(response['results'], list):
@ -470,13 +478,16 @@ class ATCutils:
list) and _field != 'EventID':
for val2 in detection_dict[_field]:
if isinstance(val2, str) or isinstance(val2, int):
dictionary_of_fields.append(_field)
normalized_key = ATCutils.get_normalized_field(_field)
dictionary_of_fields.append(normalized_key)
break
else:
for val3 in val2:
dictionary_of_fields.append(val3)
normalized_key = ATCutils.get_normalized_field(val3)
dictionary_of_fields.append(normalized_key)
else:
dictionary_of_fields.append(val)
normalized_key = ATCutils.get_normalized_field(val)
dictionary_of_fields.append(normalized_key)
return dictionary_of_fields
@ -501,15 +512,18 @@ class ATCutils:
list) and _field != 'EventID':
for val2 in detection_dict[_field]:
if isinstance(val2, str) or isinstance(val2, int):
dictionary_of_fields.append(_field)
normalized_key = ATCutils.get_normalized_field(_field)
dictionary_of_fields.append(normalized_key)
break
elif isinstance(val2, str):
continue
else:
for val3 in val2:
dictionary_of_fields.append(val3)
normalized_key = ATCutils.get_normalized_field(val3)
dictionary_of_fields.append(normalized_key)
else:
dictionary_of_fields.append(_field)
normalized_key = ATCutils.get_normalized_field(_field)
dictionary_of_fields.append(normalized_key)
return dictionary_of_fields
@ -559,6 +573,7 @@ class ATCutils:
continue
if isinstance(_field, str):
_field = ATCutils.get_normalized_field(_field)
if _field == 'CommandLine' or \
_field == 'ProcessCommandLine' or \
_field == 'ProcesssCommandLine' or \
@ -567,6 +582,7 @@ class ATCutils:
if isinstance(_field, dict):
for item in _field:
item = ATCutils.get_normalized_field(item)
if item == 'CommandLine' or \
item == 'ProcessCommandLine' or \
item == 'ProcesssCommandLine' or \
@ -578,18 +594,18 @@ class ATCutils:
@staticmethod
def check_for_event_ids_presence(detection_rule_obj):
"""check if this is event id based detection rule"""
for _field in detection_rule_obj['detection']:
if _field in ["condition", "timeframe"]:
continue
for __field in detection_rule_obj['detection'][_field]:
if isinstance(__field, str) or isinstance(__field, int):
if __field == 'EventID':
return True
elif isinstance(__field, dict):
for item in __field:
if item == 'EventID':
if detection_rule_obj.get('detection'):
for _field in detection_rule_obj['detection']:
if _field in ["condition", "timeframe"]:
continue
for __field in detection_rule_obj['detection'][_field]:
if isinstance(__field, str) or isinstance(__field, int):
if __field == 'EventID':
return True
elif isinstance(__field, dict):
for item in __field:
if item == 'EventID':
return True
return False
@ -660,6 +676,7 @@ class ATCutils:
if not detectionrule.get('action'):
logsource = ATCutils.get_logsource_of_the_document(detectionrule)
event_id_based_dr = ATCutils.check_for_event_ids_presence(detectionrule)
# if this is event id based detection rule we calculate PER SELECTION
@ -732,6 +749,7 @@ class ATCutils:
# check if first document has logsource
logsource = ATCutils.get_logsource_of_the_document(detectionrule)
if logsource:
event_id_based_dr = ATCutils.check_for_event_ids_presence(detectionrule)
if event_id_based_dr:
@ -780,6 +798,7 @@ class ATCutils:
for addition in detectionrule['additions']:
logsource = ATCutils.get_logsource_of_the_document(addition)
event_id_based_dr = ATCutils.check_for_event_ids_presence(addition)
if event_id_based_dr:
@ -801,23 +820,25 @@ class ATCutils:
else:
full_list_of_fields = []
# just in case there are multiple selections in first document
for _field in addition['detection']:
if str(_field) in ["condition", "timeframe"]:
continue
try:
detection_fields = ATCutils\
.search_for_fields2(addition['detection'][_field])
except Exception as e:
detection_fields = ATCutils\
.search_for_fields(addition['detection'])
if detection_fields:
for field in detection_fields:
if field not in full_list_of_fields:
full_list_of_fields.append(field)
# sometimes docs don't have detection section
if addition.get('detection'):
# just in case there are multiple selections in first document
for _field in addition['detection']:
if str(_field) in ["condition", "timeframe"]:
continue
try:
detection_fields = ATCutils\
.search_for_fields2(addition['detection'][_field])
except Exception as e:
detection_fields = ATCutils\
.search_for_fields(addition['detection'])
if detection_fields:
for field in detection_fields:
if field not in full_list_of_fields:
full_list_of_fields.append(field)
final_list += ATCutils.calculate_dn_for_non_eventid_based_dr(
dn_list, full_list_of_fields, logsource)

View File

@ -1,5 +1,7 @@
import sys
import re
import yaml
import pprint
import getopt
import json
import os
@ -36,6 +38,29 @@ def main(**kwargs):
techniques = []
list_of_customers = []
# raw Sigma rule without fields that are present separately
dr_raw = alert.copy()
fields_to_remove_from_raw_dr = [
'title',
'id',
'status',
'date',
'modified',
'description',
'references',
'author',
'tags',
'logsource',
'falsepositives',
'level'
]
for field in fields_to_remove_from_raw_dr:
dr_raw.pop(field, None)
dr_raw = str(yaml.dump((dr_raw), default_flow_style=False))
for customer in cu_list:
if 'detectionrule' in customer:
if alert['title'] in customer['detectionrule'] and customer['customer_name'] not in list_of_customers:
@ -59,7 +84,6 @@ def main(**kwargs):
pass
enrichments = [er for er in enrichments_list if er['title'] in alert.get('enrichment', [{'title':'-'}])]
#print(enrichments)
if len(enrichments) < 1:
enrichments = [{'title': 'not defined'}]
dn_titles = ATCutils.main_dn_calculatoin_func(path)
@ -102,6 +126,24 @@ def main(**kwargs):
#date_created = datetime.datetime.now().isoformat()
date_created = '2019-03-01T22:50:37.587060'
# we use date of creation to have timelines of progress
if 'modified' in alert:
try:
date_modified = datetime.datetime.strptime(alert['modified'], '%Y/%m/%d').isoformat()
except:
pass
if not date_modified:
try:
# in case somebody mixed up month and date, like in "Detection of SafetyKatz"
date_modified = datetime.datetime.strptime(alert['modified'], '%Y/%d/%m').isoformat()
except:
date_modified = None
else:
# temporary solution to avoid errors. all internal DRs must have date of creation
#date_created = datetime.datetime.now().isoformat()
date_modified = None
# we create index document based on DR title, which is unique (supposed to be).
# this way we will update existing documents in case of changes,
@ -146,6 +188,11 @@ def main(**kwargs):
else:
dr_author = 'not defined'
if 'id' in alert:
dr_id = alert['id']
else:
dr_id = 'not defined'
if 'internal_responsible' in alert:
dr_internal_responsible = alert['internal_responsible']
else:
@ -196,10 +243,13 @@ def main(**kwargs):
_index.update({
"date_created": date_created,
"date_modified": date_modified,
"customer": list_of_customers,
"tactic": list_of_tactics,
"dr_id": dr_id,
"technique": list_of_techniques,
"detection_rule": dr_title,
"raw_detection_rule": dr_raw,
"detection_rule_title": dr_title,
"detection_rule_author": dr_author,
"detection_rule_internal_responsible": dr_internal_responsible,
"detection_rule_development_status": dr_status,