What You Corrupt Is Not What You Crash:
Challenges in Fuzzing Embedded Devices
Marius Muench∗ , Jan Stijohann†‡ , Frank Kargl‡ , Aurélien Francillon∗ and Davide Balzarotti∗∗ EURECOM. {muench, francill, balzarot}@eurecom.fr† Siemens AG. jan.stijohann@siemens.com‡ Ulm University. frank.kargl@uni-ulm.de

Abstract—As networked embedded systems are becoming
more ubiquitous, their security is becoming critical to ourdaily life. While manual or automated large scale analysis of
those systems regularly uncover new vulnerabilities, the way
those systems are analyzed follows often the same approaches
used on desktop systems. More specifically, traditional testing
approaches relies on observable crashes of a program, and binary
instrumentation techniques are used to improve the detection of
those faulty states.
In this paper, we demonstrate that memory corruptions, acommon class of security vulnerabilities, often result in different
behavior on embedded devices than on desktop systems. In
particular, on embedded devices, effects of memory corruption
are often less visible. This reduces significantly the effectivenessof traditional dynamic testing techniques in general, and fuzzingin particular.
Additionally, we analyze those differences in several categories
of embedded devices and show the resulting impact on firmware
analysis. We further describe and evaluate relatively simple
heuristics which can be applied at run time (on an execution
trace or in an emulator), during the analysis of an embedded
device to detect previously undetected memory corruptions.

I.

I NTRODUCTION

Networked embedded systems are becoming a key component of modern life and the interconnection among different
devices and their associated online services is at the core of
the so called “Internet of Things”. While it is impossible
to accurately determine the precise number of such devices
currently deployed worldwide, it is commonly agreed that thisnumber is continuously growing and likely to exceed 20 billion
by 2020 [38].
The rapid growth in connected devices is accompanied
by an increase of their attack surface. This is especiallydaunting as those devices’ applications not only span consumer electronics but also encompasses the medical sector,
autonomous vehicles, Industrial Control Systems (ICS) andmore. Although the importance of the security of those devices
is vital, recent attacks are drawing a devastating picture of

Network and Distributed Systems Security (NDSS) Symposium 201818-21 February 2018, San Diego, CA, USA
ISBN 1-1891562-49-5
http://dx.doi.org/10.14722/ndss.2018.23166
www.ndss-symposium.org

reality: Malware like Mirai was attributed to puppeteer morethan tens of millions devices in 2016 [30], subsystems invehicles of notable manufactures [26], [34] as well as third
party devices [16] have been proven to be vulnerable to remoteattacks, and more than 1200 attacks on ICS have been observedin 2015 alone [32].
A large number of vulnerabilities found today on embedded
devices can still be considered as “low-hanging fruits”, suchas authentication or insecure management interfaces which
could be mitigated by enhancing the awareness of both vendors
and end-users. Another prevalent class of vulnerabilities that
affect these devices, however, is due to programming errors
leading to memory corruptions [39]. One of the most popular
techniques to discover these flaws is fuzz-testing, which, unlike
source code analysis and reverse engineering, is well suited forlarge-scale automation.
In a nutshell, fuzz-testing, or “fuzzing”, describes the process of automatically generating and sending malformed input
to the software under test, while monitoring its behavior for
anomalies [50]. Anomalies themselves are visible ramificationsof fault states, often resulting in crashes. Over the past decade,fuzzing became a major part of software development testing
as well as a very common tool for third party software security
testing.
Unfortunately, while common desktop systems have a variety of mechanisms to detect faulty states (e.g., segmentation
faults, heap hardening and sanitizers) and to analyze them (e.g.,command-line return values or core dumps), embedded devices
often lack such mechanisms because of their limited I/Ocapabilities, constrained cost, and limited computing power.
As a result, silent memory corruptions occur more frequently
on embedded devices than on traditional computer systems,
creating a significant challenge for conducting fuzzing sessions
on embedded systems software.
Because of the incredible importance that those systems
are taking, fuzzing needs to be applied to embedded devices
as it is today on software running on desktop computers. It
is therefore crucial to understand the difficulties in doing so
and to develop alternative ways to detect and analyze memory
corruptions.
Fuzzing can be performed with or without the availabilityof the source code. Source code availability makes testing
more efficient as memory semantics can be used to detect
anomalies, for example, by using compile time corruption
detection techniques. However, fuzzing embedded devices –Most of the theory behind fuzzing and most of the available fuzzing tools were designed to test software running on
desktop PCs. As we will discuss later in this paper, there are anumber of relevant differences that makes fuzzing embedded
system particularly challenging. As those differences depends
on the characteristic of the device under test, we first introduce
the classification of embedded systems that we will use in therest of the paper. We then discuss previous experiments thatapplied fuzz testing to different embedded devices, and finally
we present the challenges encountered to apply fuzzing in thisdomain.

which lack memory protections, exploit countermeasures, andfor which source code is very rarely available – becomes quitedifficult. Indeed, the only way left to identify a successful
memory corruption is to monitor the device to detect signsof an incorrect behavior. In this paper, we show that these“liveness” checks are insufficient to detect many classes ofvulnerabilities because it is often difficult to detect in a blackbox manner when the internal memory of the embedded device
has been corrupted.
While the aforementioned problems certainly have influenced previous fuzzing experiments on embedded devices
conducted by other researchers, up to our knowledge, they
have not been independently studied to this date. In thispaper we therefore make the first comprehensive analysis of
the effects of memory corruptions on different categories of
embedded systems. The study would not be complete without
discussing possible solutions that can be adopted to mitigate
this problem. In particular, we investigate a combination of(partial or full) emulation with a number of runtime analysis
techniques designed to replace the fault detection mechanisms
present in traditional desktop computer systems. Our experiments show that the analysis techniques we propose can detect
100% of the corrupted states triggered during our fuzzing
session, and therefore can significantly improve the number ofdetected vulnerabilities. We also show that partial emulation
significantly reduces the fuzzing throughput. However, whenfull emulation is possible, this setup can combine the bestof both worlds and detect 100% of the corrupting inputs
while improving the fuzzing efficiency beyond what could beobtained using the physical device.

A. Classes of Embedded Devices
While a precise definition of what is an embedded system
is hard to establish [21], in this paper, we adopt the widely accepted idea that embedded devices are separated from modern
general-purpose computers by two common characteristics: a)embedded systems are designed to fulfill a special purpose,and b) embedded systems often interact with the physicalworld through a number of peripherals1 connected to sensors
and actuators. The aforementioned two criteria cover a wide
variety of devices, ranging from hard disk controllers to smart
thermostats, from digital cameras to PLCs. These families canbe further classified according to several aspects, such as theiractual computing power, their unit cost, the field of usage, theextent to which they interacts with the environment, or thetiming constraints imposed on the device.
Yet, these classifications tell very little about the type of
security mechanisms that are available on a given device.
Therefore, in this paper we divide embedded systems according to the type of operating system (OS) they use. While theoperating system is certainly not the only source of security
features, it is responsible for handling the recovery from faulty
states, and it often serves as building block for additional, more
complex, security primitives.

In summary, in this paper we make the following contributions:• we analyze the challenges of fuzzing embedded devices,• we make a classification of embedded systems withrespect to the difficulty of detecting memory corruptionsin their software,• we evaluate the real world effects of different memory
corruptions on those classes of embedded systems,• we describe the techniques that can be used for improving
fuzzing on embedded devices,• we present six heuristics that can be used to detect faults
due to the memory corruption, when the firmware of anembedded system can be run in either a partial or fullemulation environment,• we implemented our heuristics on top of a combinationof the Avatar [54] and PANDA [13] frameworks and we
conducted a number of tests to show their effectivenessand their overhead in a fuzzing experiment.
II.

We separate embedded devices in the following three classes:
Type-I: General purpose OS-based devices.
General purpose Operating Systems are often retrofitted
to suit embedded systems. However, in comparison to
the traditional desktop or server counterparts, embedded
systems typically follow more minimalistic approaches.
For example, the Linux OS kernel is widely used in
the embedded world, where it is typically coupled withlightweight user space environments (e.g., busybox anduClibc).
Type-II: Embedded OS-based devices.
In recent years, custom operating systems for embedded devices have gained popularity. These systems are
particularly suitable for devices with low computational
power, and while advanced processor features such as a
Memory Management Unit (MMU) may not be present,a logical seperation between kernel and application codeis still present. Operating systems such as uClinux,

F UZZING E MBEDDED S YSTEMSThe term fuzz-testing, or simply “fuzzing”, was first introduced in 1990 by Miller et al. [33] to describe an automatedprogram testing technique in which the system under test was
fed with random input data. Nowadays, fuzzing is one of themost prevalent techniques for vulnerability discovery and moresophisticated, or “smart”, fuzzing techniques exist to guide thetest case generation process. In general, modern fuzzers can be
categorized in mutation-based fuzzers [42], [55], generationbased fuzzers [4], [15] and guided fuzzers [17]–[20], [49],based on the input generation strategy.

1 Note that while embedded systems can either be self-contained or consistof several embedded devices, for the sake of simplicity, we will use the term
embedded systems analogously to embedded device, as our work is focusedon single devices, rather than on interconnected systems.

