csle-attack-profiler 0.6.0__py3-none-any.whl → 0.6.2__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.6.0'
1
+ __version__ = '0.6.2'
@@ -1,12 +1,12 @@
1
+ from typing import List, Union, Tuple, Any
2
+ import numpy as np
3
+ import sys
1
4
  from csle_common.dao.system_identification.emulation_statistics import EmulationStatistics
2
5
  from csle_common.dao.emulation_action.attacker.emulation_attacker_action_id import EmulationAttackerActionId
3
6
  from csle_common.dao.emulation_action.attacker.emulation_attacker_action_type import EmulationAttackerActionType
4
7
  from csle_common.dao.emulation_action.attacker.emulation_attacker_action_outcome import EmulationAttackerActionOutcome
5
8
  from csle_common.dao.emulation_action.attacker.emulation_attacker_action import EmulationAttackerAction
6
9
  from csle_attack_profiler.attack_profiler import AttackProfiler
7
- from typing import List, Union, Tuple, Any
8
- import numpy as np
9
- import sys
10
10
 
11
11
 
12
12
  class HMMProfiler:
@@ -20,6 +20,7 @@ class HMMProfiler:
20
20
 
21
21
  :param statistics: The list of EmulationStatistics objects
22
22
  :param model_name: The name of the model
23
+ :return: None
23
24
  """
24
25
  self.statistics = statistics
25
26
  self.transition_matrix: List[List[float]] = []
@@ -38,11 +39,12 @@ class HMMProfiler:
38
39
 
39
40
  :param transition_matrix: The transition matrix
40
41
  :param states: The list of states of the HMM (format: 'A:attack_name' or
41
- 'no_intrusion' based on emulation statistics file)
42
+ 'no_intrusion' based on emulation statistics file)
42
43
  :param metrics: The list of metrics to profile
43
44
  :param save: Whether to save the matrices to a file
44
45
  :param location: The location to save the matrices, if save = True, e.g "./resources",
45
- default is current directory
46
+ default is current directory
47
+ :return: None
46
48
  """
47
49
  emission_matrix, emission_matrix_observations = self.get_matrices_of_observation(self.statistics,
48
50
  metric, hidden_states)
@@ -63,6 +65,7 @@ class HMMProfiler:
63
65
  Loads the HMM model from the given location.
64
66
 
65
67
  :param location: The location of the model files, default is current directory
68
+ :return: None
66
69
  """
67
70
  self.transition_matrix = np.load(f'{location}/transition_matrix.npy')
68
71
  self.hidden_states = np.load(f'{location}/hidden_states.npy')
@@ -75,18 +78,16 @@ class HMMProfiler:
75
78
  Profiles a sequence of observations based on the HMM model.
76
79
 
77
80
  :param sequence: The sequence of observations
78
-
79
-
80
81
  :return: The most likely sequence of states
81
82
  """
82
-
83
+
83
84
  path = HMMProfiler.viterbi(self.hidden_states, self.start_state_probs,
84
85
  self.transition_matrix, self.emission_matrix,
85
86
  sequence, self.emission_matrix_observations)
86
87
  profiled_sequence = []
87
88
  for i in range(len(path)):
88
89
  profiled_sequence.append(self.hidden_states[int(path[i])])
89
-
90
+
90
91
  return profiled_sequence
91
92
 
