Skip to content

Commit 307a703

Browse files
authored
Merge pull request #7 from mal-lang/6-tutorial-simple-mal-language
Added simple language+model tutorial
2 parents ae55235 + a4f8df9 commit 307a703

File tree

9 files changed

+246
-0
lines changed

9 files changed

+246
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
tmp
22
venv
3+
.venv
34
logs
45

56
# Ignore files generated by scripts
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#id: "org.mal-lang.my-language"
2+
#version: "1.0.0"
3+
4+
category System {
5+
asset Computer {
6+
| connect
7+
-> access
8+
| crackPassword [HardAndCertain]
9+
-> access
10+
& access
11+
-> compromise
12+
| compromise
13+
-> folder.accessFolder
14+
-> toComputer.connect
15+
-> toComputer.crackPassword
16+
}
17+
asset Folder {
18+
| accessFolder
19+
-> stealSecrets
20+
| stealSecrets
21+
}
22+
}
23+
24+
associations {
25+
Computer [fromComputer] * <-- ComputerConnection --> * [toComputer] Computer
26+
Computer [computer] * <-- ComputerFolderConnection --> * [folder] Folder
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import os
2+
from maltoolbox.model import Model
3+
from maltoolbox.language import LanguageGraph
4+
from maltoolbox.attackgraph import AttackGraph
5+
from malsim import MalSimulator, run_simulation
6+
from malsim.agents import RandomAgent
7+
8+
9+
def create_model(lang_graph: LanguageGraph) -> Model:
10+
model = Model("my-model", lang_graph)
11+
12+
# Two computers
13+
comp_a = model.add_asset("Computer", "ComputerA")
14+
comp_b = model.add_asset("Computer", "ComputerB")
15+
16+
# Connection between computers
17+
comp_a.add_associated_assets("toComputer", [comp_b])
18+
19+
# Two folders
20+
folder1 = model.add_asset("Folder", "FolderA")
21+
folder2 = model.add_asset("Folder", "FolderB")
22+
23+
# Connect the folders to the computers
24+
comp_b.add_associated_assets("folder", [folder2])
25+
comp_a.add_associated_assets("folder", [folder1])
26+
27+
return model
28+
29+
30+
def main():
31+
lang_file = "my-language.mal"
32+
current_dir = os.path.dirname(os.path.abspath(__file__))
33+
lang_file_path = os.path.join(current_dir, lang_file)
34+
my_language = LanguageGraph.load_from_file(lang_file_path)
35+
36+
# Create our example model
37+
model = create_model(my_language)
38+
39+
graph = AttackGraph(my_language, model)
40+
41+
simulator = MalSimulator(graph)
42+
simulator.register_attacker(
43+
"MyAttacker", entry_points=["ComputerA:connect"], goals=["FolderB:stealSecrets"]
44+
)
45+
agents = [{"name": "MyAttacker", "agent": RandomAgent({})}]
46+
path = run_simulation(simulator, agents)
47+
48+
49+
if __name__ == "__main__":
50+
main()
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)