2

StudyYear

SectorFuzzing Approach

Koscher et al. [27]Mulliner et al. [35]Kamel et Lanet [23]Almgren et al. [3]Alimi et al. [2]Van den Broek et al. [51]Kammerstetter al. [25]Lee et al. [29]

2010
2011
2013
2013
2014
2014
2014
2015

Automotive
GSM feature phones
SmartCards
PLCs & SmartMeters
SmartCards
Smartphones–
Automotive

Mutation-based, Blackbox
Generation-based, Blackbox
Generation-based, Blackbox
Mut.- & gen.-based, Blackbox
Generation-based, Blackbox
Generation-based, Blackbox
Mutation-based, Dynamic Inst.
Random-based, Blackbox

Type-I3
3-

Type of Device
Type-II Type-III
3
3
3
3
3
3
3

TABLE I: Fuzzing experiments of embedded systems in the literature.
ZephyrOS or VxWorks are examples for these systems
and they are usually adopted on single-purpose user
electronics, such as IP cameras or DVD players.

all doors, disable the car’s light, and permanently enable
the horn. Similar results were reported in a later study by
Lee et al. [29]. Hereby, random fuzzing of the data field in
CAN packets led at least to observable changes in the car’s
instrumentation panel.

Type-III: Devices without an OS-Abstraction.
These devices adopt a so called “monolithic firmware”,
whose operation is typically based on a single control
loop and interrupts triggered from the peripherals inorder to handle events from the outer world. Monolithic
approaches can be found in a large variety of controllersof hardware components, like CD reader, WiFi cards or
GPS dongles. The code running on these devices can becompletely custom, or it can be based on operating systemlibraries such as Contiki, TinyOS or mbed OS 22 .
While those libraries are providing abstractions to programmers, the resulting firmware will contain both system
and application code compiled together, and, thus, formsa monolithic software.

In [3], Almgren et al. developed several mutation-based
and generational-based fuzzers which were used to test various
PLCs and smart meters. The fuzzing experiments lead to thediscovery of several known and unknown denial of service
vulnerabilities, some leading to a completely unresponsive
PLC which could only be recovered after a power cycle anda cold restart.
Mulliner et al. [35] and Van den Broek et al. [51] developed a generation-based fuzzer in accordance to the GSMspecification to test GSM implementations in both feature- andsmart-phones. While both studies were able to trigger a large
number of errors – including memory exhaustions, reboots,
and denial-of-service conditions – the authors concluded that
correctly monitoring the devices under test in an automatedmanner is still a very challenging task.

For better readability, we will use Type-I, Type-II, andType-III inter-exchangeably with the corresponding classnames. Additionally, we will use Type-0 in order to reference
to traditional multi-purpose systems.

To avoid this problem, PROSPECT [25] introduces a novel
approach that involves the partial emulation of an embedded
device’s firmware during a fuzzing experiment. The authors
were able to emulate Linux-based systems by forwarding
system calls that are likely to access peripherals to the physicaldevice. Using this method, the authors discovered a previous
unknown 0-day vulnerability in a fire alarm system.

B. Past Experiments
In recent years, fuzz-testing has become more and more
popular as a way to test the security of embedded systems.
Table I provides a short overview of some of the recent efforts
in this area. These works covered different sectors, different
input generation strategies, and different classes of embedded
devices.

C. Main Challenges of Fuzzing Embedded Devices
Based on our experience and on problems reported by otherauthors while conducting fuzzing experiments in previous
works, we identified three main challenges that make fuzzing
much more complex and less efficient on embedded devices
compared to desktop systems. On top of these specific problems, embedded systems are also inheriting other challenges
which are common to fuzzing in general (e.g., test casegeneration) but we consider them out the scope of this work.

For instance, Alimi et al. [2] fuzzed parts of the MasterCard
M/Chip specifications by generating test-inputs using a genetic
algorithm. The authors observed that real cards would become
unusable during comprehensive fuzz-testing, and therefore
moved the parts under test into a simulator where they could
trigger the approval of illegitimate transactions.
Some modern smart cards also contain a web server
implementation. Kamel et Lanet [23] designed a generationbased fuzzer for the HTTP implementations of those web
servers and were able to trigger several flaws, including errors
in the smart cards. Koscher et al. [27] conducted a security
test of automotive systems using fuzzing in addition to reverse
engineering and packet sniffing. More specifically, the authors
fuzzed packets for the CAN bus, discovering packets to unlock[C1] – Fault Detection. The vast majority of fuzzing techniques relies on observable crashes as immediate consequences of faults occurring during a program’s execution.
Desktop systems offer a variety of protection measurements
which are triggering a crash upon a fault, and while some ofthem are designed with security in mind (e.g., stack canaries),
others are inherent architecture artifacts, such as segmentation
faults caused by a Memory Management Unit (MMU). Unfortunately, these techniques are rarely present (or are very

2 Interestingly, and despite the name, mbed OS 2 (also referenced as ”mbed
OS classic”) is an operating system library. Later versions, on the other hand,are actual embedded OS and would serve as building block for Type-II devices.

3

in many cases its is challenging to re-compile the software.
In fact, embedded systems can span a variety of devices, eachwith its own operating system, peripherals and processor –for which a comprehensive toolchain is rarely available to thetester. A common solution that does not require access to thesource code of the application is to resort to binary dynamic instrumentation frameworks such as Pin [31], Valgrind [37], andDynamoRio [7]. AFL is also capable of retrieving coverageinformation while fuzzing binary software by adding instrumentation capabilities to QEMU, which has recently even beleveraged for fuzzing full-blown Linux kernels [36].

limited) on embedded devices. As we will describe in moredetails in Section III, this poses a significant challenge forfuzzing embedded devices.
Moreover, even when mechanisms leading to observablecrashes are in place, monitoring those crashes can be complicated. In fact, while for desktop systems crashes are often
accompanied with error messages, embedded systems may lackequivalent I/O functionalities.
As a result, a sophisticated liveness check (or probing)has to be deployed to check the effects of the test on thedevice. In general, it is possible to adopt two types of probing.
Active probing inserts special requests into the communication
to the device or to its environment. This influences the stateof the software under test – as the state of the software isperiodically checked by providing valid input and evaluating
the corresponding response. In contrary, passive probing aims
at retrieving information about the device’s state without
altering it. This could be achieved by looking at the answers
provided by the device to the test inputs or by observing“visible” crashes.

Unfortunately, all of the above static and dynamic instrumentation tools are closely tied to the target operating system
and CPU architecture, and at the time of writing none of themprovides support for Type-II and Type-III embedded devices.
III.

M EMORY C ORRUPTIONS IN E MBEDDED S YSTEMSIn this section we investigate the effects on different classesof embedded devices of memory corruption vulnerabilities,
a class of bugs which often leads to crashes on desktop
systems. For better readability, we first introduce a definition
for observable crash and fault states. Afterwards we will
describe our case study and the obtained results.

[C2] – Performance and Scalability. In case of desktop
systems, multiple instances of the same software can easily
be started and fuzzed in parallel, e.g., via multi-processing or
virtualization. This parallelization poses a substantial challenge
when dealing with embedded devices. Obtaining a large number of copies of the same physical device is often infeasible
due to limited resources (e.g., financial), and to infrastructure
requirements such as space and power supply.

A. Bugs, Faults, Corruptions & Crashes
In general, both firmware and software are built to solve
specific tasks, and thus they follow a – not necessarily explicit– specification of what they are intended to do. It is also
well known that software and firmware code can contain bugs:
errors that can bring a program into an unintended state. Whensuch unintended state can be exploited by an attacker, a bug
is classified as a security vulnerability.

Additionally, fuzzing frequently requires to restart thetarget under test in order to re-establish a clean state forthe next test case. While this is easy to achieve for desktop
systems, e.g., via virtual machine snapshots, resetting the stateof an embedded system can take a considerable amount oftime (up to few minutes), as it often requires a full reboot of
the device. This results in a considerable slow down of theentire testing process, as we will discuss in more details in
Section VI.

A common class of bugs that often leads to vulnerabilities
are memory corruptions or memory errors. In particular, Van
der Veen et al. [52] differentiate between spatial and temporal
memory errors. Whereas the first type denotes out of bounds
accesses of a memory object, the second type represents
accesses to a memory object that does not exist anymore.
A memory corruption itself can cause an observable crashof a program, whereby the program is either terminatedor some recovery procedures, such as exception handlers,
are executed. Today, many common security mechanisms –such as stack canaries or heap consistency checks – trigger
these crashes. However, under particular conditions, we canencounter memory corruptions which do not lead to any
observable and immediate crash of the system. We call thesecases silent memory corruptions. In a silent memory corruption
the program continues its execution and enters an unintended
faulty state.