92
93
  def get_matrices_of_observation(self, statistics: List[EmulationStatistics],
@@ -97,14 +98,13 @@ class HMMProfiler:
97
98
  :param statistics: The list of EmulationStatistics objects
98
99
  :param metric: The metric to get the emission matrix for
99
100
  :param states: The list of states
100
-
101
101
  :return: The emission matrix, the list of observations, the list of states
102
102
  """
103
103
  emission_matrix = []
104
104
  attack_observations = {}
105
105
  attack_observations_total_counts = {}
106
106
  all_keys = set()
107
-
107
+
108
108
  for stats in statistics:
109
109
  for condition, metric_distribution in stats.conditionals_counts.items():
110
110
  action = condition.split('_')
@@ -118,7 +118,7 @@ class HMMProfiler:
118
118
  # Add the observations of the attack to the dictionary
119
119
  if metric in metric_distribution:
120
120
  attack_observations[action[0]] = metric_distribution[metric]
121
- # Sum the total counts of the observations
121
+ # Sum the total counts of the observations
122
122
  attack_observations_total_counts[action[0]] = sum(attack_observations[action[0]].values())
123
123
  # Aggregate the counts from the metric distribution
124
124
  else:
@@ -135,7 +135,7 @@ class HMMProfiler:
135
135
  # Store all possible values for the observation
136
136
  if action[0] in attack_observations:
137
137
  all_keys.update(attack_observations[action[0]])
138
-
138
+
139
139
  # Normalize the counts
140
140
  for attack, _ in attack_observations.items():
141
141
  attack_observations_total_counts[attack] = sum(attack_observations[attack].values())
@@ -182,7 +182,6 @@ class HMMProfiler:
182
182
  Converts a list of states to a list of AttackProfiles.
183
183
 
184
184
  :param states: The list of states to convert
185
-
186
185
  :return: The list of EmulationAttackerActionId
187
186
  """
188
187
 
@@ -198,8 +197,8 @@ class HMMProfiler:
198
197
  id=EmulationAttackerActionId.CVE_2015_1427_EXPLOIT, name="CVE-2015-1427 exploit", cmds=None,
199
198
  type=EmulationAttackerActionType.EXPLOIT,
200
199
  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=[],
200
+ "get remote code execution and then sets up a SSH backdoor"
201
+ "to upgrade the channel", index=None, ips=[],
203
202
  action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
204
203
  p = AttackProfiler.get_attack_profile(action)
205
204
  new_states.append(p)
@@ -232,7 +231,7 @@ class HMMProfiler:
232
231
  id=EmulationAttackerActionId.PING_SCAN_HOST, name="Ping Scan",
233
232
  cmds=None, type=EmulationAttackerActionType.RECON,
234
233
  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,
234
+ "are up with Ping, without scanning the ports.", ips=None, index=None,
236
235
  action_outcome=EmulationAttackerActionOutcome.INFORMATION_GATHERING, backdoor=False)
237
236
  p = AttackProfiler.get_attack_profile(action)
238
237
  new_states.append(p)
@@ -241,7 +240,7 @@ class HMMProfiler:
241
240
  id=EmulationAttackerActionId.SAMBACRY_EXPLOIT, name="Sambacry Explolit", cmds=None,
242
241
  type=EmulationAttackerActionType.EXPLOIT,
243
242
  descr="Uses the sambacry shell to get remote code execution and then"
244
- "sets up a SSH backdoor to upgrade the channel",
243
+ "sets up a SSH backdoor to upgrade the channel",
245
244
  index=None, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
246
245
  p = AttackProfiler.get_attack_profile(action)
247
246
  new_states.append(p)
@@ -259,7 +258,8 @@ class HMMProfiler:
259
258
  name="SSH dictionary attack for username=pw", cmds=None,
260
259
  type=EmulationAttackerActionType.EXPLOIT, index=None,
261
260
  descr="A dictionary attack that tries common passwords and usernames for SSH"
262
- "where username=password", ips=None, action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
261
+ "where username=password", ips=None,
262
+ action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
263
263
  p = AttackProfiler.get_attack_profile(action)
264
264
  new_states.append(p)
265
265
  elif state == 'A:FTP dictionary attack for username=pw':
@@ -267,7 +267,7 @@ class HMMProfiler:
267
267
  id=EmulationAttackerActionId.FTP_SAME_USER_PASS_DICTIONARY_HOST,
268
268
  name="FTP dictionary attack for username=pw", cmds=None, type=EmulationAttackerActionType.EXPLOIT,
269
269
  index=None, descr="A dictionary attack that tries common passwords and"
270
- "usernames for FTP where username=password", ips=None,
270
+ "usernames for FTP where username=password", ips=None,
271
271
  action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
272
272
  p = AttackProfiler.get_attack_profile(action)
273
273
  new_states.append(p)
@@ -277,7 +277,7 @@ class HMMProfiler:
277
277
  name="Telnet dictionary attack for username=pw", cmds=None,
278
278
  type=EmulationAttackerActionType.EXPLOIT, index=None,
279
279
  descr="A dictionary attack that tries common passwords and usernames for"
