22from stix2 import TAXIICollectionSource , Filter , FileSystemSource
33from taxii2client import Server , Collection
44
5+ class MatrixEntry :
6+ def __init__ (self , id = None , name = None ):
7+ if id is not None :
8+ self .id = id
9+ if name is not None :
10+ self .name = name
11+
12+ @property
13+ def id (self ):
14+ if self .__id is not None :
15+ return self .__id
16+
17+ @id .setter
18+ def id (self , id ):
19+ self .__id = id
20+
21+ @property
22+ def name (self ):
23+ if self .__name is not None :
24+ return self .__name
25+
26+ @name .setter
27+ def name (self , name ):
28+ self .__name = name
529
630class MatrixGen :
731 convert = {
@@ -18,6 +42,18 @@ class MatrixGen:
1842 'exfiltration' : 'Exfiltration' ,
1943 'impact' : 'Impact'
2044 }
45+ t_lookup = {'Initial Access' : 'TA0001' ,
46+ 'Execution' : 'TA0002' ,
47+ 'Persistence' : 'TA0003' ,
48+ 'Privilege Escalation' : 'TA0004' ,
49+ 'Defense Evasion' : 'TA0005' ,
50+ 'Credential Access' : 'TA0006' ,
51+ 'Discovery' : 'TA0007' ,
52+ 'Lateral Movement' : 'TA0008' ,
53+ 'Collection' : 'TA0009' ,
54+ 'Command and Control' : 'TA0011' ,
55+ 'Exfiltration' : 'TA0010' ,
56+ 'Impact' : 'TA0040' }
2157 def __init__ (self , fresh = True ):
2258 """
2359 Initialization - Creates a matrix generator object
@@ -64,19 +100,19 @@ def _get_technique_listing(self, tactic, mode='enterprise'):
64100 :param tactic: The tactic to grab techniques from
65101 :param mode: The domain to draw from
66102 """
67- techniques = {}
103+ techniques = []
68104 subtechs = {}
69105 techs = self .collections [mode ].query ([Filter ('type' , '=' , 'attack-pattern' ), Filter ('kill_chain_phases.phase_name' , '=' , tactic )])
70106 for entry in techs :
71107 if entry ['kill_chain_phases' ][0 ]['kill_chain_name' ] == 'mitre-attack' :
72108 tid = [t ['external_id' ] for t in entry ['external_references' ] if t ['source_name' ] == 'mitre-attack' ]
73109 if '.' not in tid [0 ]:
74- techniques [ entry ['name' ]] = tid [ 0 ]
110+ techniques . append ( MatrixEntry ( id = tid [ 0 ], name = entry ['name' ]))
75111 else :
76112 parent = tid [0 ].split ('.' )[0 ]
77113 if parent not in subtechs :
78114 subtechs [parent ] = []
79- subtechs [parent ].append ({ entry ['name' ]: tid [ 0 ]} )
115+ subtechs [parent ].append (MatrixEntry ( id = tid [ 0 ], name = entry ['name' ]) )
80116 return techniques , subtechs
81117
82118 @staticmethod
@@ -107,18 +143,18 @@ def _construct_panop(codex, subtechs, excludes):
107143 sr = entry [0 ]
108144 joins .append ([entry [0 ], column - 1 , len (stechs [entry [1 ]])])
109145 for element in stechs [entry [1 ]]:
110- matrix_obj [(sr , column )] = [ x for x in element .keys ()][ 0 ]
146+ matrix_obj [(sr , column )] = element .name
111147 sr += 1
112148 cycle = False
113149 column += 1
114150 row = 2
115- matrix_obj [(1 , column )] = col [0 ][ 'tactic' ]
116- c_name = col [0 ][ 'tactic' ]
151+ matrix_obj [(1 , column )] = col [0 ]. name
152+ c_name = col [0 ]. name
117153 stechs = col [1 ]['subtechs' ]
118154 to_add = []
119155 for element in col [2 :]:
120- elname = [ x for x in element .keys ()][ 0 ]
121- tid = element [[ x for x in element . keys ()][ 0 ]]
156+ elname = element .name
157+ tid = element . id
122158 skip = False
123159 for entry in range (0 , len (et )):
124160 if et [entry ] == tid and (e_tacs [entry ] == False or MatrixGen .convert [e_tacs [entry ]] == c_name ):
@@ -139,6 +175,48 @@ def _construct_panop(codex, subtechs, excludes):
139175 row += 1
140176 return matrix_obj , joins
141177
178+ @staticmethod
179+ def _get_ID (codex , name ):
180+ """
181+ INTERNAL - Do lookups to retrieve the ID of a technique given it's name
182+
183+ :param codex: The list of lists matrix object (output of build_matrix)
184+ :param name: The name of the technique to retrieve the ID of
185+ :return: The ID of the technique referenced by name
186+ """
187+ for col in codex :
188+ for elm in col :
189+ if col .index (elm ) == 1 :
190+ for sp in elm ['subtechs' ]:
191+ for t in elm ['subtechs' ][sp ]:
192+ if t .name == name :
193+ return t .id
194+ else :
195+ if elm .name == name :
196+ return elm .id
197+ return ''
198+
199+ @staticmethod
200+ def _get_name (codex , id ):
201+ """
202+ INTERNAL - Do lookups to retrieve the name of a technique given it's ID
203+
204+ :param codex: The list of lists matrix object (output of build_matrix)
205+ :param id: The ID of the technique to retrieve the name of
206+ :return: The name of the technique referenced by id
207+ """
208+ for col in codex :
209+ for elm in col :
210+ if col .index (elm ) == 1 :
211+ for sp in elm ['subtechs' ]:
212+ for t in elm ['subtechs' ][sp ]:
213+ if t .id == id :
214+ return t .name
215+ else :
216+ if elm .id == id :
217+ return elm .name
218+ return ''
219+
142220 def build_matrix (self , mode = 'enterprise' ):
143221 """
144222 Build a ATT&CK matrix object, as a list of lists containing technique dictionaries
@@ -149,29 +227,15 @@ def build_matrix(self, mode='enterprise'):
149227 tacs = self ._get_tactic_listing (mode )
150228 for tac in tacs :
151229 techs , subtechs = self ._get_technique_listing (tac .lower ().replace (' ' , '-' ), mode )
152- colm = [dict (tactic = tac )]
153- temp = []
230+ colm = [MatrixEntry (id = self .t_lookup [tac ], name = tac )]
154231 stemp = {}
155232 # sort subtechniques via id, append to column
156233 for par in subtechs :
157- stemp [par ] = []
158- temp = []
159- for entry in subtechs [par ]:
160- for obj in entry :
161- temp .append (entry [obj ])
162- for entry in sorted (temp ):
163- obj = {}
164- for k in subtechs [par ]:
165- for j in k :
166- if k [j ] == entry :
167- obj [j ] = entry
168- stemp [par ].append (obj )
234+ subtechs [par ].sort (key = lambda x : x .name )
235+ stemp [par ] = subtechs [par ]
169236 colm .append ({'subtechs' :stemp })
170- temp = []
171237 # sort techniques alphabetically, append to column
172- for entry in techs :
173- temp .append (entry )
174- for entry in sorted (temp ):
175- colm .append ({entry :techs [entry ]})
238+ techs .sort (key = lambda x : x .name )
239+ colm .extend (techs )
176240 matrix .append (colm )
177241 return matrix
0 commit comments