In this experiment, we manually looked at the crashes that were triggered by Fuzzware over the course of the different experiments. As most fuzzing experiments produced a large number of crashes, we pre-clustered the crashes by the pc / lr values of the crashes, and looked at sample crashes for different pc / lr values.
In total, we identified 61 unique crashes. We collected a sample crashing test case for each one of the uniquely identified crashes.
The pre-clustering of crashes for a given fuzzware-project directory can be done via the fuzzware genstats crashcontexts
utility:
fuzzware genstats crashcontexts
A file containing different crash contexts can afterwards be found in fuzzware-project-*/stats/crash_contexts.txt
. This file should already be generated for each project for the synthetic samples (pw-discovery and P2IM / uEmu in case you are running the experiments using the provided experiment kickoff scripts.
Note To replay the inputs in this directory, you may need to use the initial version of
fuzzware-emulator
and rebuild fuzzware. For example:git clone https://github.com/fuzzware-fuzzer/fuzzware-experiments git clone https://github.com/fuzzware-fuzzer/fuzzware fuzzware-crash-replay cd fuzzware-crash-replay ./update.sh git --git-dir emulator/.git checkout 075dbb5 ./build_docker.sh fuzzware-crash-replay ./run_docker.sh -r fuzzware-crash-replay -d ../fuzzware-experiments
Then within docker
cd 04-crash-analysis/01 ./run.sh cd ~/fuzzware/targets/03-fuzzing-new-targets/zephyr-os/prebuilt_samples/CVE-2021-3319/POC ./run.sh
The following table shows an overview of the crashes which were produced by Fuzzware and which we identified as unique.
# | Firmware Set | Category | Target | Run Script | Description |
---|---|---|---|---|---|
01 | Synthetic Samples | Security | ARCH_PRO | run.sh | Password bypass crash |
02 | Synthetic Samples | Security | EFM32GG_STK3700 | run.sh | Password bypass crash |
03 | Synthetic Samples | Security | EFM32LG_STK3600 | run.sh | Password bypass crash |
04 | Synthetic Samples | Security | LPC1549 | run.sh | Password bypass crash |
05 | Synthetic Samples | Security | LPC1768 | run.sh | Password bypass crash |
06 | Synthetic Samples | Security | MOTE_L152RC | run.sh | Password bypass crash |
07 | Synthetic Samples | Security | NUCLEO_F103RB | run.sh | Password bypass crash |
08 | Synthetic Samples | Security | NUCLEO_F207ZG | run.sh | Password bypass crash |
09 | Synthetic Samples | Security | NUCLEO_L152RE | run.sh | Password bypass crash |
10 | Synthetic Samples | Security | UBLOX_C027 | run.sh | Password bypass crash |
11 | P2IM | Security | CNC | run.sh | Stack OOB write |
12 | P2IM | Security | Gateway | run.sh | OOB write in HAL |
13 | P2IM | Security | Heat Press | run.sh | Buffer overflow |
14 | P2IM | Security | PLC | run.sh | Missing bounds check |
15 | P2IM | Security | PLC | run.sh | Missing bounds check |
16 | P2IM | Security | PLC | run.sh | Missing bounds check |
17 | P2IM | Security | PLC | run.sh | Missing bounds check |
18 | P2IM | Security | CNC | run.sh | CNC input validation |
19 | P2IM | Security | Soldering Iron | run.sh | Expired pointer use |
20 | P2IM | Unchecked Init | Robot | run.sh | Initialization race |
21 | P2IM | Unchecked Init | Gateway | run.sh | Missing pointer check |
22 | P2IM | Unchecked Init | Gateway | run.sh | Missing pointer check |
23 | P2IM | Unchecked Init | Gateway | run.sh | Expired pointer use |
24 | P2IM | Unchecked Init | Gateway | run.sh | Missing pointer check |
25 | P2IM | Unchecked Init | PLC | run.sh | Missing initialization |
26 | P2IM | Unchecked Init | Reflow Oven | run.sh | Missing pointer check |
27 | uEmu | Security | utasker_USB | run.sh | OOB write to USB buf |
28 | uEmu | Security | Thermostat | run.sh | Stack buffer overflow |
29 | uEmu | Security | RF_Door_Lock | run.sh | Stack buffer overflow |
30 | uEmu | Security | uEmu.GPSTracker | run.sh | Unconstrained alloca |
31 | uEmu | Security | uEmu.GPSTracker | run.sh | Unchecked parsing |
32 | uEmu | Security | XML_Parser | run.sh | Double free |
33 | uEmu | Security | XML_Parser | run.sh | Stack buffer overflow |
34 | uEmu | Security | XML_Parser | run.sh | NULL pointer deref |
35 | uEmu | Security | XML_Parser | run.sh | Format String |
36 | uEmu | Unchecked Init | 6LoWPAN_Receiver | run.sh | Missing error handling |
37 | uEmu | Unchecked Init | 6LoWPAN_Sender | run.sh | Missing error handling |
38 | uEmu | Unchecked Init | RF_Door_Lock | run.sh | Recursion in init error handling |
39 | uEmu | Unchecked Init | 3DPrinter | run.sh | Missing initialization |
40 | uEmu | Unchecked Init | utasker_MODBUS | run.sh | Missing initialization |
41 | uEmu | Unchecked Init | utasker_MODBUS | run.sh | Missing initialization |
42 | uEmu | Unchecked Init | utasker_MODBUS | run.sh | Missing initialization |
43 | uEmu | Unchecked Init | Zepyhr_SocketCan | run.sh | Missing error handling |
44 | uEmu | Unchecked Init | Zepyhr_SocketCan | run.sh | Initialization race |
45 | uEmu | False Positive | utasker_USB | run.sh | Hardware assumption |
46 | Zephyr | Security | run.sh | CVE-2020-10064 | |
47 | Zephyr | Security | run.sh | CVE-2020-10065 | |
48 | Zephyr | Security | run.sh | CVE-2020-10066 | |
49 | Zephyr | Security | run.sh | CVE-2021-3319 | |
50 | Zephyr | Security | run.sh | CVE-2021-3320 | |
51 | Zephyr | Security | run.sh | CVE-2021-3321 | |
52 | Zephyr | Security | run.sh | CVE-2021-3322 | |
53 | Zephyr | Security | run.sh | CVE-2021-3323 | |
54 | Zephyr | Security | run.sh | CVE-2021-3329 | |
55 | Zephyr | Security | run.sh | CVE-2021-3330 | |
56 | Zephyr | False Positive | run.sh | Zephyr omitted watchdog check | |
57 | Zephyr | False Positive | run.sh | Zephyr omitted radio frame size check | |
58 | Contiki-NG | Security | run.sh | CVE-2020-12140 | |
59 | Contiki-NG | Security | run.sh | CVE-2020-12141 | |
60 | Contiki-NG | Security | run.sh | HALucinator 2019 CVE | |
61 | Contiki-NG | Security | run.sh | HALucinator 2019 CVE |
Within a fuzzware-project
that results from a fuzzware pipeline
run, we can find crashing inputs in the regular afl crash directory under main*/fuzzers/fuzzer*/crashes/id*
. We can replay these crashes by using the fuzzware replay
utility. A sample invocation of fuzzware replay
for a crash which shows the crash context is:
fuzzware replay -v main003/fuzzers/fuzzer1/crashes/id:000000*
This prints the crash reason, as well as the crashing register state to stdout. For examples of how to replay a crashing input outside the directory, also see the CVE reproduction POCs.
Note that many bugs give an attacker high degrees of control over the crash context. This means that based on fuzzing input, the same bug may lead to very different crash contexts. For example, consider a buffer overflow in the global data section which corrupts operating system-internal data structures (such as task structs, timer pointers, and driver state structures). Based on the exact corruption and interrupt context switch timings relative to the corruption, the same bug may lead to very different crashing states. For example, a task switch after the corruption may operate on a corrupted kernel struct and crash in the kernel scheduling logic, a handler of an external interrupt may use a corrupted driver state struct after the corruption occurred, and crash in the ISR, and so on. At the same time, two different bugs which both corrupt the data section may cause similar-looking crashing states.
The experiment was performed manually based on the output of the fuzzing runs from the previous experiments.
- Run fuzzers (
fuzzware pipeline
) for each target sample, followed by crash context generation (fuzzware genstats crashcontexts
). See the previous experiments for resultingfuzzware-project
directories. - Refer to
fuzzware-project/stats/crash_contexts.txt
. Replay the crashing inputs from previous fuzzing runs. Any crashes can be found infuzzware-project*/main*/fuzzers/fuzzer*/crashes
of the different targets. - Manual step: Perform a manual root cause analysis of the crashes.