280
- "Telnet where username=password", ips=None,
280
+ "Telnet where username=password", ips=None,
281
281
  action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
282
282
  p = AttackProfiler.get_attack_profile(action)
283
283
  new_states.append(p)
@@ -302,7 +302,7 @@ class HMMProfiler:
302
302
  id=EmulationAttackerActionId.SSH_BACKDOOR, name="Install SSH backdoor",
303
303
  cmds=None, type=EmulationAttackerActionType.POST_EXPLOIT,
304
304
  descr="If taken root on remote machine, installs a ssh backdoor useful for"
305
- "upgrading telnetor weaker channels", index=None, ips=[],
305
+ "upgrading telnetor weaker channels", index=None, ips=[],
306
306
  action_outcome=EmulationAttackerActionOutcome.PIVOTING, alt_cmds=None, backdoor=True)
307
307
  p = AttackProfiler.get_attack_profile(action)
308
308
  new_states.append(p)
@@ -310,7 +310,7 @@ class HMMProfiler:
310
310
  new_states.append(state)
311
311
 
312
312
  return new_states
313
-
313
+
314
314
  def calculate_initial_states(self, transition_matrix: List[List[float]]) -> List[float]:
315
315
  """
316
316
  Calculates the initial states probabilities based on the transition matrix.
@@ -318,7 +318,6 @@ class HMMProfiler:
318
318
  1 / (# of states)
319
319
 
320
320
  :param transition_matrix: The transition matrix
321
-
322
321
  :return: The start states probabilities
323
322
  """
324
323
  start_states = []
@@ -341,7 +340,6 @@ class HMMProfiler:
341
340
  :param emission_matrix: The emission matrix
342
341
  :param obs: The observation sequence
343
342
  :param emissions_list: The list of possible observations
344
-
345
343
  :return: The most likely sequence of hidden states