[C3] – Instrumentation. While fuzzing was originally proposed as a black-box testing technique, it quickly became
evident that a more clever generation and prioritization of
the input values required access to some information about
the state of the system under test. For example, it is very
popular for desktop systems to adopt a mix of compile-time
instrumentation and run-time instrumentation to collect codecoverage information about the provided inputs and to detect
subtle corruption of the memory of the system.
Probably the most established fuzzer which uses sourcecode instrumentation to collect information about retrieved
code coverage is American Fuzzing Lop (AFL) [55]. Prominent examples of instrumentation to detect memory corruptions
are sanitizers like AddressSanitizer [43], ThreadSanitizer [44],MemorySantizer [48], and UndefinedBehaviourSanitizer [11],which also have proven to be very efficient in uncovering
vulnerabilities in combination with fuzzing [45].

The important consequence of silent memory corruptions
is that the actual fault might only become noticeable at a later
point in time when a certain functionality is requested or whena particular sequence of events is received. While this may not
be considered a problem as long as the device continues to
executes, it poses a significant threat for the safety and securityof embedded devices. In fact, as soon as the system enters afaulty state, the integrity of its operation cannot be guaranteed
anymore, and wrong data could be returned or processed at
any time.

While all of these components are available for the popular
compilers clang and gcc, source code is not typicallyavailable for firmware images from embedded devices, and4

Desktop
Type-I
Type-II
Type-III

Platform

Manufacturer & Model

CPU Family

Operaring System

LIBC

MMU

Single Board Computer
RouterIP camera
Development Board

Beaglebone Black
Linksys EA6300v1
Foscam FI8918W
STM Nucleo-L152RE

Cortex A-9
Cortex A-9
ARM7TDMI-S
Cortex M-3

Debian GNU/Linux
Embedded LinuxuCLinux
None

glibc
uclibcuclibclibmbed3
3
7
73

TABLE II: Devices selected for the experiments.
While desktop systems are also subject to silent corruptions, those are a lot less frequent because they are deploying
several lines of defenses. Indeed while those lines of defense
are often in fact designed for hardening programs against
attacks they also make faults more likely to lead to a crash.
Those mechanisms include memory isolation, protection mechanisms, and memory structures integrity checks.
Embedded systems, on the other hand, often lack similar
mechanisms. This can not only lead to devastating consequences as soon as a system interacts with the exterior world,
but it also complicates the security analysis of those systems as
many black box testing techniques, and fuzzing in particular,
rely on observable effects to infer the state of the device.
B. Experimental Setup
In order to study how memory corruptions behave acrossdifferent computing systems, we conducted a number of experiments. Our goal is to trigger the same memory corruption
conditions on different systems to analyze whether they yield
to observable crashes or result in silent corruptions.
For these experiments, we selected one device for eachdevice class presented in Section II-A, and compare the resultswith a baseline system consisting of full-fledged GNU-Linux
desktop OS. All systems are ARM-based and we recompiled
each firmware image to obtain comparable results. We introduced our vulnerabilities in two popular and widely used libraries: mbed TLS, an SSL library designed for both embedded
and desktop system, and expat, a popular XML parser. The
two are good candidates for our experiments because memory
corruptions vulnerabilities were previously publicly released inboth of them, and because they are popular, open source, andpresent in many modern embedded devices.

Listing 1: Examples of artificial vulnerabilities.
XML_Parse(XML_Parser parser, const char *s, int len,int isFinal)
2 {3char overflowable[128];
4
[...]
5
//this returns a heap object
6
void *buff = XML_GetBuffer(parser, len);
7
[...]
8
//trigger immediate stack-based buffer overflow
9
if (len == 1222){
10
memcpy(overflowable, s, len);11
return;12}
13//this will cause a null pointer dereference
14
else if (len == 1223){
15
buff = NULL;
16}
17//causing a heap-based buffer overflow
18
if (len == 1225){
19
memcpy(buff, s, len);20
memcpy(buff + 1225, s, len);
21
}
22
else{
23
memcpy(buff, s, len);24}
25//cause an uncontrolled format-string
vulnerability
26
if (len == 1224){
27
printf(buff);
28
}
1

29

[...]

30
31

}

C. Artificial Vulnerabilities
Since our focus is not the discovery of new bugs but rather
the analysis of the effects of memory corruptions on embedded
systems, we inserted several vulnerabilities leading to memory
corruptions in our test samples. Specifically, we used stackbased buffer overflows and heap-based buffer overflows as
examples of spatial memory corruptions, and null pointerdereferences and double free vulnerabilities as examples of
temporal memory corruptions. Additionally, we also inserted
a format string vulnerability that can either be used for
information leakage or for arbitrary memory corruptions.

To analyze their behavior in a realistic context, we chose
existing Commercial Off-The-Shelf (COTS) products: a router
to represent Type-I systems, and an IP camera for Type-II
devices. We compiled our vulnerable application for thosetargets and then loaded it on the device. For the monolithic
class, however, obtaining a COTS device with customizable
firmware is difficult, as their firmware usually consists of only
one custom binary blob responsible for the entire operationof the device. Therefore, we used a development board withpublicly available software for peripheral interaction. For thisdevice we included our test code in the firmware, compiled it
and then loaded the firmware on the device. Table II shows asummary of our three test platforms, including information
about which C library is used and whether an MMU ispresent. Besides the operating system, these properties mainly
determine the behavior of a system in case of a memory
corruption.

Our approach of bug insertion was inspired by the one usedin LAVA [14], i.e., we ensured that each vulnerability had
its own independent trigger condition. However, as we only
had to inject a limited number of vulnerabilities, we manually
selected the vulnerable paths and the position of each bug.
Likewise, for the purpose of our experiments we did not need“realistic” checks or path conditions particularly difficult to
3 Note that Cortex M-3 microcontrollers can be equipped with an optional
Memory Protection Unit (MPU), which provides basic memory protections.
While present on this specific Type-III device, we do not utilize its features,as this is a very common scenario and the most problematic case.

5expatmbed TLS

Desktop

Type-I

Type-II

Type-III

Desktop

Type-I

Type-II

Format String

3

3

7

7

3

3

7

!

(malfunc.)(hang)

Stack-based buffer overflow

3

3

3

!

3

!

(hang)

3

3(opaque)(opaque)(hang)

Heap-based buffer overflow

3(late crash)

7

7

3

7

7

Double Free

3

3

7

7
(malfunc.)

3(late crash)

7

7

Null Pointer Dereference

3

3

3

7

(reboot)(malfunc.)

3(reboot)

Platform!

3!
(late crash)!

3Type-III

7

TABLE III: Observed Behaviour of triggered memory corruptions.
explore. Therefore, we simply added custom branches in thecode that triggered the various vulnerabilities based on thelength of the user-provided payload. Listing 1 shows four of
the inserted vulnerabilities inside the expat library.

are the preferred outcomes of an experiment as during a
fuzzing session they allow to immediately identify the responsible input.
Hangs and late crashes (R3 and R4) can be more difficult todeal with, in particular when the crash is delayed long enoughthat a fuzzer may have already sent multiple other inputs tothe target system and the input responsible for the corruption
will therefore be difficult to identify. However, the presence ofa fault is still observable in these cases. Things get even more
complex when a device starts malfunctioning (R5). In this case,there are no crashes at all, but the results provided to certain
requests may be incorrect. To detect this case, a fuzzer would
need to know what is the correct output for each input it sends
to the system – something which is very seldom the case in
security testing. A possible workaround can consist in inserting
between two consecutive inputs a number of functional test
requests for which the output is known. However, even when
this solution is sufficient to detect the malfunction, it introduces
a considerable delay in the fuzzing experiment. Finally, theworst case is when the device continues its operation with noobservable side effect (R6). In fact, since part of the device
memory has been corrupted, there may be side-effects or
unexpected behaviors in the future.

D. Observed Behavior
The goal of this experiment was to observe the behavior ofeach device after sending malicious inputs that trigger one of
the inserted vulnerabilities. As expected, the software runningon GNU/Linux desktop crashed each time it was provided witha malicious input that triggered the vulnerability.
However, the embedded devices were not always able todetect the fault and, in some cases, they even continued theexecution with no visible effects – despite the fact that theunderlying memory of the system was corrupted. To better
differentiate between the different behaviors we observed inour experiments, we categorize our observations in six possible
results:[R1] Observable Crash (3) – The execution of the device
under test stops and a message or other visible effectis easily observable. In less optimal cases, no detailed
information about the causes of the crash is produced(we mark these cases as opaque in Table III).
[R2] Reboot (3) – The device immediately reboots. For TypeIII devices there is no difference between a crash and areboot because they are monolithic applications. However,for Type-I and Type-II devices a given service (e.g., a web
server) can crash while the rest of the embedded system
may still continue to work properly.
[R3] Hang (!) – The target hangs and stops responding to new
requests, possibly stuck in an infinite loop.
[R4] Late Crash (!) – The target system continues its execution for a non-negligible amount of time and crashes
afterwards (e.g., when the connection is terminated).
[R5] Malfunctioning (7) – The process continues, but reports
wrong data and incorrect results.
[R6] No Effect (7) – Despite the corrupted memory, the target
continues normally with no observable side-effects.

