Skip to content

Commit

Permalink
Final Changes for feature_add_auto_mitre_software_code
Browse files Browse the repository at this point in the history
- removed the mitre_softwareid_gen and mitre_softwareid_found from the CCCS_YARA.yml file as they were empty arrays.
- added Helper.check_argument_list_var()
  - it take a required_fields variable, should be self.required_fields
  - metadata, which should be the metadata you want to find the are argument value of
  - variable_name, the name of the argument variable you want returned
  - the function ensures there is an argument list variable of the name if it does not exist and returns it otherwise
  • Loading branch information
cccs-gm committed Sep 8, 2020
1 parent 8fb7377 commit 261cf2c
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 30 deletions.
5 changes: 1 addition & 4 deletions CCCS_YARA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ info|exploit|technique|tool|malware:
validator: valid_category_type
argument:
parent: category
if_key_gen_mitre_att: ^tool$|^malware$
mitre_softwareid_gen: []
generate_mitre_att_from: ^tool$|^malware$

malware_type:
description: 'Give an indication as to what the capability/purpose of the malware is'
Expand All @@ -215,8 +214,6 @@ mitre_att:
unique: No
optional: Yes
validator: valid_mitre_att
argument:
mitre_softwareid_found: []

actor_type:
description: 'The type of the actor'
Expand Down
45 changes: 28 additions & 17 deletions yara-validator/validator_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
BASE62_REGEX = r'^[0-9a-zA-z]+$'
UNIVERSAL_REGEX = r'^[^a-z]*$'
MITRE_GROUP_NAME = 'name'

CHILD_PLACE_HOLDER = 'child_place_holder'

