csle-attack-profiler 0.5.3__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of csle-attack-profiler might be problematic. Click here for more details.

@@ -1 +1 @@
1
- __version__ = '0.5.3'
1
+ __version__ = '0.6.0'
@@ -9,6 +9,459 @@ import numpy as np
9
9
  import sys
10
10
 
11
11
 
12
+ class HMMProfiler:
13
+ """
14
+ The HMMProfiler class is used to profile a sequence of observations based on a Hidden Markov Model (HMM).
15
+ """
16
+
17
+ def __init__(self, statistics: List[EmulationStatistics], model_name: Union[str, None] = None) -> None:
18
+ """
19
+ Class constructor
20
+
21
+ :param statistics: The list of EmulationStatistics objects
22
+ :param model_name: The name of the model
23
+ """
24
+ self.statistics = statistics
25
+ self.transition_matrix: List[List[float]] = []
26
+ self.emission_matrix: List[List[float]] = []
27
+ self.hidden_states: List[str] = []
28
+ self.emission_matrix_observations: List[int] = []
29
+ self.start_state_probs: List[float] = []
30
+ self.model_name = None
31
+
32
+ def create_model(self, transition_matrix: List[List[float]],
33
+ hidden_states: List[str], metric: str,
34
+ save_model: bool = False, location: str = ".") -> None:
35
+ """
36
+ Creates the HMM model based on the given transition matrix, states and metrics.
37
+ If save = True, matrices are saved to given location
38
+
39
+ :param transition_matrix: The transition matrix
40
+ :param states: The list of states of the HMM (format: 'A:attack_name' or
41
+ 'no_intrusion' based on emulation statistics file)
42
+ :param metrics: The list of metrics to profile
43
+ :param save: Whether to save the matrices to a file
44
+ :param location: The location to save the matrices, if save = True, e.g "./resources",
45
+ default is current directory
46
+ """
47
+ emission_matrix, emission_matrix_observations = self.get_matrices_of_observation(self.statistics,
48
+ metric, hidden_states)
49
+ self.emission_matrix = emission_matrix
50
+ self.emission_matrix_observations = emission_matrix_observations
51
+ self.transition_matrix = transition_matrix
52
+ self.start_state_probs = self.calculate_initial_states(self.transition_matrix)
53
+ self.hidden_states = hidden_states
54
+ if save_model and location:
55
+ np.save(f'{location}/transition_matrix.npy', transition_matrix)
56
+ np.save(f'{location}/hidden_states.npy', hidden_states)
57
+ np.save(f'{location}/start_state_probs.npy', self.start_state_probs)
58
+ np.save(f'{location}/emission_matrix_{metric}.npy', emission_matrix)
59
+ np.save(f'{location}/emission_matrix_observations_{metric}.npy', emission_matrix_observations)
60
+
61
+ def load_model(self, location: str, metric: str) -> None:
62
+ """
63
+ Loads the HMM model from the given location.
64
+
65
+ :param location: The location of the model files, default is current directory
66
+ """
67
+ self.transition_matrix = np.load(f'{location}/transition_matrix.npy')
68
+ self.hidden_states = np.load(f'{location}/hidden_states.npy')
69
+ self.start_state_probs = np.load(f'{location}/start_state_probs.npy')
70
+ self.emission_matrix = np.load(f'{location}/emission_matrix_{metric}.npy')
71
+ self.emission_matrix_observations = np.load(f'{location}/emission_matrix_observations_{metric}.npy')
72
+
73
+ def profile_sequence(self, sequence: List[int]) -> List[str]:
74
+ """
75
+ Profiles a sequence of observations based on the HMM model.
76
+
77
+ :param sequence: The sequence of observations
78
+
79
+
80
+ :return: The most likely sequence of states
81
+ """
82
+
83
+ path = HMMProfiler.viterbi(self.hidden_states, self.start_state_probs,
84
+ self.transition_matrix, self.emission_matrix,
85
+ sequence, self.emission_matrix_observations)
86
+ profiled_sequence = []
87
+ for i in range(len(path)):
88
+ profiled_sequence.append(self.hidden_states[int(path[i])])
89
+
90
+ return profiled_sequence
91
+
92
+ def get_matrices_of_observation(self, statistics: List[EmulationStatistics],
93
+ metric: str, states: List[str]) -> Tuple[List[List[float]], List[int]]:
94
+ """
95
+ Creates the emission matrix for a given metric based on the statistics from the EmulationStatistics objects.
96
+
97
+ :param statistics: The list of EmulationStatistics objects
98
+ :param metric: The metric to get the emission matrix for
99
+ :param states: The list of states
100
+
101
+ :return: The emission matrix, the list of observations, the list of states
102
+ """
103
+ emission_matrix = []
104
+ attack_observations = {}
105
+ attack_observations_total_counts = {}
106
+ all_keys = set()
107
+
108
+ for stats in statistics:
109
+ for condition, metric_distribution in stats.conditionals_counts.items():
110
+ action = condition.split('_')
111
+ if action[0] == 'no':
112
+ action[0] = 'no_intrusion'
113
+ if action[0] not in attack_observations:
114
+ # We are not intrested in the observations from 'intrusion' or 'A:Continue'
115
+ if action[0] == 'intrusion' or action[0] == 'A:Continue':
116
+ continue
117
+ else:
118
+ # Add the observations of the attack to the dictionary
119
+ if metric in metric_distribution:
120
+ attack_observations[action[0]] = metric_distribution[metric]
121
+ # Sum the total counts of the observations
122
+ attack_observations_total_counts[action[0]] = sum(attack_observations[action[0]].values())
123
+ # Aggregate the counts from the metric distribution
124
+ else:
125
+ counts_observation = metric_distribution[metric]
126
+ for element in counts_observation:
127
+ if element in attack_observations[action[0]]:
128
+ # Aggregate the counts if the element is already in the dictionary
129
+ attack_observations[action[0]][element] += counts_observation[element]
130
+ else:
131
+ attack_observations[action[0]][element] = counts_observation[element]
132
+ # Sum the total counts of the observations
133
+ attack_observations_total_counts[action[0]] += sum(attack_observations[action[0]].values())
134
+
135
+ # Store all possible values for the observation
136
+ if action[0] in attack_observations:
137
+ all_keys.update(attack_observations[action[0]])
138
+
139
+ # Normalize the counts
140
+ for attack, _ in attack_observations.items():
141
+ attack_observations_total_counts[attack] = sum(attack_observations[attack].values())
142
+ for key in all_keys:
143
+ int_key = int(key)
144
+ if key in attack_observations[attack]:
145
+ count = attack_observations[attack].pop(key, 0)
146
+ attack_observations[attack][int_key] = count / attack_observations_total_counts[attack]
147
+ else:
148
+ attack_observations[attack][int_key] = 0
149
+ # Sort the dictionary by key
150
+ attack_observations[attack] = dict(sorted(attack_observations[attack].items()))
151
+
152
+ # Take any attack as the reference to get the keys
153
+ emission_matrix_observations = []
154
+ emission_matrix_states = []
155
+ # Create the emission matrix
156
+ for state in states:
157
+ if state in attack_observations:
158
+ # Normalize the and then append
159
+ emission_matrix.append(list(attack_observations[state].values()))
160
+ # Get the keys of all observations
161
+ emission_matrix_observations = list(attack_observations[state].keys())
162
+ emission_matrix_states.append(state)
163
+ else:
164
+ # LaPlace smoothing for missing observations
165
+ num_keys = len(all_keys)
166
+ laplace_probability = 1 / (num_keys + 2)
167
+ laplace_sum = laplace_probability * num_keys
168
+ laplace_probability_adj = laplace_probability / laplace_sum
169
+ emission_matrix.append([laplace_probability_adj] * num_keys)
170
+ emission_matrix_states.append(state)
171
+
172
+ # Check if the sum of the probabilities is 1
173
+ for i in range(len(emission_matrix)):
174
+ sum_prob = round(sum(emission_matrix[i]), 10)
175
+ if sum_prob != 1:
176
+ print(f'Sum of probabilities for state {emission_matrix_states[i]} is {sum_prob}')
177
+
178
+ return (emission_matrix, emission_matrix_observations)
179
+
180
+ def convert_states_to_profiles(self, states: List[str]) -> List[Union[AttackProfiler, str]]:
181
+ """
182
+ Converts a list of states to a list of AttackProfiles.
183
+
184
+ :param states: The list of states to convert
185
+
186
+ :return: The list of EmulationAttackerActionId
187
+ """
188
+
189
+ new_states: List[Union[AttackProfiler, str]] = []
190
+ for state in states:
191
+ if state == 'A:Continue':
192
+ action = EmulationAttackerAction(id=EmulationAttackerActionId.CONTINUE, name="Continue", cmds=[],
193
+ type=None, descr="CONTINUE", ips=[], index=0, action_outcome='')
194
+ p = AttackProfiler.get_attack_profile(action)
195
+ new_states.append(p)
196
+ elif state == 'A:CVE-2015-1427 exploit':
197
+ action = EmulationAttackerAction(
198
+ id=EmulationAttackerActionId.CVE_2015_1427_EXPLOIT, name="CVE-2015-1427 exploit", cmds=None,
199
+ type=EmulationAttackerActionType.EXPLOIT,
200
+ descr="Uses the CVE-2015-1427 vulnerability to "
201
+ "get remote code execution and then sets up a SSH backdoor"
202
+ "to upgrade the channel", index=None, ips=[],
203
+ action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
204
+ p = AttackProfiler.get_attack_profile(action)
205
+ new_states.append(p)
206
+ elif state == 'A:DVWA SQL Injection Exploit':
207
+ action = EmulationAttackerAction(
208
+ id=EmulationAttackerActionId.DVWA_SQL_INJECTION, name="DVWA SQL Injection Exploit",
209
+ cmds=None, type=EmulationAttackerActionType.EXPLOIT,
210
+ descr="Uses the DVWA SQL Injection exploit to extract secret passwords",
211
+ index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
212
+ p = AttackProfiler.get_attack_profile(action)
213
+ new_states.append(p)
214
+ elif state == 'A:Install tools':
215
+ action = EmulationAttackerAction(
216
+ id=EmulationAttackerActionId.INSTALL_TOOLS, name="Install tools", cmds=None,
217
+ type=EmulationAttackerActionType.POST_EXPLOIT,
218
+ descr="If taken root on remote machine, installs pentest tools, e.g. nmap",
219
+ index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.PIVOTING)
220
+ p = AttackProfiler.get_attack_profile(action)
221
+ new_states.append(p)
222
+ elif state == 'A:Network service login':
223
+ action = EmulationAttackerAction(
224
+ id=EmulationAttackerActionId.NETWORK_SERVICE_LOGIN, name="Network service login",
225
+ cmds=[], type=EmulationAttackerActionType.POST_EXPLOIT,
226
+ descr="Uses known credentials to login to network services on a server",
227
+ index=None, ips=None, action_outcome=EmulationAttackerActionOutcome.LOGIN)
228
+ p = AttackProfiler.get_attack_profile(action)
229
+ new_states.append(p)
230
+ elif state == 'A:Ping Scan':
231
+ action = EmulationAttackerAction(
232
+ id=EmulationAttackerActionId.PING_SCAN_HOST, name="Ping Scan",
233
+ cmds=None, type=EmulationAttackerActionType.RECON,
234
+ descr="A host discovery scan, it is quick because it only checks of hosts "
235
+ "are up with Ping, without scanning the ports.", ips=None, index=None,
236
+ action_outcome=EmulationAttackerActionOutcome.INFORMATION_GATHERING, backdoor=False)
237
+ p = AttackProfiler.get_attack_profile(action)
238
+ new_states.append(p)
239
+ elif state == 'A:Sambacry Explolit':
240
+ action = EmulationAttackerAction(
241
+ id=EmulationAttackerActionId.SAMBACRY_EXPLOIT, name="Sambacry Explolit", cmds=None,
242
+ type=EmulationAttackerActionType.EXPLOIT,
243
+ descr="Uses the sambacry shell to get remote code execution and then"
244
+ "sets up a SSH backdoor to upgrade the channel",
245
+ index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
246
+ p = AttackProfiler.get_attack_profile(action)
247
+ new_states.append(p)
248
+ elif state == 'A:ShellShock Explolit':
249
+ action = EmulationAttackerAction(
250
+ id=EmulationAttackerActionId.SHELLSHOCK_EXPLOIT, name="ShellShock Explolit",
251
+ cmds=None, type=EmulationAttackerActionType.EXPLOIT,
252
+ descr="Uses the Shellshock exploit and curl to do remote code execution and create a backdoor",
253
+ index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
254
+ p = AttackProfiler.get_attack_profile(action)
255
+ new_states.append(p)
256
+ elif state == 'A:SSH dictionary attack for username=pw':
257
+ action = EmulationAttackerAction(
258
+ id=EmulationAttackerActionId.SSH_SAME_USER_PASS_DICTIONARY_HOST,
259
+ name="SSH dictionary attack for username=pw", cmds=None,
260
+ type=EmulationAttackerActionType.EXPLOIT, index=None,
261
+ descr="A dictionary attack that tries common passwords and usernames for SSH"
262
+ "where username=password", ips=None, action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
263
+ p = AttackProfiler.get_attack_profile(action)
264
+ new_states.append(p)
265
+ elif state == 'A:FTP dictionary attack for username=pw':
266
+ action = EmulationAttackerAction(
267
+ id=EmulationAttackerActionId.FTP_SAME_USER_PASS_DICTIONARY_HOST,
268
+ name="FTP dictionary attack for username=pw", cmds=None, type=EmulationAttackerActionType.EXPLOIT,
269
+ index=None, descr="A dictionary attack that tries common passwords and"
270
+ "usernames for FTP where username=password", ips=None,
271
+ action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
272
+ p = AttackProfiler.get_attack_profile(action)
273
+ new_states.append(p)
274
+ elif state == 'A:Telnet dictionary attack for username=pw':
275
+ action = EmulationAttackerAction(
276
+ id=EmulationAttackerActionId.TELNET_SAME_USER_PASS_DICTIONARY_HOST,
277
+ name="Telnet dictionary attack for username=pw", cmds=None,
278
+ type=EmulationAttackerActionType.EXPLOIT, index=None,
279
+ descr="A dictionary attack that tries common passwords and usernames for"
280
+ "Telnet where username=password", ips=None,
281
+ action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
282
+ p = AttackProfiler.get_attack_profile(action)
283
+ new_states.append(p)
284
+ elif state == 'A:CVE-2010-0426 exploit':
285
+ action = EmulationAttackerAction(
286
+ id=EmulationAttackerActionId.CVE_2010_0426_PRIV_ESC,
287
+ name="CVE-2010-0426 exploit", cmds=None, type=EmulationAttackerActionType.PRIVILEGE_ESCALATION,
288
+ descr="Uses the CVE-2010-0426 vulnerability to perform privilege escalation to get root access",
289
+ index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.PRIVILEGE_ESCALATION_ROOT)
290
+ p = AttackProfiler.get_attack_profile(action)
291
+ new_states.append(p)
292
+ elif state == 'A:TCP SYN (Stealth) Scan':
293
+ action = EmulationAttackerAction(
294
+ id=EmulationAttackerActionId.TCP_SYN_STEALTH_SCAN_HOST, name="TCP SYN (Stealth) Scan",
295
+ cmds=None, type=EmulationAttackerActionType.RECON,
296
+ descr="A stealthy and fast TCP SYN scan to detect open TCP ports on the subnet", ips=None,
297
+ index=None, action_outcome=EmulationAttackerActionOutcome.INFORMATION_GATHERING, backdoor=False)
298
+ p = AttackProfiler.get_attack_profile(action)
299
+ new_states.append(p)
300
+ elif state == 'ssh backdoor':
301
+ action = EmulationAttackerAction(
302
+ id=EmulationAttackerActionId.SSH_BACKDOOR, name="Install SSH backdoor",
303
+ cmds=None, type=EmulationAttackerActionType.POST_EXPLOIT,
304
+ descr="If taken root on remote machine, installs a ssh backdoor useful for"
305
+ "upgrading telnetor weaker channels", index=None, ips=[],
306
+ action_outcome=EmulationAttackerActionOutcome.PIVOTING, alt_cmds=None, backdoor=True)
307
+ p = AttackProfiler.get_attack_profile(action)
308
+ new_states.append(p)
309
+ else:
310
+ new_states.append(state)
311
+
312
+ return new_states
313
+
314
+ def calculate_initial_states(self, transition_matrix: List[List[float]]) -> List[float]:
315
+ """
316
+ Calculates the initial states probabilities based on the transition matrix.
317
+
318
+ 1 / (# of states)
319
+
320
+ :param transition_matrix: The transition matrix
321
+
322
+ :return: The start states probabilities
323
+ """
324
+ start_states = []
325
+ total_states = len(transition_matrix)
326
+ for _ in range(total_states):
327
+ start_states.append(1 / total_states)
328
+
329
+ return start_states
330
+
331
+ @staticmethod
332
+ def viterbi(hidden_states: List[EmulationAttackerActionId], init_probs: List[float],
333
+ trans_matrix: List[List[float]], emission_matrix: List[List[float]],
334
+ obs: List[int], emissions_list: List[int]) -> List[float]:
335
+ """
336
+ Viterbi algorithm for Hidden Markov Models (HMM).
337
+
338
+ :param hidden_states: The hidden states
339
+ :param init_probs: The initial probabilities of the hidden states
340
+ :param trans_matrix: The transition matrix
341
+ :param emission_matrix: The emission matrix
342
+ :param obs: The observation sequence
343
+ :param emissions_list: The list of possible observations
344
+
345
+ :return: The most likely sequence of hidden states
346
+ """
347
+ # Convert the emissions list to a numpy array, to use the where function
348
+ emissions_list_typed: np.ndarray[int, Any] = np.array(emissions_list)
349
+
350
+ # Check that the sum equals 1
351
+ for i in range(len(emission_matrix)):
352
+ if round(sum(emission_matrix[i]), 10) != 1:
353
+ print(f'Sum of probabilities for state {hidden_states[i]} is not 1')
354
+ print(f'Sum of probabilities: {sum(emission_matrix[i])}')
355
+
356
+ # The number of hidden states
357
+ S = len(hidden_states)
358
+ # The number of observations
359
+ T = len(obs)
360
+
361
+ # The Viterbi matrix (prob) T x S matrix of zeroes
362
+ prob = np.zeros((T, S))
363
+ # The backpointer matrix (prev)
364
+ prev = np.empty((T, S))
365
+ # Initialization
366
+ for i in range(S):
367
+ # Fetch the index of the observation in the emission_matrix
368
+ index, = np.where(emissions_list_typed == obs[0])
369
+ if index[0].size > 0:
370
+ prob[0][i] = init_probs[i] * emission_matrix[i][index[0]]
371
+ else:
372
+ print(f'Observation {obs[0]} not found in the emission matrix')
373
+ sys.exit(1)
374
+
375
+ # Recursion
376
+ for t in range(1, T):
377
+ index, = np.where(emissions_list_typed == obs[t])
378
+ for i in range(S):
379
+ max_prob = -1
380
+ max_state = -1
381
+ for j in range(S):
382
+ new_prob = prob[t - 1][j] * trans_matrix[j][i] * emission_matrix[i][index[0]]
383
+ if new_prob > max_prob:
384
+ max_prob = new_prob
385
+ max_state = j
386
+ prob[t][i] = max_prob
387
+ prev[t][i] = max_state
388
+
389
+ path = np.zeros(T)
390
+ path[T - 1] = np.argmax(prob[T - 1])
391
+ for t in range(T - 2, -1, -1):
392
+ path[t] = prev[t + 1][int(path[t + 1])]
393
+ # Convert the path to a list
394
+ typed_path: List[float] = path.tolist()
395
+
396
+ return typed_path
397
+
398
+ def generate_sequence(self, intrusion_length: int, initial_state_index: int,
399
+ seed: Union[int, None] = None) -> Tuple[List[str], List[int]]:
400
+ """
401
+ Generates a sequence of states and corresponding observations based on the given emission matrix,
402
+ and transition matrix. First, a sequence of observation from 'no intrusion' is generated
403
+ based on the geometric distribution of the initial state. Then, a sequence observations and states are
404
+ generated based on emission matrix and transition matrix. The length of this intrusion
405
+ sequence is given by the intrusion_length parameter.
406
+
407
+ :param intrusion_length: The length of the intrusion
408
+ :param initial_state_index: The index of the initial state
409
+ :param seed: The seed for the random number generator
410
+
411
+ return: The sequence of states and observations
412
+ """
413
+
414
+ P_obs = self.emission_matrix
415
+ P_states = self.transition_matrix
416
+ states = self.hidden_states
417
+ observations = self.emission_matrix_observations
418
+
419
+ if seed:
420
+ np.random.seed(seed)
421
+ obs_len = len(observations)
422
+ states_len = len(states)
423
+ # Return the geometric distribution of the initial state
424
+ dist = np.random.geometric(p=P_states[initial_state_index][0], size=1000)
425
+ T_i = round(sum(dist) / len(dist))
426
+
427
+ state_seq = [states[initial_state_index]] * T_i
428
+ obs_seq = []
429
+ for i in range(T_i):
430
+
431
+ o_i = np.random.choice(obs_len, p=P_obs[initial_state_index])
432
+ obs_seq.append(observations[o_i])
433
+
434
+ recon_states_sum = np.sum(P_states[initial_state_index][1:])
435
+ recon_states = P_states[initial_state_index][1:] / recon_states_sum
436
+
437
+ intrusion_start_state = np.random.choice(states_len - 1, p=recon_states) + 1
438
+ intrusion_start_observation = np.random.choice(obs_len, p=P_obs[intrusion_start_state])
439
+ state_seq.append(states[intrusion_start_state])
440
+ obs_seq.append(observations[intrusion_start_observation])
441
+
442
+ s_i = intrusion_start_state
443
+ if intrusion_length == 1:
444
+ return state_seq, obs_seq
445
+ for i in range(intrusion_length - 1):
446
+ # si ~ Ps(si | si-1)
447
+ s_i = np.random.choice(states_len, p=P_states[s_i])
448
+ # oi ~ Po(oi | si)
449
+ o_i = np.random.choice(obs_len, p=P_obs[s_i])
450
+ state_seq.append(states[s_i])
451
+ obs_seq.append(observations[o_i])
452
+ return state_seq, obs_seq
453
+
454
+ from csle_common.dao.system_identification.emulation_statistics import EmulationStatistics
455
+ from csle_common.dao.emulation_action.attacker.emulation_attacker_action_id import EmulationAttackerActionId
456
+ from csle_common.dao.emulation_action.attacker.emulation_attacker_action_type import EmulationAttackerActionType
457
+ from csle_common.dao.emulation_action.attacker.emulation_attacker_action_outcome import EmulationAttackerActionOutcome
458
+ from csle_common.dao.emulation_action.attacker.emulation_attacker_action import EmulationAttackerAction
459
+ from csle_attack_profiler.attack_profiler import AttackProfiler
460
+ from typing import List, Union, Tuple, Any
461
+ import numpy as np
462
+ import sys
463
+
464
+
12
465
  class HMMProfiler:
13
466
  """
14
467
  The HMMProfiler class is used to profile a sequence of observations based on a Hidden Markov Model (HMM).
@@ -432,12 +885,14 @@ class HMMProfiler:
432
885
  recon_states_sum = np.sum(P_states[initial_state_index][1:])
433
886
  recon_states = P_states[initial_state_index][1:] / recon_states_sum
434
887
 
435
- intrusion_start_state = np.random.choice(states_len - 1, p=recon_states)
888
+ intrusion_start_state = np.random.choice(states_len - 1, p=recon_states) + 1
436
889
  intrusion_start_observation = np.random.choice(obs_len, p=P_obs[intrusion_start_state])
437
890
  state_seq.append(states[intrusion_start_state])
438
891
  obs_seq.append(observations[intrusion_start_observation])
439
892
 
440
893
  s_i = intrusion_start_state
894
+ if intrusion_length == 1:
895
+ return state_seq, obs_seq
441
896
  for i in range(intrusion_length):
442
897
  # si ~ Ps(si | si-1)
443
898
  s_i = np.random.choice(states_len, p=P_states[s_i])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csle-attack-profiler
3
- Version: 0.5.3
3
+ Version: 0.6.0
4
4
  Summary: Library with MITRE attack profiler for CSLE
5
5
  Author: Bength Pappila
6
6
  Author-email: brpa@kth.se
@@ -15,8 +15,8 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Intended Audience :: Science/Research
16
16
  Requires-Python: >=3.8
17
17
  Requires-Dist: mitreattack-python ==2.0.14
18
- Requires-Dist: csle-base ==0.5.3
19
- Requires-Dist: csle-common ==0.5.3
18
+ Requires-Dist: csle-base ==0.6.0
19
+ Requires-Dist: csle-common ==0.6.0
20
20
  Provides-Extra: testing
21
21
  Requires-Dist: pytest >=6.0 ; extra == 'testing'
22
22
  Requires-Dist: pytest-cov >=2.0 ; extra == 'testing'
@@ -0,0 +1,8 @@
1
+ csle_attack_profiler/__init__.py,sha256=C7_gE0lIQJ8Wh2jgU8C8P_xyvq76bKTf0gm8bGYhMBk,37
2
+ csle_attack_profiler/__version__.py,sha256=CBY3jsC-9HCm7eZ6CKD-sYLCejqOJ1pYWPQM4LGIXcI,22
3
+ csle_attack_profiler/attack_profiler.py,sha256=bYqSeItJkhswronvIRFPyvf1rtvUKEWPPF4K2t1JVqE,10535
4
+ csle_attack_profiler/hmm_profiling.py,sha256=891sNt1bWlqIIUKCnzZaEGnEsIhB6TY3ob46MSHymoI,48264
5
+ csle_attack_profiler-0.6.0.dist-info/METADATA,sha256=biQd6Up9qFTEihixwI4t_jGFAhkr9UWsTPG8bIdL2kM,2006
6
+ csle_attack_profiler-0.6.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
+ csle_attack_profiler-0.6.0.dist-info/top_level.txt,sha256=OuI4zvPo3MQYLQ7Dqm32oM0V8rNdwHe9tjtngx2KVtA,21
8
+ csle_attack_profiler-0.6.0.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- csle_attack_profiler/__init__.py,sha256=C7_gE0lIQJ8Wh2jgU8C8P_xyvq76bKTf0gm8bGYhMBk,37
2
- csle_attack_profiler/__version__.py,sha256=TFkACo7SKbVpSQl32OCSCNmy1zVX-jsQX6dAXMbWmkU,22
3
- csle_attack_profiler/attack_profiler.py,sha256=bYqSeItJkhswronvIRFPyvf1rtvUKEWPPF4K2t1JVqE,10535
4
- csle_attack_profiler/hmm_profiling.py,sha256=6xlvv3Q84E47AYEwH1c89-oAaJb89XJXcNnmS-aO9qg,24128
5
- csle_attack_profiler-0.5.3.dist-info/METADATA,sha256=KebzwlqGaKxWGL2WM3jG_wBXEu5lx7Pvi2EPd_A1PaM,2006
6
- csle_attack_profiler-0.5.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
- csle_attack_profiler-0.5.3.dist-info/top_level.txt,sha256=OuI4zvPo3MQYLQ7Dqm32oM0V8rNdwHe9tjtngx2KVtA,21
8
- csle_attack_profiler-0.5.3.dist-info/RECORD,,