emfuzzer 0.1.0__tar.gz
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.
- emfuzzer-0.1.0/LICENSE.txt +19 -0
- emfuzzer-0.1.0/PKG-INFO +270 -0
- emfuzzer-0.1.0/README.md +245 -0
- emfuzzer-0.1.0/emfuzzer/__init__.py +62 -0
- emfuzzer-0.1.0/emfuzzer/__main__.py +92 -0
- emfuzzer-0.1.0/emfuzzer/arguments.py +16 -0
- emfuzzer-0.1.0/emfuzzer/case.py +43 -0
- emfuzzer-0.1.0/emfuzzer/coap/__init__.py +109 -0
- emfuzzer-0.1.0/emfuzzer/coap/code.py +98 -0
- emfuzzer-0.1.0/emfuzzer/coap/validator.py +91 -0
- emfuzzer-0.1.0/emfuzzer/config.py +86 -0
- emfuzzer-0.1.0/emfuzzer/context.py +74 -0
- emfuzzer-0.1.0/emfuzzer/delay.py +38 -0
- emfuzzer-0.1.0/emfuzzer/injector/__init__.py +50 -0
- emfuzzer-0.1.0/emfuzzer/injector/subprocess.py +62 -0
- emfuzzer-0.1.0/emfuzzer/injector/subtask.py +30 -0
- emfuzzer-0.1.0/emfuzzer/io/__init__.py +227 -0
- emfuzzer-0.1.0/emfuzzer/io/net.py +31 -0
- emfuzzer-0.1.0/emfuzzer/io/sockets.py +71 -0
- emfuzzer-0.1.0/emfuzzer/io/streams.py +91 -0
- emfuzzer-0.1.0/emfuzzer/results/__init__.py +109 -0
- emfuzzer-0.1.0/emfuzzer/results/basic.py +17 -0
- emfuzzer-0.1.0/emfuzzer/ssh/__init__.py +10 -0
- emfuzzer-0.1.0/emfuzzer/ssh/connectionconfig.py +29 -0
- emfuzzer-0.1.0/emfuzzer/ssh/invoker.py +139 -0
- emfuzzer-0.1.0/emfuzzer/ssh/reader.py +93 -0
- emfuzzer-0.1.0/emfuzzer/subtasks/__init__.py +130 -0
- emfuzzer-0.1.0/emfuzzer/subtasks/ping.py +149 -0
- emfuzzer-0.1.0/emfuzzer/subtasks/remote.py +76 -0
- emfuzzer-0.1.0/emfuzzer/subtasks/subprocess.py +152 -0
- emfuzzer-0.1.0/emfuzzer/subtasks/subtask.py +63 -0
- emfuzzer-0.1.0/emfuzzer/version.py +48 -0
- emfuzzer-0.1.0/pyproject.toml +123 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2025 Warsaw University of Technology
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
emfuzzer-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: emfuzzer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Experiments runner for embedded environments
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: embedded,fuzzing,testing,experiments
|
|
7
|
+
Author: Konrad Grochowski
|
|
8
|
+
Author-email: Konrad.Grochowski@pw.edu.pl
|
|
9
|
+
Requires-Python: >= 3.13
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Dist: paramiko (==3.5.1)
|
|
21
|
+
Project-URL: ChangeLog, https://github.com/ZBOSK-II/emfuzzer/blob/master/CHANGELOG.md
|
|
22
|
+
Project-URL: GitHub, https://github.com/ZBOSK-II/emfuzzer
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
Emfuzzer - fuzzing experiments orchestrator for embedded
|
|
26
|
+
============================================================
|
|
27
|
+
|
|
28
|
+
When executing fuzzing experiment on embedded environment
|
|
29
|
+
one often faces challenge of performing multiple tasks in
|
|
30
|
+
reapetable and observable manner, for example: reset board,
|
|
31
|
+
ensure embedded software booted, send fuzzing data using
|
|
32
|
+
selected link, monitor peripheral state to detect changes
|
|
33
|
+
in behaviour etc.
|
|
34
|
+
|
|
35
|
+
This is what Emfuzzer helps to orchestrate: it runs various
|
|
36
|
+
tools and scripts in specific manner, then gathers their
|
|
37
|
+
results for further inspections.
|
|
38
|
+
|
|
39
|
+
_Note_: although focused on fuzzing and embedded systems,
|
|
40
|
+
Emfuzzer can help with any software-related experiments,
|
|
41
|
+
that require repeatable order of tasks and results capture.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Installation
|
|
45
|
+
------------------------------------------------------------
|
|
46
|
+
Emfuzzer is available on PyPI, it is recommended to install
|
|
47
|
+
it in isolated environment, either by using Python `venv` or
|
|
48
|
+
tools like `pipx`.
|
|
49
|
+
|
|
50
|
+
``` shell
|
|
51
|
+
python -m venv .venv
|
|
52
|
+
source .venv/bin/activate
|
|
53
|
+
pip install emfuzzer
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
``` shell
|
|
57
|
+
pipx install emfuzzer
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Usage
|
|
61
|
+
------------------------------------------------------------
|
|
62
|
+
To run experiments simply run:
|
|
63
|
+
|
|
64
|
+
``` shell
|
|
65
|
+
emfuzzer --config=experiment.json test1.bin test2.bin
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For each specified data file steps from `experiment.json`
|
|
69
|
+
will be executed and gathered results stored in file named
|
|
70
|
+
`emfuzzer-CURRENTDATE.json`. Application will output logs
|
|
71
|
+
to the console and also store them in `.log` file next to
|
|
72
|
+
the `.json` results file. The prefix for output files can
|
|
73
|
+
be modified using `--output-prefix` command line switch.
|
|
74
|
+
|
|
75
|
+
See `default-config.json` in source directory for example
|
|
76
|
+
of experiment definition (this file can be safely used -
|
|
77
|
+
the "experiment" calls `cat` on each passed file).
|
|
78
|
+
|
|
79
|
+
To obtain complete command line switches documentation call
|
|
80
|
+
`emfuzzer --help`.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
Experiment
|
|
84
|
+
------------------------------------------------------------
|
|
85
|
+
Each data file passed to the emfuzzer represents a single
|
|
86
|
+
Test Case. For each test case following experiment steps
|
|
87
|
+
will be performed:
|
|
88
|
+
|
|
89
|
+
1. Setup tasks will be executed and their results stored.
|
|
90
|
+
2. Monitoring tasks will be started.
|
|
91
|
+
3. Injector will use Test Case data to perform the main
|
|
92
|
+
experiment task (e.g. fuzzing data injection).
|
|
93
|
+
4. Injector will observe system behaviour to capture
|
|
94
|
+
result of the injection.
|
|
95
|
+
5. Monitoring tasks will finish, their results stored.
|
|
96
|
+
6. Check tasks will be executed and their results stored.
|
|
97
|
+
7. Go to 1 for next Test Case.
|
|
98
|
+
|
|
99
|
+
Configuration
|
|
100
|
+
------------------------------------------------------------
|
|
101
|
+
Experiment configuration is stored in JSON format.
|
|
102
|
+
|
|
103
|
+
See chapters below for list of all types of injectors, tasks
|
|
104
|
+
and monitors (with arguments).
|
|
105
|
+
|
|
106
|
+
Below is the `default-config.json` with comments:
|
|
107
|
+
``` json-with-comments
|
|
108
|
+
{
|
|
109
|
+
"delays": {
|
|
110
|
+
"between_cases": 0.2, // delay between Test Cases
|
|
111
|
+
"before_inject": 1 // delay after all setups
|
|
112
|
+
},
|
|
113
|
+
"injector": {
|
|
114
|
+
"type": "subprocess", // type of injection
|
|
115
|
+
"args": { // arguments for given injector
|
|
116
|
+
"cmd": [
|
|
117
|
+
"cat"
|
|
118
|
+
],
|
|
119
|
+
"shell": false,
|
|
120
|
+
"timeout": 1
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"case": { // for each case
|
|
124
|
+
"setups": [ // list of setups
|
|
125
|
+
{
|
|
126
|
+
"type": "subprocess", // type of setup tasks
|
|
127
|
+
"name": "setup", // name used in results
|
|
128
|
+
"args": { // arguments for given setup
|
|
129
|
+
"cmd": [
|
|
130
|
+
"echo",
|
|
131
|
+
"SETUP"
|
|
132
|
+
],
|
|
133
|
+
"finish": {
|
|
134
|
+
"timeout": 0.5,
|
|
135
|
+
"signal": "NONE"
|
|
136
|
+
},
|
|
137
|
+
"shell": false
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{ // second setup
|
|
141
|
+
"type": "ping_alive",
|
|
142
|
+
"name": "ping",
|
|
143
|
+
"args": {
|
|
144
|
+
"host": "127.0.0.1",
|
|
145
|
+
"timeout": 10,
|
|
146
|
+
"interval": 1
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"monitoring": [], // monitoring tasks
|
|
151
|
+
"checks": [ // list of checks tasks
|
|
152
|
+
{ // same as setups
|
|
153
|
+
"type": "ping_stable",
|
|
154
|
+
"name": "ping",
|
|
155
|
+
"args": {
|
|
156
|
+
"host": "127.0.0.1",
|
|
157
|
+
"count": 2,
|
|
158
|
+
"interval": 1
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"type": "subprocess",
|
|
163
|
+
"name": "teardown",
|
|
164
|
+
"args": {
|
|
165
|
+
"cmd": [
|
|
166
|
+
"echo",
|
|
167
|
+
"TEARDOWN"
|
|
168
|
+
],
|
|
169
|
+
"finish": {
|
|
170
|
+
"timeout": 0.5,
|
|
171
|
+
"signal": "NONE"
|
|
172
|
+
},
|
|
173
|
+
"shell": false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
Injectors
|
|
183
|
+
------------------------------------------------------------
|
|
184
|
+
Injectors are the tools that take the experiment data,
|
|
185
|
+
use it to "inject" it into the system, and then observe the
|
|
186
|
+
effects. Currently supported injectors' types:
|
|
187
|
+
|
|
188
|
+
* `subprocess` - execute script and captures its exit code.
|
|
189
|
+
Arguments:
|
|
190
|
+
- `cmd` - (list of strings) command to be executed, data
|
|
191
|
+
will be passed as the last argument to the call
|
|
192
|
+
- `shell` - (boolean) true when shell should be used todo
|
|
193
|
+
interpret the command
|
|
194
|
+
- `timeout` - (float) time to wait for command to finish
|
|
195
|
+
* `coap` - CoAP (Constrained Application Protocol) injector
|
|
196
|
+
data will be sent over UDP to specified address, success
|
|
197
|
+
when positive response is received from the system.
|
|
198
|
+
Arguments:
|
|
199
|
+
- `target` - dictionary containing `host` and `port` of
|
|
200
|
+
the target
|
|
201
|
+
- `response_timeout` - (float) timeout to wait for CoAP
|
|
202
|
+
response after sending the data
|
|
203
|
+
- `observation_timeout` - (float) additional time for
|
|
204
|
+
detecting any unexpected messages _after_ the response
|
|
205
|
+
|
|
206
|
+
SubTasks & Monitors
|
|
207
|
+
------------------------------------------------------------
|
|
208
|
+
Sub Tasks are tasks that for each case can be executed as
|
|
209
|
+
setups or checks.
|
|
210
|
+
|
|
211
|
+
_Note_: failure of "setup" _does not_ interrupt the test
|
|
212
|
+
case execution - it is logged and stored in results, next
|
|
213
|
+
steps are still executed, to be analyzed later.
|
|
214
|
+
|
|
215
|
+
Monitors are tasks that their execution is started after
|
|
216
|
+
setups, then they are active during the injection and
|
|
217
|
+
finish before checks.
|
|
218
|
+
|
|
219
|
+
Available tasks:
|
|
220
|
+
* `subprocess` - execute script and capture its exit code.
|
|
221
|
+
Arguments:
|
|
222
|
+
- `cmd` - (list of strings) command to be executed
|
|
223
|
+
- `shell` - (boolean) true when shell should be used todo
|
|
224
|
+
interpret the command
|
|
225
|
+
- `finish` - configuration of finishing the task:
|
|
226
|
+
- `signal` - (string) signal name to be sent to the
|
|
227
|
+
task (can be `NONE`)
|
|
228
|
+
- `timeout` - (float) time to wait for command to
|
|
229
|
+
finish (starts after signal is sent)
|
|
230
|
+
* `ping_stable` - pings a target number of times, expects
|
|
231
|
+
all pings to reply.
|
|
232
|
+
Arguments:
|
|
233
|
+
- `host` - (string) host to be checked
|
|
234
|
+
- `count` - (integer) number of pings to sent
|
|
235
|
+
- `interval` - (integer) interval between pings
|
|
236
|
+
* `ping_alive` - pings a target and expects first response
|
|
237
|
+
Arguments:
|
|
238
|
+
- `host` - (string) host to be checked
|
|
239
|
+
- `timeout` - (float) timeout to wait for response
|
|
240
|
+
- `interval` - (integer) interval between pings
|
|
241
|
+
* `remote` - executes command over SSH and captures its
|
|
242
|
+
exit code.
|
|
243
|
+
Arguments:
|
|
244
|
+
- `connection` - dictionary containing:
|
|
245
|
+
- `host`
|
|
246
|
+
- `port`
|
|
247
|
+
- `username`
|
|
248
|
+
- `password`
|
|
249
|
+
- `command` - (string) command to be executed
|
|
250
|
+
- `start_key` - (string) string expected in the output
|
|
251
|
+
of the executed command for the command to be considered
|
|
252
|
+
"started successfully"
|
|
253
|
+
- `start_timeout` - (float) timeout for the start of
|
|
254
|
+
the command
|
|
255
|
+
- `finish` - configuration of finishing the task:
|
|
256
|
+
- `signal` - (string) signal name to be sent to the
|
|
257
|
+
task (can be `NONE`)
|
|
258
|
+
- `timeout` - (float) time to wait for command to
|
|
259
|
+
finish (starts after signal is sent)
|
|
260
|
+
* `coap_monitor` - listens for CoAP responses
|
|
261
|
+
Arguments:
|
|
262
|
+
- `target` - dictionary containing `host` and `port` of
|
|
263
|
+
the target
|
|
264
|
+
- `response_timeout` - (float) timeout to wait for CoAP
|
|
265
|
+
response after sending any data
|
|
266
|
+
(useful only when used as Injector)
|
|
267
|
+
- `observation_timeout` - (float) additional time for
|
|
268
|
+
detecting any unexpected messages when monitoring
|
|
269
|
+
finishes
|
|
270
|
+
|
emfuzzer-0.1.0/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
Emfuzzer - fuzzing experiments orchestrator for embedded
|
|
2
|
+
============================================================
|
|
3
|
+
|
|
4
|
+
When executing fuzzing experiment on embedded environment
|
|
5
|
+
one often faces challenge of performing multiple tasks in
|
|
6
|
+
reapetable and observable manner, for example: reset board,
|
|
7
|
+
ensure embedded software booted, send fuzzing data using
|
|
8
|
+
selected link, monitor peripheral state to detect changes
|
|
9
|
+
in behaviour etc.
|
|
10
|
+
|
|
11
|
+
This is what Emfuzzer helps to orchestrate: it runs various
|
|
12
|
+
tools and scripts in specific manner, then gathers their
|
|
13
|
+
results for further inspections.
|
|
14
|
+
|
|
15
|
+
_Note_: although focused on fuzzing and embedded systems,
|
|
16
|
+
Emfuzzer can help with any software-related experiments,
|
|
17
|
+
that require repeatable order of tasks and results capture.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Installation
|
|
21
|
+
------------------------------------------------------------
|
|
22
|
+
Emfuzzer is available on PyPI, it is recommended to install
|
|
23
|
+
it in isolated environment, either by using Python `venv` or
|
|
24
|
+
tools like `pipx`.
|
|
25
|
+
|
|
26
|
+
``` shell
|
|
27
|
+
python -m venv .venv
|
|
28
|
+
source .venv/bin/activate
|
|
29
|
+
pip install emfuzzer
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
``` shell
|
|
33
|
+
pipx install emfuzzer
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Usage
|
|
37
|
+
------------------------------------------------------------
|
|
38
|
+
To run experiments simply run:
|
|
39
|
+
|
|
40
|
+
``` shell
|
|
41
|
+
emfuzzer --config=experiment.json test1.bin test2.bin
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For each specified data file steps from `experiment.json`
|
|
45
|
+
will be executed and gathered results stored in file named
|
|
46
|
+
`emfuzzer-CURRENTDATE.json`. Application will output logs
|
|
47
|
+
to the console and also store them in `.log` file next to
|
|
48
|
+
the `.json` results file. The prefix for output files can
|
|
49
|
+
be modified using `--output-prefix` command line switch.
|
|
50
|
+
|
|
51
|
+
See `default-config.json` in source directory for example
|
|
52
|
+
of experiment definition (this file can be safely used -
|
|
53
|
+
the "experiment" calls `cat` on each passed file).
|
|
54
|
+
|
|
55
|
+
To obtain complete command line switches documentation call
|
|
56
|
+
`emfuzzer --help`.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
Experiment
|
|
60
|
+
------------------------------------------------------------
|
|
61
|
+
Each data file passed to the emfuzzer represents a single
|
|
62
|
+
Test Case. For each test case following experiment steps
|
|
63
|
+
will be performed:
|
|
64
|
+
|
|
65
|
+
1. Setup tasks will be executed and their results stored.
|
|
66
|
+
2. Monitoring tasks will be started.
|
|
67
|
+
3. Injector will use Test Case data to perform the main
|
|
68
|
+
experiment task (e.g. fuzzing data injection).
|
|
69
|
+
4. Injector will observe system behaviour to capture
|
|
70
|
+
result of the injection.
|
|
71
|
+
5. Monitoring tasks will finish, their results stored.
|
|
72
|
+
6. Check tasks will be executed and their results stored.
|
|
73
|
+
7. Go to 1 for next Test Case.
|
|
74
|
+
|
|
75
|
+
Configuration
|
|
76
|
+
------------------------------------------------------------
|
|
77
|
+
Experiment configuration is stored in JSON format.
|
|
78
|
+
|
|
79
|
+
See chapters below for list of all types of injectors, tasks
|
|
80
|
+
and monitors (with arguments).
|
|
81
|
+
|
|
82
|
+
Below is the `default-config.json` with comments:
|
|
83
|
+
``` json-with-comments
|
|
84
|
+
{
|
|
85
|
+
"delays": {
|
|
86
|
+
"between_cases": 0.2, // delay between Test Cases
|
|
87
|
+
"before_inject": 1 // delay after all setups
|
|
88
|
+
},
|
|
89
|
+
"injector": {
|
|
90
|
+
"type": "subprocess", // type of injection
|
|
91
|
+
"args": { // arguments for given injector
|
|
92
|
+
"cmd": [
|
|
93
|
+
"cat"
|
|
94
|
+
],
|
|
95
|
+
"shell": false,
|
|
96
|
+
"timeout": 1
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"case": { // for each case
|
|
100
|
+
"setups": [ // list of setups
|
|
101
|
+
{
|
|
102
|
+
"type": "subprocess", // type of setup tasks
|
|
103
|
+
"name": "setup", // name used in results
|
|
104
|
+
"args": { // arguments for given setup
|
|
105
|
+
"cmd": [
|
|
106
|
+
"echo",
|
|
107
|
+
"SETUP"
|
|
108
|
+
],
|
|
109
|
+
"finish": {
|
|
110
|
+
"timeout": 0.5,
|
|
111
|
+
"signal": "NONE"
|
|
112
|
+
},
|
|
113
|
+
"shell": false
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{ // second setup
|
|
117
|
+
"type": "ping_alive",
|
|
118
|
+
"name": "ping",
|
|
119
|
+
"args": {
|
|
120
|
+
"host": "127.0.0.1",
|
|
121
|
+
"timeout": 10,
|
|
122
|
+
"interval": 1
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"monitoring": [], // monitoring tasks
|
|
127
|
+
"checks": [ // list of checks tasks
|
|
128
|
+
{ // same as setups
|
|
129
|
+
"type": "ping_stable",
|
|
130
|
+
"name": "ping",
|
|
131
|
+
"args": {
|
|
132
|
+
"host": "127.0.0.1",
|
|
133
|
+
"count": 2,
|
|
134
|
+
"interval": 1
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"type": "subprocess",
|
|
139
|
+
"name": "teardown",
|
|
140
|
+
"args": {
|
|
141
|
+
"cmd": [
|
|
142
|
+
"echo",
|
|
143
|
+
"TEARDOWN"
|
|
144
|
+
],
|
|
145
|
+
"finish": {
|
|
146
|
+
"timeout": 0.5,
|
|
147
|
+
"signal": "NONE"
|
|
148
|
+
},
|
|
149
|
+
"shell": false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
Injectors
|
|
159
|
+
------------------------------------------------------------
|
|
160
|
+
Injectors are the tools that take the experiment data,
|
|
161
|
+
use it to "inject" it into the system, and then observe the
|
|
162
|
+
effects. Currently supported injectors' types:
|
|
163
|
+
|
|
164
|
+
* `subprocess` - execute script and captures its exit code.
|
|
165
|
+
Arguments:
|
|
166
|
+
- `cmd` - (list of strings) command to be executed, data
|
|
167
|
+
will be passed as the last argument to the call
|
|
168
|
+
- `shell` - (boolean) true when shell should be used todo
|
|
169
|
+
interpret the command
|
|
170
|
+
- `timeout` - (float) time to wait for command to finish
|
|
171
|
+
* `coap` - CoAP (Constrained Application Protocol) injector
|
|
172
|
+
data will be sent over UDP to specified address, success
|
|
173
|
+
when positive response is received from the system.
|
|
174
|
+
Arguments:
|
|
175
|
+
- `target` - dictionary containing `host` and `port` of
|
|
176
|
+
the target
|
|
177
|
+
- `response_timeout` - (float) timeout to wait for CoAP
|
|
178
|
+
response after sending the data
|
|
179
|
+
- `observation_timeout` - (float) additional time for
|
|
180
|
+
detecting any unexpected messages _after_ the response
|
|
181
|
+
|
|
182
|
+
SubTasks & Monitors
|
|
183
|
+
------------------------------------------------------------
|
|
184
|
+
Sub Tasks are tasks that for each case can be executed as
|
|
185
|
+
setups or checks.
|
|
186
|
+
|
|
187
|
+
_Note_: failure of "setup" _does not_ interrupt the test
|
|
188
|
+
case execution - it is logged and stored in results, next
|
|
189
|
+
steps are still executed, to be analyzed later.
|
|
190
|
+
|
|
191
|
+
Monitors are tasks that their execution is started after
|
|
192
|
+
setups, then they are active during the injection and
|
|
193
|
+
finish before checks.
|
|
194
|
+
|
|
195
|
+
Available tasks:
|
|
196
|
+
* `subprocess` - execute script and capture its exit code.
|
|
197
|
+
Arguments:
|
|
198
|
+
- `cmd` - (list of strings) command to be executed
|
|
199
|
+
- `shell` - (boolean) true when shell should be used todo
|
|
200
|
+
interpret the command
|
|
201
|
+
- `finish` - configuration of finishing the task:
|
|
202
|
+
- `signal` - (string) signal name to be sent to the
|
|
203
|
+
task (can be `NONE`)
|
|
204
|
+
- `timeout` - (float) time to wait for command to
|
|
205
|
+
finish (starts after signal is sent)
|
|
206
|
+
* `ping_stable` - pings a target number of times, expects
|
|
207
|
+
all pings to reply.
|
|
208
|
+
Arguments:
|
|
209
|
+
- `host` - (string) host to be checked
|
|
210
|
+
- `count` - (integer) number of pings to sent
|
|
211
|
+
- `interval` - (integer) interval between pings
|
|
212
|
+
* `ping_alive` - pings a target and expects first response
|
|
213
|
+
Arguments:
|
|
214
|
+
- `host` - (string) host to be checked
|
|
215
|
+
- `timeout` - (float) timeout to wait for response
|
|
216
|
+
- `interval` - (integer) interval between pings
|
|
217
|
+
* `remote` - executes command over SSH and captures its
|
|
218
|
+
exit code.
|
|
219
|
+
Arguments:
|
|
220
|
+
- `connection` - dictionary containing:
|
|
221
|
+
- `host`
|
|
222
|
+
- `port`
|
|
223
|
+
- `username`
|
|
224
|
+
- `password`
|
|
225
|
+
- `command` - (string) command to be executed
|
|
226
|
+
- `start_key` - (string) string expected in the output
|
|
227
|
+
of the executed command for the command to be considered
|
|
228
|
+
"started successfully"
|
|
229
|
+
- `start_timeout` - (float) timeout for the start of
|
|
230
|
+
the command
|
|
231
|
+
- `finish` - configuration of finishing the task:
|
|
232
|
+
- `signal` - (string) signal name to be sent to the
|
|
233
|
+
task (can be `NONE`)
|
|
234
|
+
- `timeout` - (float) time to wait for command to
|
|
235
|
+
finish (starts after signal is sent)
|
|
236
|
+
* `coap_monitor` - listens for CoAP responses
|
|
237
|
+
Arguments:
|
|
238
|
+
- `target` - dictionary containing `host` and `port` of
|
|
239
|
+
the target
|
|
240
|
+
- `response_timeout` - (float) timeout to wait for CoAP
|
|
241
|
+
response after sending any data
|
|
242
|
+
(useful only when used as Injector)
|
|
243
|
+
- `observation_timeout` - (float) additional time for
|
|
244
|
+
detecting any unexpected messages when monitoring
|
|
245
|
+
finishes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright (c) 2025 Warsaw University of Technology
|
|
2
|
+
# This file is licensed under the MIT License.
|
|
3
|
+
# See the LICENSE.txt file in the root of the repository for full details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Main module of the application.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from .arguments import Arguments
|
|
13
|
+
from .case import Case
|
|
14
|
+
from .config import Config
|
|
15
|
+
from .context import Context
|
|
16
|
+
from .delay import Delay
|
|
17
|
+
from .injector import Injector
|
|
18
|
+
from .results import Results
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def run(args: Arguments, config: Config) -> int:
|
|
24
|
+
results = Results(config)
|
|
25
|
+
|
|
26
|
+
with Context(config) as context:
|
|
27
|
+
case = Case.from_config(context=context, results=results)
|
|
28
|
+
|
|
29
|
+
injector = Injector.from_config(results=results, context=context)
|
|
30
|
+
|
|
31
|
+
delay_between_cases = Delay.from_config(
|
|
32
|
+
"delays", "between_cases", config=config
|
|
33
|
+
)
|
|
34
|
+
delay_before_inject = Delay.from_config(
|
|
35
|
+
"delays", "before_inject", config=config
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for path in args.data:
|
|
39
|
+
logger.info(f"Opening {path}")
|
|
40
|
+
with path.open("rb") as file:
|
|
41
|
+
data = file.read()
|
|
42
|
+
if len(data) == 0:
|
|
43
|
+
logger.warning(f"No data found, skipping {path}")
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
case_name = str(path)
|
|
47
|
+
results.add_key(case_name)
|
|
48
|
+
|
|
49
|
+
with case.execute(case_name):
|
|
50
|
+
delay_before_inject.wait()
|
|
51
|
+
injector.inject(case_name, data)
|
|
52
|
+
|
|
53
|
+
delay_between_cases.wait()
|
|
54
|
+
|
|
55
|
+
results.finish()
|
|
56
|
+
logger.info(f"Results:\n {results.summary()}")
|
|
57
|
+
|
|
58
|
+
with open(args.output_prefix + ".json", "w", encoding="utf-8") as f:
|
|
59
|
+
json.dump(results.to_dict(), f, indent=2)
|
|
60
|
+
f.write("\n")
|
|
61
|
+
|
|
62
|
+
return results.total_errors()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (c) 2025 Warsaw University of Technology
|
|
2
|
+
# This file is licensed under the MIT License.
|
|
3
|
+
# See the LICENSE.txt file in the root of the repository for full details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Main entry point to the application.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import logging
|
|
11
|
+
import sys
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from . import run
|
|
16
|
+
from .arguments import Arguments
|
|
17
|
+
from .config import Config
|
|
18
|
+
from .version import VERSION
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def __parse_data(parser: argparse.ArgumentParser, data: list[str]) -> list[Path]:
|
|
22
|
+
result = [Path(f) for f in data]
|
|
23
|
+
for f in result:
|
|
24
|
+
if not f.is_file():
|
|
25
|
+
parser.error(f"Specified path is not a file: {f}")
|
|
26
|
+
if len(result) != len(set(result)):
|
|
27
|
+
parser.error("Non-unique file names as inputs - results would be inconsistent")
|
|
28
|
+
return result
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def __setup_logger(prefix: str) -> None:
|
|
32
|
+
log_format = "%(asctime)s [%(levelname)8s](%(name)20s): %(message)s"
|
|
33
|
+
logging.basicConfig(
|
|
34
|
+
level=logging.DEBUG,
|
|
35
|
+
format=log_format,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
root_logger = logging.getLogger()
|
|
39
|
+
handler = logging.FileHandler(f"{prefix}.log")
|
|
40
|
+
handler.setFormatter(root_logger.handlers[0].formatter)
|
|
41
|
+
logging.getLogger().addHandler(handler)
|
|
42
|
+
|
|
43
|
+
root_logger.info(f"Started instance ({VERSION})")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def parse_args() -> Arguments:
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
prog="emfuzzer",
|
|
49
|
+
description="Fuzzing experiments orchestrator for embedded",
|
|
50
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"data",
|
|
54
|
+
nargs="+",
|
|
55
|
+
help="list of files containing binary data to send to the target",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--output-prefix",
|
|
59
|
+
help="prefix to be used for saving output (logs, reports, etc.)",
|
|
60
|
+
default="emfuzzer",
|
|
61
|
+
type=str,
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--config",
|
|
65
|
+
help="path to the configuration file",
|
|
66
|
+
default="default-config.json",
|
|
67
|
+
type=Path,
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--version",
|
|
71
|
+
action="version",
|
|
72
|
+
version=VERSION,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
args = parser.parse_args()
|
|
76
|
+
|
|
77
|
+
args.data = __parse_data(parser, args.data)
|
|
78
|
+
args.output_prefix += f"-{datetime.now():%Y%m%d-%H%M%S}"
|
|
79
|
+
|
|
80
|
+
return args
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def main() -> int:
|
|
84
|
+
args = parse_args()
|
|
85
|
+
|
|
86
|
+
__setup_logger(args.output_prefix)
|
|
87
|
+
|
|
88
|
+
return run(args, Config.from_file(args.config))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
sys.exit(main())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright (c) 2025 Warsaw University of Technology
|
|
2
|
+
# This file is licensed under the MIT License.
|
|
3
|
+
# See the LICENSE.txt file in the root of the repository for full details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Module representing command line arguments.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Protocol
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Arguments(Protocol): # pylint: disable=too-few-public-methods
|
|
14
|
+
data: list[Path]
|
|
15
|
+
output_prefix: str
|
|
16
|
+
config: Path
|