You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
618 lines
27 KiB
618 lines
27 KiB
#!/usr/bin/python
|
|
|
|
import sys
|
|
from splunk.appserver.mrsparkle.lib.util import make_splunkhome_path
|
|
from MitreAttackInfo import MitreAttackInfo
|
|
from LookupHelper import LookupHelper
|
|
|
|
|
|
def add_to_sys_path(paths, prepend=False):
|
|
for path in paths:
|
|
if prepend:
|
|
if path in sys.path:
|
|
sys.path.remove(path)
|
|
sys.path.insert(0, path)
|
|
elif not path in sys.path:
|
|
sys.path.append(path)
|
|
|
|
add_to_sys_path([make_splunkhome_path(['etc', 'apps', 'Splunk_Security_Essentials', 'lib', 'py23', 'splunklib'])], prepend=True)
|
|
# We should not rely on core enterprise packages:
|
|
add_to_sys_path([make_splunkhome_path(['etc', 'apps', 'Splunk_Security_Essentials', 'lib', 'py3'])], prepend=True)
|
|
# Common libraries like future
|
|
add_to_sys_path([make_splunkhome_path(['etc', 'apps', 'Splunk_Security_Essentials', 'lib', 'py23'])], prepend=True)
|
|
from six.moves import reload_module
|
|
try:
|
|
if 'future' in sys.modules:
|
|
import future
|
|
reload_module(future)
|
|
except Exception:
|
|
'''noop: future was not loaded yet'''
|
|
|
|
|
|
import json, csv, re, os
|
|
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, six.moves.urllib.request
|
|
|
|
|
|
from splunk.clilib.cli_common import getConfKeyValue
|
|
|
|
from io import open
|
|
from six.moves import range
|
|
|
|
|
|
app = "Splunk_Security_Essentials"
|
|
which_killchain = "attack"
|
|
output="matrix"
|
|
matrix_lookup_file = ""
|
|
matrix_lookup_filename = ""
|
|
matrix_lookup_path = "../lookups/"
|
|
prettyPrint = True
|
|
debug = []
|
|
EnableDebug = True
|
|
sessionKey = ""
|
|
owner = ""
|
|
groups = []
|
|
platforms= []
|
|
inScopeOnly = False
|
|
popularOnly = False
|
|
forGroupOnly = False
|
|
techniquesOnly = False
|
|
techniques= []
|
|
popularity_threshold = 3
|
|
bookmarkedOnly = False
|
|
refresh_matrix_cache = False
|
|
use_cache = True
|
|
|
|
for line in sys.stdin:
|
|
m = re.search("search:\s*(.*?)$", line)
|
|
if m:
|
|
searchString = six.moves.urllib.parse.unquote(m.group(1))
|
|
if searchString:
|
|
m = re.search("mitremap[^\|]*name=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
which_killchain = m.group(1)
|
|
m = re.search("mitremap[^\|]*refresh_cache=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "true":
|
|
refresh_matrix_cache = True
|
|
m = re.search("mitremap[^\|]*pretty=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "false":
|
|
prettyPrint = False
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*content_available=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "true":
|
|
inScopeOnly = True
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*group_only=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "true":
|
|
forGroupOnly = True
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*bookmarked_only=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "true":
|
|
bookmarkedOnly = True
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*min_popularity=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
try:
|
|
popularity_threshold = int(m.group(1))
|
|
except:
|
|
popularity_threshold = 3
|
|
m = re.search("mitremap[^\|]*popular_only=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "true":
|
|
popularOnly = True
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*groups=\"([^ ]* [^=\"]*)\"", searchString)
|
|
if m:
|
|
groupstr = m.group(1)
|
|
localgroups = groupstr.split(",")
|
|
for group in localgroups:
|
|
group = group.strip()
|
|
if group not in groups:
|
|
groups.append(group)
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*groups=\"([^\" ]*)\"", searchString)
|
|
if m:
|
|
groupstr = m.group(1)
|
|
localgroups = groupstr.split(",")
|
|
for group in localgroups:
|
|
group = group.strip()
|
|
if group not in groups:
|
|
groups.append(group)
|
|
use_cache = False
|
|
m = re.search("mitremap[^\|]*techniques=\"([^\"]*)\"", searchString)
|
|
if m:
|
|
techniquesstr = m.group(1)
|
|
techniques_input = techniquesstr.split(",")
|
|
techniquesOnly = True
|
|
use_cache = False
|
|
if (len(techniques_input)>0):
|
|
for technique in techniques_input:
|
|
technique = technique.strip().split(".")[0]
|
|
if technique not in techniques:
|
|
techniques.append(technique)
|
|
if technique in "*":
|
|
techniquesOnly = False
|
|
use_cache = False
|
|
|
|
m = re.search("mitremap[^\|]*platforms=\"([^ ]*( |)[^=\"]*)\"", searchString)
|
|
matrix_lookup_filename = "mitre_enterprise_matrix.csv"
|
|
matrix_lookup_file = matrix_lookup_path+matrix_lookup_filename
|
|
if m:
|
|
platformstr = m.group(1)
|
|
localgroups = platformstr.split(",")
|
|
if len(localgroups)==1:
|
|
if (localgroups[0].lower()=="enterprise" or localgroups[0].lower()=="*"):
|
|
matrix_lookup_filename = "mitre_enterprise_matrix.csv"
|
|
else:
|
|
matrix_lookup_filename = "mitre_"+ localgroups[0].replace(" ", "_").lower()+ "_matrix.csv"
|
|
matrix_lookup_file = matrix_lookup_path+matrix_lookup_filename
|
|
elif len(localgroups)==5:
|
|
# Special case for Cloud matrix
|
|
platforms_input = ([x.lower() for x in localgroups]).sort()
|
|
cloud = (["office 365","azure ad","google workspace","saas","iaas"]).sort()
|
|
if (platforms_input == cloud):
|
|
matrix_lookup_filename = "mitre_cloud_matrix.csv"
|
|
else:
|
|
refresh_matrix_cache = False
|
|
use_cache = False
|
|
else:
|
|
# It gets complicated when you select many platforms. Don't use the cache in that instance
|
|
refresh_matrix_cache = False
|
|
use_cache = False
|
|
for platform in localgroups:
|
|
platform = platform.strip()
|
|
if platform.lower() not in platforms:
|
|
if (platform.lower()=="cloud"):
|
|
platforms.append("office 365")
|
|
platforms.append("azure ad")
|
|
platforms.append("google workspace")
|
|
platforms.append("saas")
|
|
platforms.append("iaas")
|
|
elif (platform.lower()=="enterprise" or platform.lower()=="*"):
|
|
platforms= []
|
|
else:
|
|
platforms.append(platform.lower())
|
|
|
|
m = re.search("mitremap[^\|]*output=\"*\s*([^ \"]*)", searchString)
|
|
if m:
|
|
if m.group(1).lower() == "list":
|
|
output = "list"
|
|
else:
|
|
output = "matrix"
|
|
|
|
m = re.search("sessionKey:\s*(.*?)$", line)
|
|
if m:
|
|
sessionKey = m.group(1)
|
|
m = re.search("owner:\s*(.*?)$", line)
|
|
if m:
|
|
owner = m.group(1)
|
|
|
|
|
|
lookups = LookupHelper(sessionKey)
|
|
|
|
# If we have passed the refresh_cache flag, delete all existing cache files as we want to refresh all of them
|
|
if refresh_matrix_cache == True:
|
|
import glob
|
|
files = glob.glob(matrix_lookup_path+'mitre_*_matrix.csv')
|
|
for file in files:
|
|
lookup_filename = os.path.basename(file)
|
|
lookups.deleteLookup(lookup_filename)
|
|
|
|
# Only return the cached content from the lookup, rather than pull and parse it again
|
|
if output == "matrix" and refresh_matrix_cache == False and use_cache:
|
|
if os.path.exists(matrix_lookup_file):
|
|
with open(matrix_lookup_file) as f:
|
|
print(f.read())
|
|
exit()
|
|
else:
|
|
refresh_matrix_cache = True
|
|
|
|
def strip_non_ascii(string):
|
|
''' Returns the string without non ASCII characters'''
|
|
stripped = (c for c in string if 0 < ord(c) < 127)
|
|
return ''.join(stripped)
|
|
|
|
def errorOut(obj):
|
|
print("Error!")
|
|
print('"' + json.dumps(obj).replace('"', '""') + '"')
|
|
sys.exit()
|
|
|
|
mitre_names = {}
|
|
phase_short_names_to_tactics = {}
|
|
mitre_tactics = {}
|
|
mitre_tactics_to_pretty_names = {}
|
|
mitre_techniques = {}
|
|
mitre_attack_blob = {}
|
|
mitre_groups = {}
|
|
mitre_software = {}
|
|
mitre_techniques_to_groups = {}
|
|
mitre_techniques_to_software = {}
|
|
mitre_techniques_to_malware = {}
|
|
mitre_refs_to_refs = {}
|
|
mitre_refs_to_names = {}
|
|
ShowcaseInfo = {"summaries": {}}
|
|
inScopeTechniques = {}
|
|
popularTechniques = {}
|
|
bookmarkedTechniques = {}
|
|
columns = {}
|
|
|
|
if inScopeOnly or bookmarkedOnly:
|
|
try:
|
|
# Getting configurations
|
|
base_url = "https://" + getConfKeyValue('web', 'settings', 'mgmtHostPort')
|
|
except:
|
|
errorOut({"response": "Error getting configurations!"})
|
|
|
|
try:
|
|
# Getting configurations
|
|
request = six.moves.urllib.request.Request(base_url + '/services/SSEShowcaseInfo',
|
|
headers = { 'Authorization': ('Splunk %s' % sessionKey)})
|
|
search_results = six.moves.urllib.request.urlopen(request)
|
|
|
|
ShowcaseInfo = json.loads(search_results.read())
|
|
for summaryName in ShowcaseInfo['summaries']:
|
|
if "mitre_technique" in ShowcaseInfo['summaries'][summaryName] and ShowcaseInfo['summaries'][summaryName]["mitre_technique"] != "":
|
|
mitres = ShowcaseInfo['summaries'][summaryName]["mitre_technique"].split("|")
|
|
bookmark_status = ShowcaseInfo['summaries'][summaryName]["bookmark_status"]
|
|
for mitre in mitres:
|
|
if mitre != "" and mitre != "None":
|
|
if mitre not in inScopeTechniques:
|
|
inScopeTechniques[mitre] = 1
|
|
else:
|
|
inScopeTechniques[mitre] += 1
|
|
if (bookmark_status != "none"):
|
|
if mitre not in bookmarkedTechniques:
|
|
bookmarkedTechniques[mitre] = 1
|
|
else:
|
|
bookmarkedTechniques[mitre] += 1
|
|
|
|
except Exception as e:
|
|
errorOut({"status": "ERROR", "description": "Error occurred while grabbing SSE ShowcaseInfo", "message": str(e)})
|
|
|
|
|
|
if which_killchain == "attack":
|
|
info = MitreAttackInfo(sessionKey)
|
|
mitre_attack_blob = info.returnMitreAttackBlob()
|
|
AllMitreTactics = info.returnMitreAttackIdsList()
|
|
columns = AllMitreTactics
|
|
|
|
for obj in mitre_attack_blob['objects']:
|
|
if "name" in obj:
|
|
obj['name'] = obj['name'].replace(u'\xe4', "a")
|
|
obj['name'] = strip_non_ascii(obj['name'])
|
|
if "external_references" in obj:
|
|
for reference in obj['external_references']:
|
|
if "url" in reference and "type" in obj and (obj["type"] == "attack-pattern" or obj["type"] == "x-mitre-tactic") and ( "https://attack.mitre.org/techniques/" in reference['url'] or "https://attack.mitre.org/tactics/" in reference['url'] ):
|
|
mitre_names[ reference['external_id'] ] = obj['name']
|
|
mitre_refs_to_names[obj['id']] = reference['external_id']
|
|
if "url" in reference and "type" in obj and (obj["type"] == "attack-pattern" or obj["type"] == "x-mitre-tactic") and "https://attack.mitre.org/tactics/" in reference['url']:
|
|
mitre_tactics[ reference['external_id'] ] = []
|
|
mitre_tactics_to_pretty_names[ reference['external_id'] ] = obj['name']
|
|
phase_short_names_to_tactics[ obj['x_mitre_shortname'] ] = reference['external_id']
|
|
if "type" in obj and obj["type"] == "intrusion-set":
|
|
mitre_refs_to_names[obj['id']] = obj['name']
|
|
for reference in obj['external_references']:
|
|
if "url" in reference and "https://attack.mitre.org/groups" in reference['url']:
|
|
mitre_groups[reference['external_id']] = {
|
|
"url": reference['url'],
|
|
"name": obj["name"]
|
|
}
|
|
if "type" in obj and (obj["type"] == "tool" or obj["type"] == "malware"):
|
|
mitre_refs_to_names[obj['id']] = obj['name']
|
|
for reference in obj['external_references']:
|
|
if "url" in reference and "https://attack.mitre.org/software" in reference['url']:
|
|
mitre_software[reference['external_id']] = {
|
|
"url": reference['url'],
|
|
"name": obj["name"]
|
|
}
|
|
if "type" in obj and obj['type'] == "relationship":
|
|
if "intrusion-set" in obj['source_ref'] and "attack-pattern" in obj['target_ref']:
|
|
if obj['target_ref'] not in mitre_refs_to_refs:
|
|
mitre_refs_to_refs[obj['target_ref']] = []
|
|
mitre_refs_to_refs[obj['target_ref']].append(obj['source_ref'])
|
|
if "intrusion-set" in obj['target_ref'] and "attack-pattern" in obj['source_ref']:
|
|
if obj['source_ref'] not in mitre_refs_to_refs:
|
|
mitre_refs_to_refs[obj['source_ref']] = []
|
|
mitre_refs_to_refs[obj['source_ref']].append(obj['target_ref'])
|
|
|
|
if ("tool" in obj['source_ref'] or "malware" in obj['source_ref']) and "attack-pattern" in obj['target_ref']:
|
|
if obj['target_ref'] not in mitre_refs_to_refs:
|
|
mitre_refs_to_refs[obj['target_ref']] = []
|
|
mitre_refs_to_refs[obj['target_ref']].append(obj['source_ref'])
|
|
if ("tool" in obj['target_ref'] or "malware" in obj['target_ref']) and "attack-pattern" in obj['source_ref']:
|
|
if obj['source_ref'] not in mitre_refs_to_refs:
|
|
mitre_refs_to_refs[obj['source_ref']] = []
|
|
mitre_refs_to_refs[obj['source_ref']].append(obj['target_ref'])
|
|
|
|
for ref in mitre_refs_to_refs:
|
|
for refvalue in mitre_refs_to_refs[ref]:
|
|
if mitre_refs_to_names[ref] not in popularTechniques:
|
|
popularTechniques[mitre_refs_to_names[ref]] = 1
|
|
else:
|
|
popularTechniques[mitre_refs_to_names[ref]] += 1
|
|
if ref in mitre_refs_to_names and refvalue in mitre_refs_to_names and ((mitre_refs_to_names[refvalue] in groups) or (output=="list")):
|
|
if "intrusion-set" in refvalue and mitre_refs_to_names[ref] not in mitre_techniques_to_groups:
|
|
mitre_techniques_to_groups[mitre_refs_to_names[ref]] = []
|
|
if "intrusion-set" in refvalue and mitre_refs_to_names[refvalue] not in mitre_techniques_to_groups[mitre_refs_to_names[ref]]:
|
|
mitre_techniques_to_groups[mitre_refs_to_names[ref]].append(mitre_refs_to_names[refvalue])
|
|
if ("tool" in refvalue or "malware" in refvalue) and mitre_refs_to_names[ref] not in mitre_techniques_to_software:
|
|
mitre_techniques_to_software[mitre_refs_to_names[ref]] = []
|
|
if ("tool" in refvalue or "malware" in refvalue) and mitre_refs_to_names[refvalue] not in mitre_techniques_to_software[mitre_refs_to_names[ref]]:
|
|
mitre_techniques_to_software[mitre_refs_to_names[ref]].append(mitre_refs_to_names[refvalue])
|
|
|
|
for obj in mitre_attack_blob['objects']:
|
|
if "external_references" in obj:
|
|
for reference in obj['external_references']:
|
|
if "url" in reference and "https://attack.mitre.org/techniques/" in reference['url']:
|
|
excluded=False
|
|
if ("revoked" in obj and obj["revoked"]==True) or (obj["description"].find("This technique has been deprecated")>-1):
|
|
excluded=True
|
|
if "kill_chain_phases" in obj and not excluded and output=="matrix" and "." not in reference['external_id']:
|
|
for phase in obj['kill_chain_phases']:
|
|
if phase['kill_chain_name'] == "mitre-pre-attack" or phase['kill_chain_name'] == "mitre-attack":
|
|
if phase['phase_name'] in phase_short_names_to_tactics:
|
|
num_content_string = ""
|
|
if popularOnly and (reference['external_id'] not in popularTechniques or popularTechniques[reference['external_id']] < popularity_threshold):
|
|
continue
|
|
if inScopeOnly and reference['external_id'] not in inScopeTechniques:
|
|
continue
|
|
if bookmarkedOnly and reference['external_id'] not in bookmarkedTechniques:
|
|
continue
|
|
if techniquesOnly and reference['external_id'] not in techniques:
|
|
continue
|
|
# if inScopeOnly:
|
|
# if inScopeTechniques[reference['external_id']] > 1:
|
|
# num_content_string = " (" + str(inScopeTechniques[reference['external_id']]) + " items)"
|
|
# else:
|
|
# num_content_string = " (" + str(inScopeTechniques[reference['external_id']]) + " item)"
|
|
|
|
if len(platforms)>0:
|
|
platform_exclusion=True
|
|
for platform in platforms:
|
|
for x_mitre_platforms in obj['x_mitre_platforms']:
|
|
if platform in x_mitre_platforms.lower():
|
|
platform_exclusion=False
|
|
if platform_exclusion==True:
|
|
continue
|
|
|
|
if reference['external_id'] in mitre_techniques_to_groups:
|
|
if prettyPrint:
|
|
mitre_tactics[ phase_short_names_to_tactics[ phase['phase_name'] ] ].append(obj['name'] + " (" + ", ".join(mitre_techniques_to_groups[ reference['external_id'] ]) + ")" + num_content_string)
|
|
else:
|
|
mitre_tactics[ phase_short_names_to_tactics[ phase['phase_name'] ] ].append( reference['external_id'] )
|
|
else:
|
|
if forGroupOnly:
|
|
continue
|
|
if prettyPrint:
|
|
mitre_tactics[ phase_short_names_to_tactics[ phase['phase_name'] ] ].append(obj['name'] + num_content_string)
|
|
else:
|
|
mitre_tactics[ phase_short_names_to_tactics[ phase['phase_name'] ] ].append( reference['external_id'] )
|
|
elif "kill_chain_phases" in obj and not excluded and output=="list":
|
|
for phase in obj['kill_chain_phases']:
|
|
if phase['kill_chain_name'] == "mitre-pre-attack" or phase['kill_chain_name'] == "mitre-attack":
|
|
if phase['phase_name'] in phase_short_names_to_tactics:
|
|
matrix_pretty_name="Enterprise ATT&CK"
|
|
if (phase['kill_chain_name'] == "mitre-pre-attack"):
|
|
matrix_pretty_name="PRE-ATT&CK"
|
|
|
|
if "." in reference['external_id']:
|
|
techniqueId=reference['external_id'].split(".")[0]
|
|
techniqueName=mitre_names[techniqueId]
|
|
sub_techniqueId=reference['external_id']
|
|
sub_techniqueName=obj['name']
|
|
techniqueIdCombined=reference['external_id']
|
|
else:
|
|
techniqueId=reference['external_id']
|
|
techniqueName=mitre_names[reference['external_id']]
|
|
sub_techniqueId="-"
|
|
sub_techniqueName="-"
|
|
techniqueIdCombined=reference['external_id']
|
|
|
|
platforms = ""
|
|
data_sources = "-"
|
|
threat_groups = "-"
|
|
software = "-"
|
|
|
|
if reference['external_id'] in mitre_techniques_to_groups:
|
|
threat_groups = "|".join(mitre_techniques_to_groups[ reference['external_id'] ])
|
|
if techniqueIdCombined in mitre_techniques_to_software:
|
|
software = "|".join(mitre_techniques_to_software[ techniqueIdCombined ])
|
|
|
|
if "x_mitre_platforms" in obj:
|
|
platforms = "|".join(obj['x_mitre_platforms'])
|
|
if "x_mitre_data_sources" in obj:
|
|
data_sources = "|".join(obj['x_mitre_data_sources'])
|
|
|
|
mitre_techniques[phase_short_names_to_tactics[phase['phase_name']]+" - "+reference['external_id']] = [matrix_pretty_name,phase_short_names_to_tactics[phase['phase_name']],mitre_tactics_to_pretty_names[phase_short_names_to_tactics[phase['phase_name']]],1,techniqueId,techniqueName, 1,sub_techniqueId,sub_techniqueName,1,techniqueIdCombined,threat_groups,platforms,data_sources,software]
|
|
|
|
else:
|
|
print("Error!")
|
|
print('"' + "Could not find an attack phase called: " + which_killchain.replace('"', '""') + '"')
|
|
sys.exit()
|
|
|
|
#print json.dumps(mitre_tactics, sort_keys = True, indent=4)
|
|
# w = csv.DictWriter(sys.stdout, mitre_tactics.keys())
|
|
# w.writeheader()
|
|
# w.writerow(mitre_tactics)
|
|
|
|
# w = csv.writer(sys.stdout)
|
|
# w.writerows(mitre_tactics.items())
|
|
|
|
|
|
|
|
|
|
w = csv.writer(sys.stdout)
|
|
all_rows = []
|
|
if output=="list":
|
|
list_columns = []
|
|
list_columns.append("Matrix")
|
|
list_columns.append("TacticId")
|
|
list_columns.append("Tactic")
|
|
list_columns.append("Tactic_Order")
|
|
list_columns.append("TechniqueId")
|
|
list_columns.append("Technique")
|
|
list_columns.append("Technique_Order")
|
|
list_columns.append("Sub_TechniqueId")
|
|
list_columns.append("Sub_Technique")
|
|
list_columns.append("Sub_Technique_Order")
|
|
list_columns.append("TechniqueIdCombined")
|
|
list_columns.append("Threat_Groups")
|
|
list_columns.append("Platforms")
|
|
list_columns.append("Data_Sources")
|
|
list_columns.append("Software")
|
|
list_columns.append("Version")
|
|
w.writerow(list_columns)
|
|
elif prettyPrint:
|
|
pretty_columns = []
|
|
for column in columns:
|
|
mitre_tactics[column].sort()
|
|
pretty_columns.append(mitre_tactics_to_pretty_names[column])
|
|
#w.writerow(pretty_columns)
|
|
all_rows.append(pretty_columns)
|
|
else:
|
|
w.writerow(columns)
|
|
|
|
if (output=="matrix"):
|
|
longest_key = len(mitre_tactics[max(mitre_tactics, key= lambda x: len(set(mitre_tactics[x])))])
|
|
for i in range(0, longest_key):
|
|
currentRow = []
|
|
for tactic in columns:
|
|
if i < len(mitre_tactics[tactic]):
|
|
if len(groups)>0:
|
|
if tactic in mitre_techniques_to_groups:
|
|
currentRow.append(", ".join(mitre_techniques_to_groups[tactic]))
|
|
else:
|
|
currentRow.append(mitre_tactics[tactic][i])
|
|
else:
|
|
currentRow.append(mitre_tactics[tactic][i])
|
|
else:
|
|
currentRow.append("")
|
|
# right here is where we would need to add each list to a list and start iterating through
|
|
all_rows.append(currentRow)
|
|
|
|
# the way this works is using a list of lists we first identify how many columns there are
|
|
# a dict then gets created with a numberic key starting at 0 for each column
|
|
# the value for that key gets initialized with an empty list
|
|
# iterate through the rows and check each element in the row.
|
|
# the end result is a dict that each key corresponds to a row and has a list that tracks whether or not it has an element
|
|
# use that to then find all of the columns that do not have any content and create a new list of columns to delete
|
|
|
|
final_rows = []
|
|
total_columns = 0
|
|
columns_to_delete = []
|
|
# get the total number of columns
|
|
for i in all_rows:
|
|
total_columns = len(i)
|
|
|
|
|
|
csv_tracker = {}
|
|
for i in range(0,total_columns):
|
|
csv_tracker[i] = []
|
|
|
|
# create this as an iterator outside the loop since we need to skip the first row with the tactic names
|
|
allRows = iter(all_rows)
|
|
next(allRows)
|
|
for row in allRows:
|
|
# check each column of our one row and update our dict. if a cell has a value we add a 1
|
|
# if not we add a 0
|
|
# dict gets updated based on the key and the column number
|
|
for column in range(0,total_columns):
|
|
if row[column] == "":
|
|
csv_tracker[column].append(0)
|
|
else:
|
|
csv_tracker[column].append(1)
|
|
|
|
# now that we have somewhat of a structure find any of the lists that are all 0's
|
|
for i in csv_tracker:
|
|
delete = True
|
|
# iterate through each k,v pair in the dict
|
|
for j in csv_tracker[i]:
|
|
# iterate through the list for the key. if we find a 1 we set delete to false
|
|
if j == 1:
|
|
delete = False
|
|
# assuming we didn't find a 1 we add that key from our dict to our final list of columns to remove
|
|
if delete == True:
|
|
columns_to_delete.append(i)
|
|
|
|
cols_to_remove = sorted(columns_to_delete, reverse=True) # Reverse so we remove from the end first
|
|
row_count = 0 # Current amount of rows processed
|
|
|
|
# Determine if the matrix lookup cache should be updated or not. Only do this if anything actually returned.
|
|
matrix_writer = None
|
|
if refresh_matrix_cache == True and longest_key>0 and use_cache:
|
|
lookup_tmp = make_splunkhome_path(['var', 'run', 'splunk', 'lookup_tmp'])
|
|
if not os.path.exists(lookup_tmp):
|
|
os.makedirs(lookup_tmp)
|
|
matrix_lookup_filename_tmp = str(lookup_tmp) + "/" + str(matrix_lookup_filename)
|
|
# debug.append({"message": "lookup_tmp", lookup_tmp: lookup_tmp})
|
|
# debug.append({"message": "matrix_lookup_filename_tmp", matrix_lookup_filename_tmp: matrix_lookup_filename_tmp})
|
|
# if EnableDebug:
|
|
# print(json.dumps(debug))
|
|
# Write the new lookup to the temp folder on the local sh
|
|
matrix_file = open(matrix_lookup_filename_tmp, "w")
|
|
matrix_writer = csv.writer(matrix_file)
|
|
|
|
for row in all_rows:
|
|
row_count += 1
|
|
for col_index in cols_to_remove:
|
|
del row[col_index]
|
|
w.writerow(row)
|
|
if matrix_writer:
|
|
matrix_writer.writerow(row)
|
|
|
|
# Add the returned matrix to the lookup cache. Only do this if antyhing actually returned.
|
|
if refresh_matrix_cache and longest_key>0 and use_cache:
|
|
matrix_file.close()
|
|
lookups.addLookup(matrix_lookup_filename)
|
|
|
|
|
|
else:
|
|
mitre_techniques = dict( sorted(mitre_techniques.items(),
|
|
key=lambda item: item[1],
|
|
reverse=False))
|
|
tacticCounter=1
|
|
techniqueCounter=1
|
|
sub_techniqueCounter=0
|
|
currentTactic = ""
|
|
currentTechnique = ""
|
|
version = "-"
|
|
if "version" in mitre_attack_blob:
|
|
version = mitre_attack_blob['version']
|
|
for technique in mitre_techniques:
|
|
currentRow = []
|
|
if (len(currentTactic) == 0):
|
|
currentTactic=mitre_techniques[technique][1]
|
|
if (len(currentTechnique) == 0):
|
|
currentTechnique=mitre_techniques[technique][4]
|
|
if currentTactic not in mitre_techniques[technique][1]:
|
|
tacticCounter+=1
|
|
techniqueCounter=1
|
|
sub_techniqueCounter=1
|
|
currentTactic=mitre_techniques[technique][1]
|
|
if currentTechnique not in mitre_techniques[technique][4]:
|
|
techniqueCounter+=1
|
|
currentTechnique=mitre_techniques[technique][4]
|
|
sub_techniqueCounter=0
|
|
|
|
for i in range(0, len(mitre_techniques[technique])):
|
|
if technique in mitre_techniques and i<len(mitre_techniques[technique]):
|
|
if (i==3):
|
|
currentRow.append(AllMitreTactics.index(currentTactic)+1)
|
|
elif (i==6):
|
|
currentRow.append(techniqueCounter)
|
|
elif (i==9):
|
|
if sub_techniqueCounter==0:
|
|
currentRow.append("")
|
|
else:
|
|
currentRow.append(sub_techniqueCounter)
|
|
sub_techniqueCounter+=1
|
|
else:
|
|
currentRow.append(mitre_techniques[technique][i])
|
|
currentRow.append(version)
|
|
|
|
w.writerow(currentRow)
|