# potential values of MetadataAttributes.optional variable
class MetadataOpt(Enum):
Expand Down Expand Up @@ -359,6 +359,7 @@ def valid_mitre_att(self, rule_to_validate_mitre_att, metadata_index, metadata_k
:return: True if the value was found in the MITRE ATT&CK database and False if it was not found
"""
MITRE_ATT = metadata_key
MITRE_SOFTWAREID_FOUND = 'mitre_softwareid_found'
self.required_fields[MITRE_ATT].attributefound()
self.required_fields_index[self.required_fields[MITRE_ATT].position].increment_count()

Expand All @@ -371,11 +372,9 @@ def valid_mitre_att(self, rule_to_validate_mitre_att, metadata_index, metadata_k
self.required_fields[MITRE_ATT].attributeinvalid()

if self.required_fields[MITRE_ATT].valid and mitre_att_to_validate.startswith('S'):
soft_codes_found = self.required_fields[MITRE_ATT].argument.get('mitre_softwareid_found')
soft_codes_found = Helper.check_argument_list_var(self.required_fields, MITRE_ATT, MITRE_SOFTWAREID_FOUND)
soft_codes_found.append(mitre_att_to_validate)

self.required_fields[MITRE_ATT].argument.update({'mitre_softwareid_found': soft_codes_found})

return self.required_fields[MITRE_ATT].valid

def valid_al_config_dumper(self, rule_to_validate_al_config_d, metadata_index, metadata_key):
Expand Down Expand Up @@ -433,7 +432,7 @@ def valid_category(self, rule_to_validate_category, metadata_index, metadata_key
CATEGORY = metadata_key
self.required_fields[CATEGORY].attributefound()
self.required_fields_index[self.required_fields[CATEGORY].position].increment_count()
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get('child_place_holder')
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get(CHILD_PLACE_HOLDER)

metadata = rule_to_validate_category[METADATA]
rule_category_to_check = metadata[metadata_index][CATEGORY]
Expand Down Expand Up @@ -466,11 +465,13 @@ def valid_category_type(self, rule_to_validate_type, metadata_index, metadata_ke
:return: True if the value matches the Regex expression and False if it was not found
"""
CATEGORY = 'category'
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get('child_place_holder')
GENERATE_MITRE_ATT_FROM = 'generate_mitre_att_from'
MITRE_SOFTWAREID_GEN = 'mitre_softwareid_gen'
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get(CHILD_PLACE_HOLDER)
self.required_fields[child_metadata_place_holder].attributefound()
self.required_fields_index[self.required_fields[child_metadata_place_holder].position].increment_count()
key_gen_mitre_att = r'' +\
self.required_fields[child_metadata_place_holder].argument.get('if_key_gen_mitre_att')
self.required_fields[child_metadata_place_holder].argument.get(GENERATE_MITRE_ATT_FROM)

metadata = rule_to_validate_type[METADATA]
rule_category_key_to_check = list(metadata[metadata_index].keys())[0]
Expand All @@ -489,11 +490,10 @@ def valid_category_type(self, rule_to_validate_type, metadata_index, metadata_ke
rule_category_key_to_check):
malware_id = Helper.get_software_id_by_name(rule_category_value_to_check)
if malware_id:
malware_ids_found = self.required_fields[child_metadata_place_holder].argument.get('mitre_softwareid_gen')
malware_ids_found = Helper.check_argument_list_var(self.required_fields, child_metadata_place_holder,
MITRE_SOFTWAREID_GEN)
malware_ids_found.append(malware_id)

self.required_fields[child_metadata_place_holder].argument.update({'mitre_softwareid_gen': malware_ids_found})

return self.required_fields[child_metadata_place_holder].valid

def valid_actor(self, rule_to_validate_actor, metadata_index, metadata_key):
Expand All @@ -510,7 +510,7 @@ def valid_actor(self, rule_to_validate_actor, metadata_index, metadata_key):
ACTOR = metadata_key
ACTOR_TYPE = self.required_fields[ACTOR].argument.get('required')
child_metadata = self.required_fields[ACTOR].argument.get('child')
child_metadata_place_holder = self.required_fields[ACTOR].argument.get('child_place_holder')
child_metadata_place_holder = self.required_fields[ACTOR].argument.get(CHILD_PLACE_HOLDER)
mitre_group_alias_regex = r'^[^a-z]+$'

self.required_fields[ACTOR].attributefound()
Expand Down Expand Up @@ -548,7 +548,7 @@ def mitre_group_generator(self, rule_to_generate_group, metadata_index, metadata
:return: This should return True all the time as there will always be a return from self.get_group_from_alias
"""
ACTOR = 'actor'
place_holder = self.required_fields[ACTOR].argument.get('child_place_holder')
place_holder = self.required_fields[ACTOR].argument.get(CHILD_PLACE_HOLDER)
if self.required_fields.get(metadata_key): # if child place holder is passed as metadata_key
MITRE_GROUP = self.required_fields[self.required_fields[metadata_key].argument['parent']].argument['child']
else:
Expand Down Expand Up @@ -598,12 +598,12 @@ def mitre_software_generator(self, rule_to_generate_mitre_att, category_key, mit
"""
CATEGORY = category_key
MITRE_ATT = mitre_key
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get('child_place_holder')
MITRE_SOFTWAREID_GEN = 'mitre_softwareid_gen'
MITRE_SOFTWAREID_FOUND = 'mitre_softwareid_found'
child_metadata_place_holder = self.required_fields[CATEGORY].argument.get(CHILD_PLACE_HOLDER)

malware_ids_found = self.required_fields[child_metadata_place_holder].argument.get('mitre_softwareid_gen')
soft_codes_found = self.required_fields[MITRE_ATT].argument.get('mitre_softwareid_found')
if not soft_codes_found:
soft_codes_found = []
malware_ids_found = Helper.check_argument_list_var(self.required_fields, child_metadata_place_holder, MITRE_SOFTWAREID_GEN)
soft_codes_found = Helper.check_argument_list_var(self.required_fields, MITRE_ATT, MITRE_SOFTWAREID_FOUND)

for malware_id_found in malware_ids_found:
if malware_id_found not in soft_codes_found:
Expand Down Expand Up @@ -940,3 +940,14 @@ def get_software_id_by_name(software_name):
return tool_return[0]['external_references'][0]['external_id']
else:
return ''

@staticmethod
def check_argument_list_var(required_fields, metadata, variable_name):
if not required_fields[metadata].argument:
required_fields[metadata].argument = {variable_name: []}
elif not required_fields[metadata].argument.get(variable_name):
required_fields[metadata].argument.update({variable_name: []})
elif not isinstance(required_fields[metadata].argument.get(variable_name), list):
required_fields[metadata].argument.update({variable_name: []})

return required_fields[metadata].argument.get(variable_name)
20 changes: 11 additions & 9 deletions yara-validator/yara_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# for querying the MITRE ATT&CK data
from stix2 import FileSystemSource

from validator_functions import Validators, MetadataOpt, StringEncoding, check_encoding
from validator_functions import Validators, MetadataOpt, StringEncoding, check_encoding, Helper
from yara_file_processor import YaraFileProcessor, YaraRule

# set current working directory
Expand All @@ -31,6 +31,8 @@
CATEGORY = 'category'
CATEGORY_TYPE = 'info|exploit|technique|tool|malware'
MITRE_ATT = 'mitre_att'
CHILD_PLACE_HOLDER = 'child_place_holder'
MITRE_SOFTWAREID_GEN = 'mitre_softwareid_gen'
VALUE = 'value'
STRING_ENCODING = 'string_encoding'
WHITE_SPACE_REPLACEMENT = 'white_space_replacement'
Expand Down Expand Up @@ -571,8 +573,8 @@ def warning_author_no_hash_check(self, rule_to_check, valid):
valid.update_warning(True, HASH, 'Rule is authored by the CCCS but no hash is referenced.')

def warning_actor_no_mitre_group(self, rule_to_check, valid):
if self.required_fields.get(ACTOR) and self.required_fields[ACTOR].argument.get('child_place_holder'):
place_holder = self.required_fields[ACTOR].argument.get('child_place_holder')
if self.required_fields.get(ACTOR) and self.required_fields[ACTOR].argument.get(CHILD_PLACE_HOLDER):
place_holder = self.required_fields[ACTOR].argument.get(CHILD_PLACE_HOLDER)
if self.required_fields[ACTOR].found and not self.required_fields[place_holder].found:
metadata_values = rule_to_check[METADATA]
for value in metadata_values:
Expand All @@ -584,15 +586,15 @@ def warning_actor_no_mitre_group(self, rule_to_check, valid):
valid.update_warning(True, ACTOR, warning_message)

def warning_no_category_type(self, rule_to_check, valid):
category_child_place_holder = self.required_fields[CATEGORY].argument.get('child_place_holder')
category_child_place_holder = self.required_fields[CATEGORY].argument.get(CHILD_PLACE_HOLDER)
if self.required_fields.get(CATEGORY).found and not self.required_fields.get(category_child_place_holder).found:
metadata_values = rule_to_check[METADATA]
for value in metadata_values:
if len(value.keys()) == 1:
key = list(value.keys())[0]
value = list(value.values())[0]
if key == CATEGORY:
warning_message = 'Category: {!r} was selected but there is no associated metadate with more' \
warning_message = 'Category: {!r} was selected but there is no associated metadate with more ' \
'information i.e. malware: "name of the malware".'.format(value)
valid.update_warning(True, CATEGORY_TYPE, warning_message)

Expand Down Expand Up @@ -626,10 +628,10 @@ def return_req_optional(self, rule_to_validate):
keys_to_return.append(key)

if self.__mitre_group_alias() and self.required_fields[ACTOR].found:
keys_to_return.append(self.required_fields[ACTOR].argument.get('child_place_holder'))
keys_to_return.append(self.required_fields[ACTOR].argument.get(CHILD_PLACE_HOLDER))

category_type = self.required_fields[CATEGORY].argument.get('child_place_holder')
if self.required_fields[category_type].argument.get('mitre_softwareid_gen'):
category_type = self.required_fields[CATEGORY].argument.get(CHILD_PLACE_HOLDER)
if Helper.check_argument_list_var(self.required_fields, category_type, MITRE_SOFTWAREID_GEN):
self.validators.mitre_software_generator(rule_to_validate, CATEGORY, MITRE_ATT)

return keys_to_return
Expand Down Expand Up @@ -671,7 +673,7 @@ def handle_child_parent_metadata(self, metadata, params, metadata_in_child_paren

if argument.get('child'):
child_metadata = argument['child']
argument.update({'child_place_holder': child_metadata + place_holder})
argument.update({CHILD_PLACE_HOLDER: child_metadata + place_holder})
metadata_in_child_parent_relationship.append(argument.get('child'))

def validate_child_parent_metadata(self, configuration, metadata_in_child_parent_relationship):
Expand Down

0 comments on commit 261cf2c

Please sign in to comment.