Table III shows the result of our experiments. It is clearthat the fewer features are provided by an embedded platform,
the less likely the system is to detect memory corruptions.
An interesting observation is already the difference between afull scale GNU/Linux and an embedded Linux for heap-based
buffer overflows and double free corruptions. While on thedesktop system the inlined heap consistency checks provided
by the standard C library are triggering a verbose crash quickly
after the corruption, the embedded Linux continues and crashes
at a later point in time, often just during the exit() handler.
The table also shows that corrupting inputs for Type-II
and Type-III devices are very rarely triggering a crash. This
provides a perfect example of how common are silent memory
corruptions in real-world embedded systems.
Another important observation can be made when looking
at the devices’ behavior for the format string vulnerability.
Both the embedded and the full scale GNU/Linux are reporting
segmentation faults, due to the attempt to access unmapped

Immediately observable crashes and reboots (R1 and R2)
6example, collecting a trace of execution to measure coverage
to mutate fuzzing inputs [55], adding checks for memory
allocations [43], or hardening the program with, e.g., Control
Flow Integrity [1]. Unfortunately, most of these tools are not
yet available for embedded systems.

memory. However, uClinux and the monolithic device arecontinuing execution, which is a result of the lack of an MMU.
This shows that the MMU plays a very important role whenit comes to detecting memory corruptions.
A similar behavior can be observed on the results of thenull pointer dereference test. The processes running on both the
GNU/Linux desktop and on the embedded Linux are correctly
crashing once the program tries to write memory to the NULLaddress range. With the same vulnerability, the monolithic
device will continue execution although data has been written
to the exact same address. In fact, as the execution of thefirmware is not dependent on the content of the memory at
this location, this memory corruption does not influence thebehavior of the system.

B) Binary Rewriting.
Prerequisites: binary firmware image, device
When only the binary firmware is available, binary rewriting can be used to instrument the code [?]. However, there aresome difficulties in instrumenting the code. First, the firmware
needs to be fully disassembled which can be difficult when it
is provided as a raw binary [5]. Second, memory semantics,
boundaries and data structures are lost in the compilation
process and need to be recovered to add instrumentation.
This requires a very challenging partial decompilation phase.
Finally, the memory usage of embedded devices is often
optimized to reduce costs, leaving little room for adding
complex instrumentations.

The result of the same test on the uClinux system isparticularly interesting: after the NULL write, the device hangs
for few seconds and then reboots. This is not surprising as
in uClinux the kernel is mapped in the lower part of thememory (so writing at address 0x0 corrupts kernel memory).
The reboot, on the other hand, is possibly caused due to an
hardware watchdog that detects the hang and automaticallyrestarts the device as a recovery mechanism.

C) Physical Re-Hosting.
Prerequisites: sources, compiler chain, different device

To summarize, while in certain conditions silent memory
corruptions can occur also in traditional desktop envoronments,
our experiments show that they are often the rule and not the
exception in the embedded world. As the use of fuzzing is
becoming prevalent to test firmware code for vulnerabilities,
and as fuzzing is relying on observable crashes to detect
bugs, the limited support for fault detection can have very
severe consequences on the effectiveness of security testingfor embedded devices.
IV.

In certain cases, the analyst may be able to recompile thecode for a different target device, for example to relocate aprocess from a Type-II device to a more test-friendly TypeI device or from a Type-I device to a regular Linux desktop
system. This may also improve scalability, if the new device
is cheaper and more readily available than the original one or
if the new target is a regular computer.
However, on top of the difficult prerequisites, methods
relying on this approach have another major drawback. In fact,
it may be difficult to reproduce bugs found on the new target
system in the original device (where they may even not bepresent at all due to the different architecture or due to changes
introduced by recompiling the binary) and conversely, bugs
that are present on the original target may not be present on
the new target.

M ITIGATIONSIn the previous section we have seen that embedded systems come in all forms and with very different characteristics.
While fuzzing a Type-I device may not present particular challenges, memory corruptions on Type-III systems rarely resultin an immediate crash, imposing a significant challenge toautomatically identify when a vulnerability has been triggered.
So how can we fuzz devices when no reliable feedback isavailable? Again, the vast diversity of existing devices makes
it difficult to find a general answer to this question. Therefore,
in this section we present six different options that may beavailable to the tester and we discuss both advantages andlimitations of each solution.

D) Full Emulation.
Prerequisites: firmware image, peripherals emulation

As the majority of software for embedded devices is
distributed in a binary-only manner, access to the source code
can be considered an exceptional case. In the rare cases inwhich the source code is available, proprietary toolchains areoften required to recompile the firmware.

Images of the device firmware are often available to the analyst, either because they are extracted directly from the device
or because they are obtained from a firmware update package
available from the manufacturer. Costin et al. [12] and Chen etal. [8] have shown that, under certain conditions, applications
extracted from Type-I firmwares can be virtually rehosted, i.e.,they can be executed inside a generic operating system runningon a default emulator. Likewise, the Qemu STM32 [6] project,which extends Qemu to emulate the STM32 chip, shows thatwhen complete hardware documentation is available, with aconsiderable effort to implement the hardware emulation it isalso possible to fully emulate Type-III firmware images.

However, when testers have all the required components(e.g., if the tester is the device manufacturer) they can instrument the source code to provide better run-time information or
to introduce a crash harness functionality. Typical instrumentation includes a combination of multiple techniques [45]. For

This solution can greatly improve fuzzing. First of all,test experiments can be conducted without the presence ofthe physical device, thus allowing for a much greater parallelization. Second, dynamic instrumentation techniques canbe easily applied and the emulator can be used to collect aA) Static Instrumentation.
Prerequisites: source code and compiler toolchain7

Memory Map

Annotated ProgramIf full emulation remains impractical in most circumstances, a partial emulation can still provide benefit while
conducting testing experiments. This approach was first proposed by Avatar [54] and Surrogates [28] for Type-III devices
and then extended to Type-I systems in PROSPECT [24],[25]. The general idea behind this solutions is to use an
emulator (in which the firmware code is executed) modified toforward peripheral interactions to the actual physical device.
The result provides the advantages of a full emulation solution
without the burden of knowing and emulating I/O operations.
However, what this solution gains in flexibility it is sacrificed
in performances (due to the additional interaction with the real
device) and scalability (due to the current need of pairing eachemulated instance with a physical device).

Memory Accesses

Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking

Register stateE) Partial Emulation.
Prerequisites: firmware image, device

Execution

large amount of information about the running firmware. The
disadvantage of this solution is that it is only applicable when
all peripherals being accessed by the target are known andcan be successfully emulated, which is unfortunately rarely thecase. Overall, being able to run an arbitrary firmware inside
an emulator still remains an open research problem.

7
3
3
3
3
3

7
3
3
7
3
3

3
7
3
7
3
3

3
3
7
7
7
7

7
3
3
7
7
3

TABLE IV: Deployed live analysis techniques and their requirements.
world devices, the chances of finding an available and usable
hardware tracing support are quite low.
To summarize, the first three solutions (A, B, and C)
require the tester to modify the firmware image, either statically through recompilation or dynamically through runtime
instrumentation. Unfortunately, as we already explained above,
this is rarely an option when conducting a third party security
testing. At the other end of the spectrum, the last threetechniques (D, E, and F) have the advantage of not requiring
any firmware modification, but they require instead additional
technologies (either a software emulator or an hardware tracing
support) to collect information about the running firmware.

F) Hardware-Supported Instrumentation.
Prerequisites: device, advanced debug feature (e.g., trace
capable debugging port and debugger)
If the tester has access to a physical device with advanced hardware instrumentation mechanisms (such as realtime tracing), it may be possible to collect enough information
to improve the fault detection during the execution of thedevice. For instance, chip manufacturers often embed hardware
tracing features such as ARM’s Embedded Trace Macrocell(ETM) and Coresight Debug and Trace, or Intel’s Processor
Trace (PT) technologies [41] 4 . ARM tracing mechanisms
are optional components of processors (“IPs”), which may be
optionally included in the processor. Multiple variants tracing
exist, such as tracing only branches, all instructions, or also all
memory accesses – and different techniques rely on different
debug ports (e.g., dedicated trace ports, Single Wire Debug(SWD) or Single Wire Output (SWO) ports). Unfortunately, theavailability of such tracing hardware is variable. In lower-end
devices (typically Type-III devices), manufacturers tend not toinclude any tracing capabilities, because of the relatively large
impact on the chip surface, and therefore on the cost, that suchmechanisms would incur. Development devices may have such
facilities (sometimes when the micro-controller design is tested
on FPGA before manufacturing) but this is less frequent incommercial production devices. Finally, in some cases debug
access may be present but not available to prevent third-party
analysis.

In this paper we show that this information can be used
to detect the presence of faults during the execution of anembedded system, by replacing the role of memory protection
or other crash harness mechanisms usually provided by the
hardware or by the operating system on desktop computers.
However, this require the development of dedicated analysis
routines specifically designed to identify the signs of memory
corruption.
V.

FAULT D ETECTION H EURISTICS

