Dizzy
Network and USB protocol fuzzing toolkit.
Install / Use
/learn @ernw/DizzyREADME
dizzy
dizzy is a fuzzing framework, written in python and capable of state-full and state-less fuzzing with a lot of output options.
usage
$dizzy_cmd version 2.0 running on Linux
usage: dizzy_cmd [-h] [-s START_AT] [-d SEC] [-l] [-o OPTIONS] [jobfile]
positional arguments:
jobfile
optional arguments:
-h, --help show this help message and exit
-s START_AT Start at the given step
-d SEC Output status every SEC seconds
-l List all loaded modules and their contents.
-o OPTIONS Overwrite a config option
A jobfile is all that is needed. The -s START_AT option can be used to start the fuzzing process at a given step and skip all previous steps. The -d SEC option configures the output interval of progress messages and using the -o OPTIONS parameter, config values from the jobfile can be overwritten.
jobfile
The Job Configfile contains all information necessary to start a fuzzing job
The [job] section defines which interaction file (state-full fuzzing) or which dizz file (state-less fuzzing) to use, the fuzzing mode and other parameters, like verbosity or the delay between mutations.
The [output] section defines were to send the generated data. The only common parameter is the type parameter, defining which session module to use. All other parameters in this section are session module dependent.
In the optional [probe] section a target probe can be defined. The probe runs after each complete fuzzing step and checks if the target is still available.
In the optional [value] section, generic values can be defined. Those values will be available to .dizz and .act files via the config_value function.
[job]
file = smb2/act/smb2_tree_connect.act
mode = none
delay = 0
verbose = 4
[output]
type = session.tcp
server = False
auto_reopen = False
session_reopen = True
timeout = 5
target_host = 127.0.0.1
target_port = 445
#[probe]
#type = icmp
#timeout = 1
#pkg_size = 64
#target_host = 192.168.2.1
[values]
creds_file = ./creds
share_path = \\127.0.0.1\test
module structure
Modules are created from the modules_src folder. A Makefile is provided that generates the __init__.py index files and packs the module zip files. To update the modules run make in the modules_src folder:
~/dizzy/modules_src# make
to the generated index files, run make clean in the modules_src folder:
~/dizzy/modules_src# make clean
In the modules_src folder, every module has an own folder, named the same as the module. Each module folder has the following structure:
- config.py -- in here, values such as the modules name, the modules external dependecies and the version number are defined.
- a folder named like the module (smb2 in this example).
- a folder named dizz, containing the module's .dizz files. (See DizzFile)
- a folder named act, containing the module's .act files. (See ActFile)
- a folder named job, containing the module's job files. (See JobFile)
- a folder named probe, containing the module's probes. (See Probe)
- a folder named session, containing the module's sessions. (See Session)
- a folder named deps, containing the module's internal dependencies. The folder will be added to pythons include path and its contents can be import ed in .dizz and .act files.
As a example the structure of the smb2 module is shown:
~/dizzy/modules_src# tree smb2
smb2
├── config.py
├── __init__.py
└── smb2
├── act
│ ├── smb2_file_access_read.act
│ ├── smb2_file_access_write.act
│ ├── smb2_neg_setup_auth.act
│ └── smb2_tree_connect.act
├── deps
│ ├── nmb
│ │ ├── ...
│ ├── pyasn1
│ │ └── ...
│ └── smb
│ ├── ...
├── dizz
│ ├── smb2_close_request.dizz
│ ├── smb2_create_request_read.dizz
│ ├── smb2_create_request_write.dizz
│ ├── smb2_negotiate_req.dizz
│ ├── smb2_read_request.dizz
│ ├── smb2_session_setup_req.dizz
│ ├── smb2_tree_connect_req.dizz
│ ├── smb2_write_request.dizz
│ └── smb_com_negotiate_req.dizz
├── job
│ ├── smb2_file_access_read.conf
│ ├── smb2_file_access_write.conf
│ └── smb2_tree_connect.conf
└── probe
└── smb2.py
wireshark plugin
In the wireshark folder, there is a lua plugin to export parsed packet in .dizz files.
Note, that it requires a lua version of 5.2.0 or newer.
To use it, run
$ cwd
~/dizzy/wireshark
$ wireshark -X lua_script:dizzy.lua
dizz file
So, now a short introduction to the dizzy packet and protocol specifications:
A single packet is described by a so called dizz file. Some example files can be found in the demo module folder that comes with dizzy. These files are python code so you need to write them in python syntax and format rules. They consist of 2 variables which need to be defined.
The first var is called objects and describes the fields of the packet.
Its a python array of objects with a specific interface:
objects = [
...
]
The following list show the pre-defined classes which is has implemented the interface already and are imported in a dizz file:
-
Field()is the standard class to defined a field. The class takes 6 arguments, which are:name(REQUIRED) have to be astr(), which is the name of the field (it to be unique in the packet).default(=b'') have to be abytes,intorstr(), which is the default value of the field.size(=None) have to be aint()orslice()(in bits).
If it isNonethan the field has the same size as the default value. Withslice()a variable size can be defined.fuzz(="none") have to be astr(), which is defined the fuzzing mode for the field.
The fuzzing mode for that field can be"none"for not fuzzing that field at all,"std"for fuzzing some values on the upper and lower value border, and"full"for fuzzing all possible values.endian(="!") have to be astr(), which is defined the endianness of the field when the default value is aint()
The endianss for that argument can be"!"for network (=big-endian),"<"for little-endian or">"for big-endian.encoding(=CONFIG["GLOBALS"]["CODEC"]) have to be astr, which defined the encoding schema of the default value if it is astr()
Example:
... objects = [ Field("length_field", b"\x00" * 8), # 8 byte length filed Field("variable_sized_field", b"\x00" * 8, slice(2, 20, 2)) ... ] ... -
List()is a field, which have a list of value from a file. The class takes 4 arguments, which are:name(REQUIRED) have to be astr(), which is the name of the field (it to be unique in the packet).path(=CONFIG["GLOBALS"]["DEFAULT_STR_LIB"]) have to be astr(), which defined the path to the file (one value per line, all values will be inserted while fuzzing)encoding(=CONFIG["GLOBALS"]["CODEC"]) have to be astr, which defined the encoding list.default(=None) have to bebytes(), which defined the first value of the list.
Example:
... objects = [ Field("length_field", b"\x00" * 8), # 8 byte length filed List("list_of_values", "/lists/test.txt", default=b"First value"), # new line separated list ... ]
The second var is called functions and is for execute some code to change the value of the fields.
Its a python array of functions with a specific interface:
functions = [
...
]
The following list show the pre-defined functions which is has the interface already and are imported in a dizz file:
-
linkis a function to copy the value of a field into another field per mutation. So that target field has the same value as the source field during the fuzzing. The function takes 2 arguments, which are:source(REQUIRED) have to be astr(), which defined the name of the source field.target(REQUIRED) have to be astr(), which defined the name of the target field.
Example:
... objects = [ Field("source_field", b"\xaa", fuzz="full"), Field("target_field", b"\x00", fuzz="none"), # this field has always the same value as source_field ... ] functions = [ link("source_field", "target_field"), ... ] -
lengthis a function to save the current size(in bits) of fields in a dedicated length field. This function is useful for example when the packet has a Type Length Value structure. The function takes 3s arguments, which are:target(REQUIRED) have to be astr(), which defined the name of the field to save the length.start(=START) have to be astr(), which defined where the function should start to count the length. The default parameter of this argument(=START) means to start at the beginning of the packet.stop(=STOP) have to be astr(), which defined where the function should stop(inclusive) to count the length. The default parameter of this argument(=STOP) means to start at the end of the packet.
Example:
objects = [ Field("field0", b"\xaa" * 2, fuzz="full"), Field("field1", b"\xaa" * 2, slice(4, 9), fuzz="full"), # calculate the current size of the field0 und field1 in bits and saves the size in length_field Field("length_field", b"\x00\x00", endian="<", fuzz="none"), ... ] functions = [ length("length_field", "field0", "field1"), ... ] -
length_bytesis a function same aslengthbut the length is s
Related Skills
node-connect
342.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
85.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
342.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