346
344
  """
347
345
  # Convert the emissions list to a numpy array, to use the where function
@@ -407,470 +405,18 @@ class HMMProfiler:
407
405
  :param intrusion_length: The length of the intrusion
408
406
  :param initial_state_index: The index of the initial state
409
407
  :param seed: The seed for the random number generator
410
-
411
408
  return: The sequence of states and observations
412
409
  """
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
-
465
- class HMMProfiler:
466
- """
467
- The HMMProfiler class is used to profile a sequence of observations based on a Hidden Markov Model (HMM).
468
- """
469
-
470
- def __init__(self, statistics: List[EmulationStatistics], model_name: Union[str, None] = None) -> None:
471
- """
472
- Class constructor
473
-
474
- :param statistics: The list of EmulationStatistics objects
475
- :param model_name: The name of the model
476
- """
477
- self.statistics = statistics
478
- self.transition_matrix: List[List[float]] = []
479
- self.emission_matrix: List[List[float]] = []
480
- self.hidden_states: List[str] = []
481
- self.emission_matrix_observations: List[int] = []
482
- self.start_state_probs: List[float] = []
483
- self.model_name = None
484
-
485
- def create_model(self, transition_matrix: List[List[float]],
486
- hidden_states: List[str], metric: str,
487
- save_model: bool = False, location: str = ".") -> None:
488
- """
489
- Creates the HMM model based on the given transition matrix, states and metrics.
490
- If save = True, matrices are saved to given location
491
-
492
- :param transition_matrix: The transition matrix
493
- :param states: The list of states of the HMM (format: 'A:attack_name' or
494
- 'no_intrusion' based on emulation statistics file)
495
- :param metrics: The list of metrics to profile
496
- :param save: Whether to save the matrices to a file
497
- :param location: The location to save the matrices, if save = True, e.g "./resources",
498
- default is current directory
499
- """
500
- emission_matrix, emission_matrix_observations = self.get_matrices_of_observation(self.statistics,
501
- metric, hidden_states)
502
- self.emission_matrix = emission_matrix
503
- self.emission_matrix_observations = emission_matrix_observations
504
- self.transition_matrix = transition_matrix
505
- self.start_state_probs = self.calculate_initial_states(self.transition_matrix)
506
- self.hidden_states = hidden_states
507
- if save_model and location:
508
- np.save(f'{location}/transition_matrix.npy', transition_matrix)
509
- np.save(f'{location}/hidden_states.npy', hidden_states)
510
- np.save(f'{location}/start_state_probs.npy', self.start_state_probs)
511
- np.save(f'{location}/emission_matrix_{metric}.npy', emission_matrix)
512
- np.save(f'{location}/emission_matrix_observations_{metric}.npy', emission_matrix_observations)
513
-
514
- def load_model(self, location: str, metric: str) -> None:
515
- """
516
- Loads the HMM model from the given location.
517
-
518
- :param location: The location of the model files, default is current directory
519
- :param metric: The data metric to use
520
- """
521
- self.transition_matrix = np.load(f'{location}/transition_matrix.npy')
522
- self.hidden_states = np.load(f'{location}/hidden_states.npy')
523
- self.start_state_probs = np.load(f'{location}/start_state_probs.npy')
524
- self.emission_matrix = np.load(f'{location}/emission_matrix_{metric}.npy')
525
- self.emission_matrix_observations = np.load(f'{location}/emission_matrix_observations_{metric}.npy')
526
-
527
- def profile_sequence(self, sequence: List[int]) -> List[str]:
528
- """
529
- Profiles a sequence of observations based on the HMM model.
530
-
531
- :param sequence: The sequence of observations
532
- :return: The most likely sequence of states
533
- """
534
-
535
- path = HMMProfiler.viterbi(self.hidden_states, self.start_state_probs,
536
- self.transition_matrix, self.emission_matrix,
537
- sequence, self.emission_matrix_observations)
538
- profiled_sequence = []
539
- for i in range(len(path)):
540
- profiled_sequence.append(self.hidden_states[int(path[i])])
541
-
542
- return profiled_sequence
543
-
544
- def get_matrices_of_observation(self, statistics: List[EmulationStatistics],
545
- metric: str, states: List[str]) -> Tuple[List[List[float]], List[int]]:
546
- """
547
- Creates the emission matrix for a given metric based on the statistics from the EmulationStatistics objects.
548
-
549
- :param statistics: The list of EmulationStatistics objects
550
- :param metric: The metric to get the emission matrix for
551
- :return: The emission matrix, the list of observations, the list of states
552
- """
553
- emission_matrix = []
554
- attack_observations = {}
555
- attack_observations_total_counts = {}
556
- all_keys = set()
557
-
558
- for stats in statistics:
559
- for condition, metric_distribution in stats.conditionals_counts.items():
560
- action = condition.split('_')
561
- if action[0] == 'no':
562
- action[0] = 'no_intrusion'
563
- if action[0] not in attack_observations:
564
- # We are not intrested in the observations from 'intrusion' or 'A:Continue'
565
- if action[0] == 'intrusion' or action[0] == 'A:Continue':
566
- continue
567
- else:
568
- # Add the observations of the attack to the dictionary
569
- if metric in metric_distribution:
570
- attack_observations[action[0]] = metric_distribution[metric]
571
- # Sum the total counts of the observations
572
- attack_observations_total_counts[action[0]] = sum(attack_observations[action[0]].values())
573
- # Aggregate the counts from the metric distribution
574
- else:
575
- counts_observation = metric_distribution[metric]
576
- for element in counts_observation:
577
- if element in attack_observations[action[0]]:
578
- # Aggregate the counts if the element is already in the dictionary
579
- attack_observations[action[0]][element] += counts_observation[element]
580
- else:
581
- attack_observations[action[0]][element] = counts_observation[element]
582
- # Sum the total counts of the observations
583
- attack_observations_total_counts[action[0]] += sum(attack_observations[action[0]].values())
584
-
585
- # Store all possible values for the observation
586
- if action[0] in attack_observations:
587
- all_keys.update(attack_observations[action[0]])
588
-
589
- # Normalize the counts
590
- for attack, _ in attack_observations.items():
591
- attack_observations_total_counts[attack] = sum(attack_observations[attack].values())
592
- for key in all_keys:
593
- int_key = int(key)
594
- if key in attack_observations[attack]:
595
- count = attack_observations[attack].pop(key, 0)
596
- attack_observations[attack][int_key] = count / attack_observations_total_counts[attack]
597
- else:
598
- attack_observations[attack][int_key] = 0
599
- # Sort the dictionary by key
600
- attack_observations[attack] = dict(sorted(attack_observations[attack].items()))
601
-
602
- # Take any attack as the reference to get the keys
603
- emission_matrix_observations = []
604
- emission_matrix_states = []
605
- # Create the emission matrix
606
- for state in states:
607
- if state in attack_observations:
608
- # Normalize the and then append
609
- emission_matrix.append(list(attack_observations[state].values()))
610
- # Get the keys of all observations
611
- emission_matrix_observations = list(attack_observations[state].keys())
612
- emission_matrix_states.append(state)
613
- else:
614
- # LaPlace smoothing for missing observations
615
- num_keys = len(all_keys)
616
- laplace_probability = 1 / (num_keys + 2)
617
- laplace_sum = laplace_probability * num_keys
618
- laplace_probability_adj = laplace_probability / laplace_sum
619
- emission_matrix.append([laplace_probability_adj] * num_keys)
620
- emission_matrix_states.append(state)
621
-
622
- # Check if the sum of the probabilities is 1
623
- for i in range(len(emission_matrix)):
624
- sum_prob = round(sum(emission_matrix[i]), 10)
625
- if sum_prob != 1:
626
- print(f'Sum of probabilities for state {emission_matrix_states[i]} is {sum_prob}')
627
-
628
- return (emission_matrix, emission_matrix_observations)
629
-
630
- def convert_states_to_profiles(self, states: List[str]) -> List[Union[AttackProfiler, str]]:
631
- """
632
- Converts a list of states to a list of AttackProfiles.
633
-
634
- :param states: The list of states to convert
635
- :return: The list of EmulationAttackerActionId
636
- """
637
-
638
- new_states: List[Union[AttackProfiler, str]] = []
639
- for state in states:
640
- if state == 'A:Continue':
641
- action = EmulationAttackerAction(id=EmulationAttackerActionId.CONTINUE, name="Continue", cmds=[],
642
- type=EmulationAttackerActionType.CONTINUE, descr="CONTINUE", ips=[],
643
- index=0, action_outcome=EmulationAttackerActionOutcome.CONTINUE)
644
- p = AttackProfiler.get_attack_profile(action)
645
- new_states.append(p)
646
- elif state == 'A:CVE-2015-1427 exploit':
647
- action = EmulationAttackerAction(
648
- id=EmulationAttackerActionId.CVE_2015_1427_EXPLOIT, name="CVE-2015-1427 exploit", cmds=[],
649
- type=EmulationAttackerActionType.EXPLOIT,
650
- descr="Uses the CVE-2015-1427 vulnerability to "
651
- "get remote code execution and then sets up a SSH backdoor"
652
- "to upgrade the channel", index=-1, ips=[],
653
- action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
654
- p = AttackProfiler.get_attack_profile(action)
655
- new_states.append(p)
656
- elif state == 'A:DVWA SQL Injection Exploit':
657
- action = EmulationAttackerAction(
658
- id=EmulationAttackerActionId.DVWA_SQL_INJECTION, name="DVWA SQL Injection Exploit",
659
- cmds=[], type=EmulationAttackerActionType.EXPLOIT,
660
- descr="Uses the DVWA SQL Injection exploit to extract secret passwords",
661
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
662
- p = AttackProfiler.get_attack_profile(action)
663
- new_states.append(p)
664
- elif state == 'A:Install tools':
665
- action = EmulationAttackerAction(
666
- id=EmulationAttackerActionId.INSTALL_TOOLS, name="Install tools", cmds=[],
667
- type=EmulationAttackerActionType.POST_EXPLOIT,
668
- descr="If taken root on remote machine, installs pentest tools, e.g. nmap",
669
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.PIVOTING)
670
- p = AttackProfiler.get_attack_profile(action)
671
- new_states.append(p)
672
- elif state == 'A:Network service login':
673
- action = EmulationAttackerAction(
674
- id=EmulationAttackerActionId.NETWORK_SERVICE_LOGIN, name="Network service login",
675
- cmds=[], type=EmulationAttackerActionType.POST_EXPLOIT,
676
- descr="Uses known credentials to login to network services on a server",
677
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.LOGIN)
678
- p = AttackProfiler.get_attack_profile(action)
679
- new_states.append(p)
680
- elif state == 'A:Ping Scan':
681
- action = EmulationAttackerAction(
682
- id=EmulationAttackerActionId.PING_SCAN_HOST, name="Ping Scan",
683
- cmds=[], type=EmulationAttackerActionType.RECON,
684
- descr="A host discovery scan, it is quick because it only checks of hosts "
685
- "are up with Ping, without scanning the ports.", ips=[], index=-1,
686
- action_outcome=EmulationAttackerActionOutcome.INFORMATION_GATHERING, backdoor=False)
687
- p = AttackProfiler.get_attack_profile(action)
688
- new_states.append(p)
689
- elif state == 'A:Sambacry Explolit':
690
- action = EmulationAttackerAction(
691
- id=EmulationAttackerActionId.SAMBACRY_EXPLOIT, name="Sambacry Explolit", cmds=[],
692
- type=EmulationAttackerActionType.EXPLOIT,
693
- descr="Uses the sambacry shell to get remote code execution and then"
694
- "sets up a SSH backdoor to upgrade the channel",
695
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
696
- p = AttackProfiler.get_attack_profile(action)
697
- new_states.append(p)
698
- elif state == 'A:ShellShock Explolit':
699
- action = EmulationAttackerAction(
700
- id=EmulationAttackerActionId.SHELLSHOCK_EXPLOIT, name="ShellShock Exploit",
701
- cmds=[], type=EmulationAttackerActionType.EXPLOIT,
702
- descr="Uses the Shellshock exploit and curl to do remote code execution and create a backdoor",
703
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
704
- p = AttackProfiler.get_attack_profile(action)
705
- new_states.append(p)
706
- elif state == 'A:SSH dictionary attack for username=pw':
707
- action = EmulationAttackerAction(
708
- id=EmulationAttackerActionId.SSH_SAME_USER_PASS_DICTIONARY_HOST,
709
- name="SSH dictionary attack for username=pw", cmds=[],
710
- type=EmulationAttackerActionType.EXPLOIT, index=-1,
711
- descr="A dictionary attack that tries common passwords and usernames for SSH"
712
- "where username=password", ips=[], action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
713
- p = AttackProfiler.get_attack_profile(action)
714
- new_states.append(p)
715
- elif state == 'A:FTP dictionary attack for username=pw':
716
- action = EmulationAttackerAction(
717
- id=EmulationAttackerActionId.FTP_SAME_USER_PASS_DICTIONARY_HOST,
718
- name="FTP dictionary attack for username=pw", cmds=[], type=EmulationAttackerActionType.EXPLOIT,
719
- index=-1, descr="A dictionary attack that tries common passwords and"
720
- "usernames for FTP where username=password", ips=[],
721
- action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
722
- p = AttackProfiler.get_attack_profile(action)
723
- new_states.append(p)
724
- elif state == 'A:Telnet dictionary attack for username=pw':
725
- action = EmulationAttackerAction(
726
- id=EmulationAttackerActionId.TELNET_SAME_USER_PASS_DICTIONARY_HOST,
727
- name="Telnet dictionary attack for username=pw", cmds=[],
728
- type=EmulationAttackerActionType.EXPLOIT, index=-1,
729
- descr="A dictionary attack that tries common passwords and usernames for"
730
- "Telnet where username=password", ips=[],
731
- action_outcome=EmulationAttackerActionOutcome.SHELL_ACCESS)
732
- p = AttackProfiler.get_attack_profile(action)
733
- new_states.append(p)
734
- elif state == 'A:CVE-2010-0426 exploit':
735
- action = EmulationAttackerAction(
736
- id=EmulationAttackerActionId.CVE_2010_0426_PRIV_ESC,
737
- name="CVE-2010-0426 exploit", cmds=[], type=EmulationAttackerActionType.PRIVILEGE_ESCALATION,
738
- descr="Uses the CVE-2010-0426 vulnerability to perform privilege escalation to get root access",
739
- index=-1, ips=[], action_outcome=EmulationAttackerActionOutcome.PRIVILEGE_ESCALATION_ROOT)
740
- p = AttackProfiler.get_attack_profile(action)
741
- new_states.append(p)
742
- elif state == 'A:TCP SYN (Stealth) Scan':
743
- action = EmulationAttackerAction(
744
- id=EmulationAttackerActionId.TCP_SYN_STEALTH_SCAN_HOST, name="TCP SYN (Stealth) Scan",
745
- cmds=[], type=EmulationAttackerActionType.RECON,
746
- descr="A stealthy and fast TCP SYN scan to detect open TCP ports on the subnet", ips=[],
747
- index=-1, action_outcome=EmulationAttackerActionOutcome.INFORMATION_GATHERING, backdoor=False)
748
- p = AttackProfiler.get_attack_profile(action)
749
- new_states.append(p)
750
- elif state == 'ssh backdoor':
751
- action = EmulationAttackerAction(
752
- id=EmulationAttackerActionId.SSH_BACKDOOR, name="Install SSH backdoor",
753
- cmds=[], type=EmulationAttackerActionType.POST_EXPLOIT,
754
- descr="If taken root on remote machine, installs a ssh backdoor useful for"
755
- "upgrading telnetor weaker channels", index=-1, ips=[],
756
- action_outcome=EmulationAttackerActionOutcome.PIVOTING, alt_cmds=None, backdoor=True)
757
- p = AttackProfiler.get_attack_profile(action)
758
- new_states.append(p)
759
- else:
760
- new_states.append(state)
761
-
762
- return new_states
763
-
764
- def calculate_initial_states(self, transition_matrix: List[List[float]]) -> List[float]:
765
- """
766
- Calculates the initial states probabilities based on the transition matrix.
767
-
768
- 1 / (# of states)
769
-
770
- :param transition_matrix: The transition matrix
771
-
772
- :return: The start states probabilities
773
- """
774
- start_states = []
775
- total_states = len(transition_matrix)
776
- for _ in range(total_states):
777
- start_states.append(1 / total_states)
778
-
779
- return start_states
780
-
781
- @staticmethod
782
- def viterbi(hidden_states: List[EmulationAttackerActionId], init_probs: List[float],
783
- trans_matrix: List[List[float]], emission_matrix: List[List[float]],
784
- obs: List[int], emissions_list: List[int]) -> List[float]:
785
- """
786
- Viterbi algorithm for Hidden Markov Models (HMM).
787
-
788
- :param hidden_states: The hidden states
789
- :param init_probs: The initial probabilities of the hidden states
790
- :param trans_matrix: The transition matrix
791
- :param emission_matrix: The emission matrix
792
- :param obs: The observation sequence
793
- :param emissions_list: The list of possible observations
794
-
795
- :return: The most likely sequence of hidden states
796
- """
797
- # Convert the emissions list to a numpy array, to use the where function
798
- emissions_list_typed: np.ndarray[int, Any] = np.array(emissions_list)
799
-
800
- # Check that the sum equals 1
801
- for i in range(len(emission_matrix)):
802
- if round(sum(emission_matrix[i]), 10) != 1:
803
- print(f'Sum of probabilities for state {hidden_states[i]} is not 1')
804
- print(f'Sum of probabilities: {sum(emission_matrix[i])}')
805
-
806
- # The number of hidden states
807
- S = len(hidden_states)
808
- # The number of observations
809
- T = len(obs)
810
-
811
- # The Viterbi matrix (prob) T x S matrix of zeroes
812
- prob = np.zeros((T, S))
813
- # The backpointer matrix (prev)
814
- prev = np.empty((T, S))
815
- # Initialization
816
- for i in range(S):
817
- # Fetch the index of the observation in the emission_matrix
818
- index, = np.where(emissions_list_typed == obs[0])
819
- if index[0].size > 0:
820
- prob[0][i] = init_probs[i] * emission_matrix[i][index[0]]
821
- else:
822
- print(f'Observation {obs[0]} not found in the emission matrix')
823
- sys.exit(1)
824
-
825
- # Recursion
826
- for t in range(1, T):
827
- index, = np.where(emissions_list_typed == obs[t])
828
- for i in range(S):
829
- max_prob = -1
830
- max_state = -1
831
- for j in range(S):
832
- new_prob = prob[t - 1][j] * trans_matrix[j][i] * emission_matrix[i][index[0]]
833
- if new_prob > max_prob:
834
- max_prob = new_prob
835
- max_state = j
836
- prob[t][i] = max_prob
837
- prev[t][i] = max_state
838
-
839
- path = np.zeros(T)
840
- path[T - 1] = np.argmax(prob[T - 1])
841
- for t in range(T - 2, -1, -1):
842
- path[t] = prev[t + 1][int(path[t + 1])]
843
- # Convert the path to a list
844
- typed_path: List[float] = path.tolist()
845
-
846
- return typed_path
847
-
848
- def generate_sequence(self, intrusion_length: int, initial_state_index: int,
849
- seed: Union[int, None] = None) -> Tuple[List[str], List[int]]:
850
- """
851
- Generates a sequence of states and corresponding observations based on the given emission matrix,
852
- and transition matrix. First, a sequence of observation from 'no intrusion' is generated
853
- based on the geometric distribution of the initial state. Then, a sequence observations and states are
854
- generated based on emission matrix and transition matrix. The length of this intrusion
855
- sequence is given by the intrusion_length parameter.
856
410
 
857
- :param P_obs: The emission matrix
858
- :param P_states: The transition matrix
859
- :param states: The list of states
860
- :param observations: The list of observations
861
- :param intrusion_length: The length of the intrusion
862
- :param initial_state: The initial state
863
- return: The sequence of states and observations
864
- """
865
411
  P_obs = self.emission_matrix
866
412
  P_states = self.transition_matrix
867
413
  states = self.hidden_states
868
414
  observations = self.emission_matrix_observations
415
+
869
416
  if seed:
870
417
  np.random.seed(seed)
871
418
  obs_len = len(observations)
872
419
  states_len = len(states)
873
-
874
420
  # Return the geometric distribution of the initial state
875
421
  dist = np.random.geometric(p=P_states[initial_state_index][0], size=1000)
876
422
  T_i = round(sum(dist) / len(dist))
@@ -878,7 +424,6 @@ class HMMProfiler:
878
424
  state_seq = [states[initial_state_index]] * T_i
879
425
  obs_seq = []
880
426
  for i in range(T_i):
881
-
882
427
  o_i = np.random.choice(obs_len, p=P_obs[initial_state_index])
883
428
  obs_seq.append(observations[o_i])
884
429
 
@@ -893,12 +438,11 @@ class HMMProfiler:
893
438
  s_i = intrusion_start_state
894
439
  if intrusion_length == 1:
895
440
  return state_seq, obs_seq
896
- for i in range(intrusion_length):
441
+ for i in range(intrusion_length - 1):
897
442
  # si ~ Ps(si | si-1)
898
443
  s_i = np.random.choice(states_len, p=P_states[s_i])
899
444
  # oi ~ Po(oi | si)
900
445
  o_i = np.random.choice(obs_len, p=P_obs[s_i])
901
446
  state_seq.append(states[s_i])
902
447
  obs_seq.append(observations[o_i])
903
-
904
448
  return state_seq, obs_seq
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csle-attack-profiler
3
- Version: 0.6.0
3
+ Version: 0.6.2
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.6.0
19
- Requires-Dist: csle-common ==0.6.0
18
+ Requires-Dist: csle-base ==0.6.2
19
+ Requires-Dist: csle-common ==0.6.2
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=gMDttCkJF3h8Jr3eq_8NuJj26-9xyjg-YjR2bSNcGxM,22
3
+ csle_attack_profiler/attack_profiler.py,sha256=bYqSeItJkhswronvIRFPyvf1rtvUKEWPPF4K2t1JVqE,10535
4
+ csle_attack_profiler/hmm_profiling.py,sha256=cHE2wImgBJ3xwW33S0JcTwsiccstijR9uRMeBPXIT18,24167
5
+ csle_attack_profiler-0.6.2.dist-info/METADATA,sha256=94eqqi8UiSNnbBK1hE1tVBCwyNrG4WHZfm2_bh2Y_F0,2006
6
+ csle_attack_profiler-0.6.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
7
+ csle_attack_profiler-0.6.2.dist-info/top_level.txt,sha256=OuI4zvPo3MQYLQ7Dqm32oM0V8rNdwHe9tjtngx2KVtA,21
8
+ csle_attack_profiler-0.6.2.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=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,,