|
| 1 | +# Tutorial 2 - Create a language and a model |
| 2 | +In this tutorial, you will learn how to build a simple MAL language and create a model from it. |
| 3 | + |
| 4 | +## Step by step |
| 5 | +### Environment set-up |
| 6 | +Create a directory for the tutorial and set it as your working directory: `mkdir mal-tutorial2 && cd mal-tutorial2` |
| 7 | +Create a Python virtual environment and activate it. |
| 8 | +- On Linux-based operating systems: |
| 9 | +``` |
| 10 | +python -m venv .venv |
| 11 | +source .venv/bin/activate |
| 12 | +``` |
| 13 | +- On Windows: |
| 14 | +``` |
| 15 | +python -m venv .venv |
| 16 | +.\.venv\Scripts\activate |
| 17 | +``` |
| 18 | +Install the requirements: |
| 19 | +``` |
| 20 | +pip install mal-toolbox |
| 21 | +pip install mal-simulator |
| 22 | +``` |
| 23 | + |
| 24 | +### MAL language creation |
| 25 | +Create a MAL file in the same directory called `my-language.mal`. |
| 26 | + |
| 27 | +Copy this code into `my-language.mal`: |
| 28 | + |
| 29 | +``` |
| 30 | +#id: "org.mal-lang.my-language" |
| 31 | +#version: "1.0.0" |
| 32 | +
|
| 33 | +category System { |
| 34 | + asset Computer { |
| 35 | + | connect |
| 36 | + -> access |
| 37 | + | crackPassword [HardAndCertain] |
| 38 | + -> access |
| 39 | + & access |
| 40 | + -> compromise |
| 41 | + | compromise |
| 42 | + -> folder.accessFolder |
| 43 | + -> toComputer.connect |
| 44 | + -> toComputer.crackPassword |
| 45 | + } |
| 46 | + asset Folder { |
| 47 | + | accessFolder |
| 48 | + -> stealSecrets |
| 49 | + | stealSecrets |
| 50 | + } |
| 51 | +} |
| 52 | +
|
| 53 | +associations { |
| 54 | + Computer [fromComputer] * <-- ComputerConnection --> * [toComputer] Computer |
| 55 | + Computer [computer] * <-- ComputerFolderConnection --> * [folder] Folder |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +This piece of code defines a simple MALLang. We define a category called System that holds two assets: |
| 60 | +- Computer |
| 61 | + - If steps `connect` and `crackPassword` happen, then `access` would be triggered, which at the same time would trigger `compromise`. |
| 62 | + - If `compromise` is activated, we would move to the step `accessFolder` in asset `Folder`. |
| 63 | +- Folder |
| 64 | + - If `accessFolder` is given, it would trigger `stealSecrets`. |
| 65 | + |
| 66 | +In the `associations` section we define the relationship assets have. In this case, `Computer` and `Folder` have an N to M relationship, represented by the `*`. |
| 67 | + |
| 68 | +Once we have the MALLang file, we can create a python file to create models from this language. |
| 69 | + |
| 70 | +### Create and compile the model |
| 71 | +Create a .py file in the same directory called `my-model.py`. |
| 72 | + |
| 73 | +Copy this code into `my-model.py`: |
| 74 | + |
| 75 | +```python |
| 76 | +import os |
| 77 | +from maltoolbox.model import Model |
| 78 | +from maltoolbox.language import LanguageGraph |
| 79 | + |
| 80 | + |
| 81 | +def create_model(lang_graph: LanguageGraph) -> Model: |
| 82 | + """Create a model with 2 computers""" |
| 83 | + model = Model("my-model", lang_graph) |
| 84 | + |
| 85 | + # Two computers |
| 86 | + comp_a = model.add_asset("Computer", "ComputerA") |
| 87 | + comp_b = model.add_asset("Computer", "ComputerB") |
| 88 | + |
| 89 | + # Connection between computers |
| 90 | + comp_a.add_associated_assets("toComputer", [comp_b]) |
| 91 | + |
| 92 | + # Two folders |
| 93 | + folder1 = model.add_asset("Folder", "FolderA") |
| 94 | + folder2 = model.add_asset("Folder", "FolderB") |
| 95 | + |
| 96 | + # Connect the folders to the computers |
| 97 | + comp_b.add_associated_assets("folder", [folder2]) |
| 98 | + comp_a.add_associated_assets("folder", [folder1]) |
| 99 | + |
| 100 | + return model |
| 101 | +``` |
| 102 | +In this simple function, we create: |
| 103 | +- Two instances of our `Computer` asset (`ComputerA` and `ComputerB`) and our `Folder` asset (`FolderA` and `FolderB`). |
| 104 | +- A connection between `ComputerA` and `ComputerB`. The string `"computer2"` comes from the `ComputerConnection` association in the MAL language we created. |
| 105 | +- Two connections between the computer and folder instances. The strings `"folder"` come from the `ComputerFolderConnection` association. |
| 106 | + |
| 107 | +Now we can instantiate the model and compile. Add this to the end of the file: |
| 108 | + |
| 109 | +```python |
| 110 | +def main(): |
| 111 | + lang_file = "my-language.mal" |
| 112 | + current_dir = os.path.dirname(os.path.abspath(__file__)) |
| 113 | + lang_file_path = os.path.join(current_dir, lang_file) |
| 114 | + my_language = LanguageGraph.load_from_file(lang_file_path) |
| 115 | + |
| 116 | + # Create our example model |
| 117 | + model = create_model(my_language) |
| 118 | + |
| 119 | + |
| 120 | +if __name__ == "__main__": |
| 121 | + main() |
| 122 | +``` |
| 123 | +Change `lang_file` accordingly. Here we create the model using our own language. To compile it, run the script with `python my-model.py`. |
| 124 | + |
| 125 | +### Create an attack graph |
| 126 | +To create an attack graph, we use the model and the MALLang. Add this import to the top of the file (below the other imports): |
| 127 | + |
| 128 | +```python |
| 129 | +from maltoolbox.attackgraph import AttackGraph |
| 130 | +``` |
| 131 | + |
| 132 | +Put this line after `model = create_model(tyr_lang)`: |
| 133 | + |
| 134 | +```python |
| 135 | +# Generate an attack graph from the model |
| 136 | +graph = AttackGraph(my_language, model) |
| 137 | +``` |
| 138 | + |
| 139 | +The attack graph is a representation of the model that folds out all of the attack steps defined in the MAL language. This can be used to run analysis or simulations. |
| 140 | + |
| 141 | +### Run simulation |
| 142 | +To run simulations, add these imports to the top of the file (below the other imports): |
| 143 | + |
| 144 | +```python |
| 145 | +from malsim import MalSimulator, run_simulation |
| 146 | +from malsim.agents import RandomAgent |
| 147 | +``` |
| 148 | + |
| 149 | +Now we can create a MalSimulator from the attack graph `graph` and run simulations. |
| 150 | + |
| 151 | +Add this to the end of the `main` function: |
| 152 | + |
| 153 | +```python |
| 154 | +simulator = MalSimulator(graph) |
| 155 | +agents = {} |
| 156 | +path = run_simulation(simulator, agents) |
| 157 | +``` |
| 158 | + |
| 159 | +When we run `python my-model.py` we will just see "Simulation over after 0 steps.". This is because we don't have any agents. Let us add an attacker agent. |
| 160 | + |
| 161 | +Replace the line `agents` with: |
| 162 | + |
| 163 | +```python |
| 164 | +simulator.register_attacker("MyAttacker", entry_points=["ComputerA:connect"], goals=["FolderB:stealSecrets"]) |
| 165 | +agents = [{'name': "MyAttacker", "agent": RandomAgent({})}] |
| 166 | +``` |
| 167 | + |
| 168 | +This registers an attacker in the simulator, and creates a list of decision agents that can be used to take decisions for the simulator attacker. |
0 commit comments