In this section we present a set of heuristics that we
implemented to mimic existing compile-time, and run-time,
techniques, and which are already able to detect all thememory corruption vulnerabilities showcased in Section III.
These heuristics are inspired by techniques that have alreadybeen in use for detecting or mitigating memory corruptionsin other settings, such as shadow stacks, compiler warnings
for unsafe function calls, and run-time verifications as implemented by instrumentation tools like Address Sanitizer [43] orValgrind [37]5 .
It is also important to mention that we selected heuristics
to be implementation independent, in order to not only workin a live analysis setting (as it is the case if the firmware is
run in an emulator) but also to be applicable “post-mortem”
on previously collected execution traces (as in the case of an

For example, while looking for test devices to conduct
our experiments we encountered devices where the tracing
support was deactivated for security reasons, multiplexed on
pins which are used for another purpose, not routed on thecircuit board (PCB), or where the silicon supported the feature
but the pins were not available. In summary, when testing real

5 While the underlying algorithms of the heuristics are based on
known principles, we implemented them as external monitors (cf. Section VI). The interested reader can find the implementations at
https://github.com/avatartwo/ndss18_wycinwyc.

4 However, only available on recent high performance processors.

8

is especially appealing as it requires the least amount of
information: only the knowledge of the executed instructions.

hardware-based tracing mechanism). As a result, they only rely
on information extracted from the execution of the binary code
and additional annotations provided by the analyst.
In the following we introduce briefly six heuristics anddiscuss their role, their limitations, and the information theyrequire in order to be applied in our scenario.

Call Frame Tracking:Call frame tracking is a more advanced version of thecall stack tracking technique which detects coarse grained
stack-based buffer overflows, without false negatives,
right when they occur. In essence, stack frames are located by tracking function calls, then contiguous memory
accesses are checked not to cross stack frames. Hereby,
this requires to identify the executed instructions as well
as register values to extract the stack pointer values
upon function entries. Then, memory accesses have tobe observed to detect the actual corruptions.

Segment Tracking:Segment tracking is possibly the simplest technique thataims to detect illegitimate memory accesses. The coreidea is to observe all memory reads and writes and verify
if they occur in valid locations, thus somehow imitating
an MMU at detecting segmentation faults. This techniqueonly requires knowledge about the memory accesses andthe memory mappings of the target. Both are easily
accessible in an emulator, however, when only traces areavailable the memory map can be obtained by reverse
engineering.

Stack Object Tracking:Stack objects tracking consists in a fine-grained detectionof out-of-bound accesses to stack variables, which isinspired by the heap object tracking approach proposed
by Serebryany et al. [43]. Hereby, memory reads andwrites observed during execution are checked against
the individual variable size and position in the stack.
Obviously, this requires to track executed instructions andmemory accesses, as well as elaborate information about
the stack variables. For the sake of simplicity, we use
variables information which is present in debug symbols.
However, in the general case, it is possible to retrieve suchinformation in an automated manner from binary code, as
proposed by several previous studies [22], [47].

Format Specifier Tracking:
Tracking format string specifiers is a naı̈ve technique todiscover insecure calls to printf()-style functions andis inspired by the printf protection outlined in [46]. In
essence, this protection validates that the format string
specifier points to a valid location upon entry in a functionof the printf() family. In the simplest case, without
presence of dynamic generated format string specifier,those valid locations would have to lie within readonly segments. All in all, this technique requires not
only knowledge about the location of format handling
functions, but also the register state while entering one ofthose functions and the argument order. Both the locationof the according function and their argument order canbe obtained by reverse engineering or automated static
analysis of the firmware.

Table IV lists the six presented heuristics and summarizes
what type information is required for the analysis.
VI.

I MPLEMENTATIONIn the previous section we introduced six heuristics which
can aid fuzzing when applied during emulation or when used toanalyze execution traces. In order to show their effectiveness,
we implemented them as part of distinct proof of concept
trace analysis and live analysis systems. However, as theavailability of hardware trace capabilities is uncommon and thelive analysis scenario presents more engineering challenges,
we focus our experiments on performing live detection whilefuzzing a firmware that is either partially or fully emulated.

Heap Object Tracking:This technique is designed to detect both temporal andspatial heap related bugs and is influenced by the instrumentation and run-time verification approaches presented
in [43]. It achieves its goal by evaluating the arguments
and return values of allocation and deallocation functions
and bookkeeping the location and sizes of heap objects.
This allows to easily detect out-of-bounds memory accesses or access to a freed object. However, this heuristic
depends on a variety of information: executed instructions, the state of the registers, memory accesses, andknowledge about allocation and deallocation functions.
The latter could be retrieved by reverse engineering, or byusing advanced methods for discovering custom allocators
as demonstrated by MemBrush [9].

The experimental live analysis system we created combines
PANDA [13], a dynamic analysis platform, and Avatar [54],an orchestration framework for dynamic analysis of embedded
devices. Our implementation required modifications to both
frameworks and we released both the modifications and theimplemented heuristics as open source 6 , to help future fuzzing
campaigns and motivate additional research in this field.

Call Stack Tracking:Call stack tracking is replicating traditional shadow stackprotections [53], therefore aiming at detecting functions
that do not return to the callee. This can help to identify
stack-based memory corruptions that overwrite the return
address of a function. It does so by monitoring all
direct and indirect function calls and return instructions.
However, as embedded devices are often interrupt-driven,
this heuristic can lead to false negatives. Nevertheless, itLive analysis system description: The three core components of the experimental system are Avatar, PANDA, and a setof PANDA analysis plugins which implements the heuristics
described above. Avatar allows dynamic binary analysis ofembedded devices by enabling partial emulation of Type-III
devices, as described in section IV. In essence, it orchestrates
the execution of the firmware on a target embedded device and6 Available at https://github.com/avatartwo/ndss18 wycinwyc.

9

on an emulator, by providing the possibility to automaticallytransfer state between device and emulator and by forwarding
I/O accesses. Avatar was originally designed to use S2E [10]as backend emulator. As part of our work, we modified it toreplace S2E with PANDA, as this solution allows not only
to perform analysis during the execution, but also to create
lightweight records of the execution which can be used to
identify the root cause of a crash.

A. Target Setup
For our tests, we compiled the expat application presented
in Section III-B for a Type-III device7 and conducted a numberof fuzzing sessions against the target using four different
configurations, covering both optimal and worst case scenarios:• NAT: Native. Fuzzing is performed directly against theactual device – therefore without using any fault detection
heuristic. We use this case as the baseline to compare theresults of other experiments.
• PE/MF: Partial Emulation with Memory Forwarding.
The firmware is emulated and access to the peripherals is
implemented by forwarding I/O memory accesses to theactual device.
• PE/PM: Partial Emulation with Peripheral Modeling.
The firmware is emulated and peripheral interaction ishandled by mimicking peripherals behavior with dedicated scripts inside Avatar, which allows to conduct
experiments without having a physical device present.
• FE: Full Emulation. Both the firmware and its peripherals are fully emulated inside PANDA.

Like S2E, PANDA is based on the full system emulator QEMU and its plugin systems allows to hook various
events, such as physical accesses to memory, translation,and execution of translated blocks. Additionally, the base
implementation of PANDA provides already several analysis
plugins, whereas one of them is of special use for our heuristics: callstack_instr. This plugin allows to register
further callbacks on function calls and returns, and provides
information about the current call stack, which simplify the
implementation of several of our heuristics. However, as thisplugin would return wrong information when the state of theapplication is corrupted, we only use its on_call eventto detect the beginning of a function, while information offunction returns are retrieved by analyzing the executed blocks.

For configurations PE/MF, PE/PM, and FE we use asnapshot of the device’s state taken after initialization as
starting point for the emulation, as described in section VI.
The execution then continues inside the emulator where we
implemented the different heuristics presented in section V.
To estimate the performance impact imposed by each scenario,
we conducted experiments in which we selectively enable one
heuristic at a time.

In summary, we use PANDA to emulate the firmware andrely on its plugin system to obtain live feedback over the
execution of a partial or fully emulated firmware. All thiswhile Avatar orchestrates the execution and selectively redirect
execution and memory accesses to the physical device.
State caching: Avatar also plays another important rolein our system, allowing the tester to save and replay thedevice state after it is initialized. In fact, since the initialization
procedures of embedded systems usually set up all peripherals,
this phase impose a significant overhead every time the fuzzer
needs to restart the device. Moreover, these procedures involvea large amount of I/O operations that have a negative impact
on the execution of a partial emulator (that needs to forward
every access to the physical device). However, as long as theperipheral interaction only concerns stateless peripherals, thisoverhead can be removed by taking advantage of the ability ofemulators to execute from an initial snapshot. Therefore, oursystem can benefit from the ability of Avatar to save a snapshotof the device state after the its initialization is completed andreuse it to initialize the emulator.

VII.

In all our tests, the inputs to the vulnerable software areprovided on a simple textual protocol which is communicatedover a serial connection. On configurations NAT and PE/MF
we used the real device serial port, while in PE/PM and FE
the serial port of the device is either modeled or emulated, andthe input is provided to the emulator with a TCP connection
and written directly in the corresponding (emulated hardware)buffer.
B. Fuzzer Setup
We built our experiments on top of boofuzz [40], an
open source fuzzer and successor to Sulley [4], which is a
popular Python-based fuzzing framework. Boofuzz does not
only generate and send malformed inputs, but it also allows
to define target monitoring and reset hooks. In comparison to
its predecessor, it allows to fuzz over user-defined communication channels and provides ready implementations for both
serial and TCP-connections, making it an ideal match for ourevaluation purposes.

E XPERIMENTS

In this section we present a number of experiments we
conducted to test our heuristics both in a partial and in a fullemulation scenario. Our goal is to show that it is possible
to integrate such heuristics in a live fuzzing experiment, thus
providing fault detection to mitigate the lack of equivalentmechanisms in the embedded system’s platform and operating
system.

Obviously, inputs that triggers a fault introduces a larger
overhead than those that do not. Therefore, to remove thisbias and make sure we can compare the results of different
experiments, we instrumented the fuzzer to forcefully generate inputs which would trigger one of the inserted memory
corruption vulnerabilities with a given probability. We denote

However, our approach may introduce a non negligible
overhead on the performance of the system, effectively increasing the time required to perform the testing session. Therefore,
we also decided to measure the effects of our solution on the
fuzzing throughput under different setup configurations.

7 Note that we chose a Type-III device because this is the most challenging
case. The intuition is that if we can detect the silent memory corruptions on
a Type-III device, the heuristics are likewise suitable for Type-II and Type-I
devices.

10Format String

Stack-based buffer overflow

Heap-based buffer overflow

Double Free

Null Pointer Dereference

3

3

7

7

7

7
7
7
3
3
7
3

3
3
3
3
7
7
3

7
7
7
7
7
3
3

7
7
7
3
7
3
3

7
7
7
3
7
3
3

this probability as Pc and conducted experiments with Pc = 0,
Pc = 0.01, Pc = 0.05 and Pc = 0.1.
Furthermore, to better simulate a realistic fuzzing session,
we added a simple liveness check for monitoring purposes:
After every fuzz input, the fuzzer receives the response of thedevice and evaluate whether it matches the expected behavior.
When the received response differs from the expected one, or
when the connection times out, the fuzzer reports a crash andrestarts the target. The fuzzer uses boofuzz to power cycle
the physical device (NAT) or instructs the emulator to restartfrom the snapshot (PE/MF, PE/PM, and FE).

Liveness Check
Individual Heuristics:a) Call Stack Trackingb) Call Frame Trackingc) Stack Object Trackingd) Segment Trackinge) Format Specifier Trackingf) Heap Object Tracking
All

Note that we use the liveness checks during all experiments, even when all heuristics are enabled as there might
be crashes not detected by our heuristics. However, in our
experiments, with all heuristics enabled, the liveness checknever detected any corruption because the heuristics of our
PANDA plugins were able to detect faulty states at a earlier
stage, which in turn triggered an immediate restart of the target.
C. Results

TABLE V: Artificial vulnerabilities discovered by the different
heuristics.

In total, we conducted 100 fuzzing sessions lasting one
hour each. We monitored the number of inputs that were
processed by the target (Itot ), the number of times a corrupting
input was sent to a target (IC ), the amount of faults detectedby the liveness check (DL ), and the number of faults detectedby the heuristics (DH ). Additionally, we denote the numberof undetected faults as (DU ). As a result:
IC = DL + DH + DU ≈ Itot ∗ PC

resulted in a successful memory corruption, were not detectedby the fuzzer.
The impact of this is shown in Figure 1, which visualizes
the amount of corrupting inputs detected by the liveness check,by the heuristics (all8 or in isolation), or that remained undetected. A closer look at the graphs shows that the combined
heuristics (rightmost bar in each group) always successfully
detected all corruptions, while relying on liveness checks(leftmost bar) always left a large fraction of faults undetected.
Furthermore, segment tracking, as it can both detect format
string and stack based buffer overflow vulnerabilities, is superseding all detections formerly done by the liveness check.
This makes sense: when the device is in a strongly corrupted
state, even detectable by the liveness check, it is likely thatmemory accesses to unusual memory locations occured.

(1)

The interested reader can find the results of the individual
fuzzing sessions in Appendix A, which are analyzed thoroughthe rest of this section.
False Positives and False Negatives
Intuitively, the presented heuristics are not perfect and arelikely to yield false positives or negatives. Interestingly, weobserved only one case of false positives in the stack object
tracking when, due to over-approximation, two consecutive
memory writes to set two distinct but adjacent stack variables
were falsely tagged as a memory corruption.

Performance
Figure 2 shows the number of input values the fuzzed
target was able to process during one hour fuzz sessions withdifferent values for PC . As expected, partial emulation withmemory forwarding (PE/MF) is slowing down fuzz testingby more than one order of magnitude. This overhead is
introduced by the communication between the firmware andthe device peripherals, which results into frequent invocationsof the orchestration features of Avatar. The major part of thisoverhead is due to the low bandwidth connection between
Avatar and the physical device, which relies on a standard
JTAG debugger connected via USB. Surrogates [28] has shownthat this issue can be solved by using dedicated hardware,
which would enable partial emulation at near-realtime speed.

In general, we want to stress that false positives andnegative rates are highly target- and implementation-dependentand a comprehensive analysis of those are out of scope of
this work. Our goal is show the limitations of fault-detection
on embedded devices and the feasibility of using heuristics toovercome this problem, rather than evaluating the effectivenessof a specific implementation.
Fault Detection
Table V shows which type of corruptions could be detectedby the liveness check or by the individual heuristics. As we
expected, fuzzing without any fault detection mechanism islargely ineffective. The liveness check alone was only able todetect the stack-based buffer overflow and format string vulnerability because, as we already described in Section III, thesebugs result in the device hanging. All the other vulnerabilities,
although they were triggered correctly by the fuzzer and they

Looking at the individual heuristics, we can observe that
their overhead is negligible in the PE/MF scenario, where thebottleneck of forwarding of MMIO requests fully determines
8 Note that the ”Combined Heuristics” consist of heuristic c-f. Heuristic a
and b have been disabled as they are redundant with heuristic c.

11

FE

1
0

FE

No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics#Inputs [%]

2

PE/PM

PC = 0.1
15 PE/MF
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0

PE/PM

FE

No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics

PE/PM

No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics
No Heuristic
Call Stack Tracking
Call Frame Tracking
Stack Object Tracking
Segment Tracking
Format Specifier Tracking
Heap Object Tracking
Combined Heuristics#Inputs [%]PC = 0.05
10 PE/MF
9
8
7
6
5
4
3
2
1
0

#Inputs [%]PC = 0.01
3 PE/MF

Fig. 1: Corruption detection in emulation based scenarios with distinct probabilities for the occurence of corrupting inputs PC .
Corruptions detected by liveness checks
Corruptions detected by heuristics
Undetected corruptions
for PC . While the first can detect all classes of vulnerabilities
we inserted in its code, the second needs to rely on a livenesscheck that can only identify two of them.

NAT
PE/MF
PE/PM
FE
PE/MF'
PE/PM'
FE'

VIII.

103

102

0

0.05
PC

D ISCUSSION

The results of our experiments show that silent memory
corruptions pose a predominant challenge for fuzzing embedded systems, as the majority of fuzzing solutions are relying
on observable crashes. In particular, our tests emphasize threedifferent aspects:#Inputs

104

1) Relying only on liveness tests is a poor strategy.
Fuzzing embedded systems by relying solely on livenesstests for fault detection is a poor strategy that is very
likely to miss many vulnerabilities. Likewise, using only asingle heuristic at a time does not guarantee the detectionof more vulnerabilities. Intuitively, the highest potential
for corruption detection is reached by combining several
heuristics.

0.10

Fig. 2: Processed inputs during one hour long fuzzing sessions
with no heuristics (NAT, PE/MF, PE/PM, FE) and combined
heuristics (PE/MF’, PE/PM’, FE’) enabled.

2) While full emulation is the best strategy, emulators
are rarely available. Our experiments shows that, if it ispossible to fully emulate the firmware of the device under
test, then few selected heuristics can mitigate the lack of
fault detection mechanisms. This increases the accuracyof vulnerability discovery to what we now expect whenfuzzing desktop applications. While this may seem tosolve the problem, full emulation is still very difficult
to achieve. In particular, third party testers often lacksufficiently detailed knowledge of the hardware to implement a good emulator. Moreover, even with sufficient
documentation, implementing a full emulator requires aconsiderable amount of manual effort.

the speed of the fuzzing experiment. However, in scenario
PE/PM and FE, we can can observe a considerable slowdown(between x1.5 and x6) introduced by the heuristic analysis
code for PC = 0.
Another important observation is that fuzzing against afully emulated target is significantly faster than against thephysical device, as long the amount of detected corruptions
is low. This is due to three main factors. First, the fact thatthe communication over TCP allows a higher throughput thanthe one over a serial port. Second, by the fact that even if thefirmware is emulated, the emulator often has a much higher
clock speed than (low resource) embedded devices. Third, adetected corruption is tied to a forcefully reboot of the target,
which means that high PC s are resulting into significant timespent rebooting, rather than sending new inputs to the target.

3) Partial emulation can lead to accurate vulnerability
detection, with a significant performance impact.
When full emulation is not possible, partial emulation
can lead to the same benefits in term of accuracy, at thecost of a significant slowdown of roughly one order ofmagnitude. In particular, partial emulation with peripheral
modeling provides an interesting trade-off between vulnerability detection and fuzz speed throughput and does
neither require a sound emulator nor a physical device

However, the most important result of our experiments
is the fact that a firmware that is executed in PANDA (fullemulation) with combined heuristics enabled can be fuzzed
faster than the original embedded device under realistic values
12corruption
detected n+1h fuzz input

on
t.

t

C

oo

g

eb

in

R
liveness checkzz

nth fuzz input

Fu

Fu

ev

zz
i

ic

e

ng

St

St

ar

ar

t

t

1st fuzz input

D

Omitting real vulnerabilities from our tests may raise thequestion whether the insights of this work are applicable ina real world scenario. We believe this to be the case, as
our work points out a fundamental (and poorly understood)
limitation of the way we test embedded systems today, whichis independent from the position of the vulnerabilities in thecode and from the way such vulnerabilities are triggered. The
behavior we observe and analyze mainly depends on the platform (OS, hardware) and on how the program was compiled(which countermeasures). The only changes when migratingfrom an artificial test scenario to real world software are theobserved false positive and negative rates of the individual
heuristics, which are, as pointed out in Section VII-C, highly
implementation-specific.

corruption

reboot time(a)reboot timecorruption
detected

zz
C ing
on
t.

t

Fu

eb
oo
R

in
zz

2*n+1th fuzz input

Furthermore, while we initially describe several options,
we then focused our study on emulation-based approaches
to tackle silent memory corruptions. However, despite theincreasing amount of research dedicated to this topic, theability to fully emulate arbitrary firmware images is still an
open problem. Therefore, the solution we discuss in this paper
may not be applicable to all scenarios and all embedded system
devices. In conclusion, our work raises awareness about the
impact of silent memory corruptions on fuzzing, provides
the building blocks to solve this problem, and will hopefully
stimulate new research in this important direction.

2*nth fuzz input

Fu

D

ev
i

ce

g

St

St

ar
t

ar
t

1st fuzz input(b)Fig. 3: Example timelines of a fuzzing session with (a) livenesschecks, and (b) live detection without liveness checks.
to be present. Moreover, this setup allows to parallelize
fuzzing sessions, thus making fuzz-testing embedded devices more scalable.
A further advantage of our emulation-based approach isthat PANDA could also be used to record and replay the execution, which largely simplify the followup analysis to identify
the root cause and possible impact when a vulnerability isdetected.

X.

In this paper, we explored the challenges in fuzzing embedded devices. We classified the types of devices and the typesof memory corruption effects – which can either be observable
or silent. We pinpointed that one of the predominant issues are
silent memory corruptions. Silent memory corruptions are notspecific to embedded systems. However, desktop system have avariety of protection mechanisms which are turning the majority of silent corruptions into observable crashes, while thosemechanisms are often lacking on embedded devices. Hence,
we studied the effects of silent corruptions on different classesof embedded devices. We evaluated a variety of approaches
to mitigate this issue, and implemented a system based on
Avatar and PANDA, which implements six different live analysis heuristics for partial and full emulation. We conducted
experiments with this system which indicates that live analysis
can improve fuzzing of embedded systems. Finally, this workshows the importance of having good emulators. Therefore,
we hope that our results will stimulate more research in both
improving the construction of emulators for embedded devices
and in fuzzing such devices.

Another interesting observation is that liveness checks
often detect crashes due to a timeout, which significantly
slows down the fuzzing experiment. In an optimal setup, where
live heuristics are able to detect the majority of corruptions,the liveness check could be omitted, which could result into
a significant performance improvement. This is conceptuallydemonstrated in Figure 3 for a simplified scenario where
processing the liveness-check and processing the fuzz-input
is taking the same computation time.
Finally, while our results directly impact the performanceof fuzzing embedded systems, this work also applies to binary
symbolic execution on embedded devices firmware (e.g., as
described in [54]). An important problem of symbolic execution is the state explosion problem: with a sufficiently
complex program and symbolic input the symbolic execution
can rapidly reach a very large number of states which exhaust
resources or takes indefinitely long time to complete. Typically,
state exploration will continue until a terminating condition isfound. Therefore, if the corruptions are not promptly detected,
the symbolic execution could spend a significant amount oftime computing useless states.
IX.

C ONCLUSIONR EFERENCES
[1]

[2]

L IMITATIONS

The aim of this work is to study the problem of silent
memory corruptions in embedded systems and explore possible
solutions. Since other aspects of fuzzing, such as the generationof inputs to discover vulnerable paths, are not specific to ourdomain and do not affect our study, we relied on artificial bugs
to have a better control over the experiments.

[3]
[4]
[5]

13

M. Abadi, M. Budiu, U. Erlingsson, and J. Ligatti, “Control-flow
integrity,” in 12th ACM conference on Computer and communications
security (CCS), 2005.
V. Alimi, S. Vernois, and C. Rosenberger, “Analysis of embedded
applications by evolutionary fuzzing,” in International Conference on
High Performance Computing & Simulation (HPCS). IEEE, 2014.
M. Almgren, D. Balzarotti, J. Stijohann, and E. Zambon, “D5.3 reporton automated vulnerability discovery techniques,” 2014.
P. Amini and A. Portnoy, “Sulley fuzzing framework,” 2010.
Z. Basnight, J. Butts, J. Lopez, and T. Dube, “Firmware modification
attacks on programmable logic controllers,” International Journal of
Critical Infrastructure Protection, vol. 6, no. 2, 2013.

[6]
[7]

[8]

[9]
[10]

[11]
[12]

[13]

[14]

[15]
[16]

[17]

[18]

[19]
[20]

[21]
[22]

[23]

[24]

[25]

[26]

[27]

[28]

[29]

A. Beckus, “QEMU with an STM32 microcontroller implementation,”
2012, http://beckus.github.io/qemu stm32/.
D. L. Bruening, “Efficient, transparent, and comprehensive runtime code
manipulation,” Ph.D. dissertation, Massachusetts Institute of Technology, 2004.
D. D. Chen, M. Egele, M. Woo, and D. Brumley, “Towards AutomatedDynamic Analysis for Linux-based Embedded Firmware,” in Network
and Distributed System Security Symposium (NDSS), 2016.
X. Chen, A. Slowinska, and H. Bos, “Who allocated my memory?
Detecting custom memory allocators in C binaries,” in WCRE, 2013.
V. Chipounov, V. Kuznetsov, and G. Candea, “S2E: a platform for invivo multi-path analysis of software systems,” ACM SIGPLAN Notices,
vol. 46, no. 3, 2011.
Clang Users Manual, “Undefined behavior sanitizer.”
A. Costin, A. Zarras, and A. Francillon, “Automated Dynamic Firmware
Analysis at Scale: A Case Study on Embedded Web Interfaces,” in11th ACM symposium on Information, computer and communications
security (ASIACCS), 2016.
B. Dolan-Gavitt, J. Hodosh, P. Hulin, T. Leek, and R. Whelan, “Repeatable reverse engineering with PANDA,” in Proceedings of the 5th
Program Protection and Reverse Engineering Workshop. ACM, 2015.
B. Dolan-Gavitt, P. Hulin, E. Kirda, T. Leek, A. Mambretti, W. Robertson, F. Ulrich, and R. Whelan, “LAVA: Large-scale automated vulnerability addition,” in 37th IEEE Symposium on Security and Privacy (SP),2016.
M. Eddington, “Peach fuzzing platform,” Peach Fuzzer, 2011.
I. D. Foster, A. Prudhomme, K. Koscher, and S. Savage, “Fast andVulnerable: A Story of Telematic Failures,” in 9th USENIX Workshop
on Offensive Technologies (WOOT), 2015.
V. Ganesh, T. Leek, and M. Rinard, “Taint-based directed whitebox
fuzzing,” in 31st International Conference on Software Engineering.
IEEE, 2009.
H. Gascon, C. Wressnegger, F. Yamaguchi, D. Arp, and K. Rieck,“Pulsar: Stateful Black-Box Fuzzing of Proprietary Network Protocols,”
in Securecomm, 2015.
P. Godefroid, M. Y. Levin, and D. Molnar, “SAGE: whitebox fuzzing
for security testing,” Queue, vol. 10, no. 1, 2012.
I. Haller, A. Slowinska, M. Neugschwandtner, and H. Bos, “Dowsing
for Overflows: A Guided Fuzzer to Find Buffer Boundary Violations,”
in USENIX Security Symposium, 2013.
S. Heath, Embedded systems design. Newnes, 2002.
W. Jin, C. Cohen, J. Gennari, C. Hines, S. Chaki, A. Gurfinkel,
J. Havrilla, and P. Narasimhan, “Recovering C++ objects from binaries
using inter-procedural data-flow analysis,” in Proceedings of the ACM
SIGPLAN on Program Protection and Reverse Engineering Workshop(PPREW), 2014.
N. Kamel and J.-L. Lanet, “Analysis of HTTP protocol implementationin smart card embedded web server,” International Journal of Information and Network Security, vol. 2, no. 5, 2013.
M. Kammerstetter, D. Burian, and W. Kastner, “Embedded Security
Testing with Peripheral Device Caching and Runtime Program State
Approximation,” in 10th International Conference on Emerging Security
Information, Systems and Technologies (SECUWARE), 2016.
M. Kammerstetter, C. Platzer, and W. Kastner, “PROSPECT: Peripheral
Proxying Supported Embedded Code Testing,” in 9th ACM symposiumon Information, computer and communications security (ASIACCS),
2014.
Keen Security Lab, “Car hacking research: Remote attack tesla motors,”
2016, http://keenlab.tencent.com/en/2016/09/19/Keen-Security-Lab-ofTencent-Car-Hacking-Research-Remote-Attack-to-Tesla-Cars/.
K. Koscher, A. Czeskis, F. Roesner, S. Patel, T. Kohno, S. Checkoway,
D. McCoy, B. Kantor, D. Anderson, H. Shacham et al., “Experimental
security analysis of a modern automobile,” in 30th IEEE Symposium
on Security and Privacy (SP), 2010.
K. Koscher, T. Kohno, and D. Molnar, “SURROGATES: enabling nearreal-time dynamic analyses of embedded systems,” in 9th USENIX
Workshop on Offensive Technologies (WOOT), 2015.
H. Lee, K. Choi, K. Chung, J. Kim, and K. Yim, “Fuzzing CAN Packets

[30]
[31]

[32]
[33]

[34]
[35]

[36]
[37]

[38]
[39]

[40]
[41]
[42]

[43]

[44]

[45]
[46]
[47]

[48]

[49]

[50]
[51]

[52]

[53]
[54]

[55]

14

into Automobiles,” in 29th International Conference on Advanced
Information Networking and Applications (AINA). IEEE, 2015.
U. Lindqvist and P. G. Neumann, “The future of the internet of things,”
Communications of the ACM, vol. 60, no. 2, 2017.
C.-K. Luk, R. Cohn, R. Muth, H. Patil, A. Klauser, G. Lowney,
S. Wallace, V. J. Reddi, and K. Hazelwood, “Pin: building customized
program analysis tools with dynamic instrumentation,” in ACM SIGPLAN notices, vol. 40, 2005.
D. McMillen, “Security attacks on industrial control systems,” IBM
Security, Tech. Rep., 2015.
B. P. Miller, L. Fredriksen, and B. So, “An empirical study of thereliability of UNIX utilities,” Communications of the ACM, vol. 33,
no. 12, 1990.
C. Miller and C. Valasek, “Remote exploitation of an unaltered passenger vehicle,” Black Hat USA, vol. 2015, 2015.
C. Mulliner, N. Golde, and J.-P. Seifert, “SMS of Death: From Analyzing to Attacking Mobile Phones on a Large Scale,” in USENIX Security
Symposium, 2011.
NCC Group, “TriforceAFL,” 2017, https://github.com/nccgroup/
TriforceAFL.
N. Nethercote and J. Seward, “Valgrind: a framework for heavyweight
dynamic binary instrumentation,” in ACM SIGPLAN notices, vol. 42,
no. 6, 2007.
A. Nordrum, “The internet of fewer things [news],” IEEE Spectrum,
vol. 53, no. 10, 2016.
D. Papp, Z. Ma, and L. Buttyan, “Embedded systems security: Threats,vulnerabilities, and attack taxonomy,” in 13th Annual Conference on
Privacy, Security and Trust (PST). IEEE, 2015.
J. Pereyda, “boofuzz,” 2016, https://github.com/jtpereyda/boofuzz.
J. R., “Processor tracing,” Intel Blog, September 2013, https://
software.intel.com/en-us/blogs/2013/09/18/processor-tracing.
S. Rawat, V. Jain, A. Kumar, L. Cojocar, C. Giuffrida, and H. Bos,
“VUzzer: Application-aware Evolutionary Fuzzing,” in Network andDistributed System Security Symposium (NDSS), 2017.
K. Serebryany, D. Bruening, A. Potapenko, and D. Vyukov, “AddressSanitizer: A Fast Address Sanity Checker,” in USENIX Annual
Technical Conference, 2012.
K. Serebryany and T. Iskhodzhanov, “ThreadSanitizer: data race detection in practice,” in Proceedings of the 3rd Workshop on Binary
Instrumentation and Applications (WBIA). ACM, 2009.
K. Serebryany, “Sanitize, Fuzz, and Harden Your C++ Code,” 2016.
T. Shellphish, “Cyber Grand Shellphish,” Phrack Papers, 2017.
A. Slowinska, T. Stancescu, and H. Bos, “Howard: A Dynamic Excavator for Reverse Engineering Data Structures,” in Network andDistributed System Security Symposium (NDSS), 2011.
E. Stepanov and K. Serebryany, “MemorySanitizer: fast detector ofuninitialized memory use in C++,” in International Symposium on Code
Generation and Optimization (CGO). IEEE, 2015.
N. Stephens, J. Grosen, C. Salls, A. Dutcher, R. Wang, J. Corbetta,
Y. Shoshitaishvili, C. Kruegel, and G. Vigna, “Driller: Augmenting
Fuzzing Through Selective Symbolic Execution,” in Network and Distributed System Security Symposium (NDSS), 2016.
M. Sutton, A. Greene, and P. Amini, Fuzzing: brute force vulnerability
discovery. Pearson Education, 2007.
F. Van Den Broek, B. Hond, and A. C. Torres, “Security testing of GSM
implementations,” in International Symposium on Engineering Secure
Software and Systems. Springer, 2014.
V. Van der Veen, L. Cavallaro, H. Bos et al., “Memory errors: thepast, the present, and the future,” in International Workshop on Recent
Advances in Intrusion Detection. Springer, 2012.
Vendicator, Stack Shield, “A stack smashing technique protection tool
for Linux,” 2000, http://www.angelfire.com/sk/stackshield/info.html.
J. Zaddach, L. Bruno, A. Francillon, and D. Balzarotti, “AVATAR:
A Framework to Support Dynamic Security Analysis of Embedded
Systems’ Firmwares,” in Network and Distributed System Security
Symposium (NDSS), 2014.
M. Zalewski, “American fuzzy lop,” 2014, http://lcamtuf.coredump.cx/
afl/.

A PPENDIX

Itot

–

14269

–

166

–

9997

–

53390

–
–
–
–
–
–
–

–
–
–
–
–
–
–

–
–
–
–
–
–
–

166
166
166
166
166
166
166

–
–
–
–
–
–
–

8467
4213
8233
9390
9257
9321
7921

–
–
–
–
–
–
–

30418
5400
28318
41101
38158
39654
25557

No Heuristic
Individual Heuristics:a) Call Stack Trackingb) Call Frame Trackingc) Stack Object Trackingd) Segment Trackinge) Format Specifier Trackingf) Heap Object Tracking
Heuristics c,d,e,f

43%

12681

100%

165

35%

2241

40%

37569

–
–
–
–
–
–
–

–
–
–
–
–
–
–

50%
0%
50%
50%
33%
0%
100%

165
166
165
166
163
166
166

34%
37%
52%
64%
23%
76%
100%

2386
2512
1894
2372
2382
1794
1985

42%
38%
42%
61%
38%
79%
100%

25059
18289
23213
31768
31123
28564
19387

No Heuristic
Individual Heuristics:a) Call Stack Trackingb) Call Frame Trackingc) Stack Object Trackingd) Segment Trackinge) Format Specifier Trackingf) Heap Object Tracking
Heuristics c,d,e,f

39%

9663

14%

163

36%

1087

38%

15985

–
–
–
–
–
–
–

–
–
–
–
–
–
–

77%
55%
33%
66%
33%
87%
100%

159
164
162
165
164
162
165

30%
36%
45%
68%
50%
91%
100%

1143
874
905
987
1192
852
863

39%
37%
40%
62%
35%
78%
100%

15604
15212
15537
9688
15909
7772
5813

No Heuristic
Individual Heuristics:a) Call Stack Trackingb) Call Frame Trackingc) Stack Object Trackingd) Segment Trackinge) Format Specifier Trackingf) Heap Object Tracking
Heuristics c,d,e,f

40%

7067

41%

158

38%

956

38%

8267

–
–
–
–
–
–
–

–
–
–
–
–
–
–

33%
50%
33%
66%
27%
64%
100%

161
161
162
164
159
161
165

36%
43%
43%
81%
32%
84%
100%

813
811
850
868
908
779
793

39%
39%
36%
64%
36%
80%
100%

7773
7773
8122
5048
8612
4387
3727

Pc =0

No Heuristic
Individual Heuristics:a) Call Stack Trackingb) Call Frame Trackingc) Stack Object Trackingd) Segment Trackinge) Format Specifier Trackingf) Heap Object Tracking
Heuristics c,d,e,f

FE
Dtot

Pc =0.01

PE/PM
Dtot
Itot

Pc =0.05

PE/MF
Dtot Itot

Itot

Pc =0.1

NAT
Dtot

Appendix A: Number of inputs sent to the target during a fuzzing session Itot and vulnerability detection efficacy Dtot for different
corruption probabilities PC in the four target setups Native (NAT), Partial Emulation with Memory Forwarding (PE/MF), Partial
Emulation with Peripheral Modeling (PE/PM) and Full Emulation (FE).

15