Finding Taint-Style Vulnerabilities in Linux-based Embedded Firmware with SSE-based Alias Analysis

 
CONTINUE READING
Finding Taint-Style Vulnerabilities in Linux-based Embedded Firmware with
                                                                      SSE-based Alias Analysis

                                                     Kai Cheng1,2 , Tao Liu3 , Le Guan4 , Peng Liu3 , Hong Li1 , Hongsong Zhu1 , Limin Sun1

                                                                        1 Institute of Information Engineering, Chinese Academy of Sciences, China
                                                                       2 School
                                                                              of Cyber Security, University of Chinese Academy of Sciences, China
                                                                                           3 The Pennsylvania State University
arXiv:2109.12209v1 [cs.CR] 24 Sep 2021

                                                                                                  4 University of Georgia

                                                    chengkai@iie.ac.cn, tul459@psu.edu, leguan@uga.edu, pxl20@psu.edu, {lihong, zhuhongsong, sunlimin}@iie.ac.cn

                                                                   Abstract                                       position in home and small business networks and therefore,
                                                                                                                  they must operate securely. The addition of various IoT de-
                                         Although the importance of using static analysis to detect
                                                                                                                  vices (e.g., thermostat, smart light, smart lock, smart alarm
                                         taint-style vulnerabilities in Linux-based embedded firmware
                                                                                                                  system) to home and small business networks makes the posi-
                                         is widely recognized, existing approaches are plagued by
                                                                                                                  tion held by Linux-based embedded firmware even more vital.
                                         three major limitations. (a) Approaches based on symbolic
                                                                                                                  Unfortunately, Linux-based embedded firmware still suffers
                                         execution may miss alias information and therefore suffer
                                                                                                                  from a number of vulnerabilities in the real world.
                                         from a high false-negative rate. (b) Approaches based on VSA
                                                                                                                     To test the real-world Linux-based embedded firmware,
                                         (value set analysis) often provide an over-approximate pointer
                                                                                                                  static analysis [13,20,35,38] and dynamic analysis [28,43,45]
                                         range. As a result, many false positives could be produced.
                                                                                                                  are two basic approaches. Recently, substantial progress has
                                         (c) Existing work for detecting taint-style vulnerability does
                                                                                                                  been made on automated dynamic analysis of embedded
                                         not consider indirect call resolution, whereas indirect calls
                                                                                                                  firmware. For example, Firmadyne [9] has successfully emu-
                                         are frequently used in Internet-facing embedded devices. As
                                                                                                                  lated the execution of over 1,900 firmware images. However,
                                         a result, many false negatives could be produced.
                                                                                                                  existing dynamic analysis techniques are still quite limited.
                                            In this work, we propose a precise demand-driven flow-,
                                                                                                                  For example, due to reasons such as complex environment
                                         context- and field-sensitive alias analysis approach. Based
                                                                                                                  dependencies of the firmware, Firmadyne [9] was only able
                                         on this new approach, we also design a novel indirect call
                                                                                                                  to emulate 23% of the collected 8,591 firmware images.
                                         resolution scheme. Combined with sanitization rule checking,
                                         our solution discovers taint-style vulnerabilities by static taint          Being complementary to dynamic analysis, static analysis
                                         analysis. We implemented our idea with a prototype called                of embedded firmware has also been attracting an increasing
                                         EmTaint and evaluated it against 35 real-world embedded                  interest in the research community. A unique merit of static
                                         firmware samples from six popular vendors. EmTaint discov-               analysis is that in many cases it can simply ignore the com-
                                         ered at least 192 bugs, including 41 n-day bugs and 151 0-day            plex environment dependencies of the firmware while still
                                         bugs. At least 115 CVE/PSV numbers have been allocated                   being able to find many vulnerabilities. In this work, we pro-
                                         from a subset of the reported vulnerabilities at the time of writ-       pose a new static binary analysis approach to find taint-style
                                         ing. Compared to state-of-the-art tools such as KARONTE                  vulnerabilities in COTS (commercial off-the-shelf) Linux-
                                         and SaTC, EmTaint found significantly more bugs on the same              based embedded firmware. (Because the source code is not
                                         dataset in less time.                                                    available, note that we cannot use any source-code-analyzing
                                                                                                                  tools.) The taint-style vulnerability is a class of vulnerabilities
                                                                                                                  that share the same underlying theme rooted in information
                                         1   Introduction                                                         flow analysis [42]. More specifically, attacker-controlled data
                                         With the emerging of the Internet of Things (IoT) technolo-              are passed from an input source to a security-sensitive sink
                                         gies, Linux-based embedded firmware is nowadays playing an               without proper check or sanitization. This vulnerability class
                                         increasingly important role. For example, almost all the main-           captures the root cause for many software defects and is often
                                         stream wireless routers are running Linux-based embedded                 manifested into many common vulnerability types including
                                         firmware under the hood. Since wireless routers are often the            buffer overflow and format string vulnerability.
                                         perimeter defense that separates the information processing                 Given an embedded firmware image, the standard proce-
                                         and storing requirements (e.g., laptops, smartphones, database           dure for detecting taint-style vulnerability consists of four
                                         servers) in an apartment, a house or a small business office             steps. (1) Recover the inter-procedural control flow graph
                                         from the Internet, the embedded firmware holds a privileged              (ICFG) of the program. (2) Identify attacker-controlled

                                                                                                              1
sources and security-sensitive sinks. (3) Find a path where the          given a variable at a particular program point, our approach
taint is propagated from the source to the sink. (4) Check the           finds all its aliases along the path by following the use-define
constraints of the tainted data at sinks. If not constrained, an         and define-use information forwards and backwards. To exe-
alert about the vulnerability is raised. An ideal solution should        cute this design, we borrow ideas from access path [12] which
achieve high accuracy in all the steps. When high accuracy is            represents memory locations by how they are accessed from
not achieved, the analysis usually suffers from two bad conse-           an initial variable. Since access path is designed for analyzing
quences: (1) a good portion of the paths found in Step 3 hold            source code, we specifically accommodate it for binary-only
one or more bogus links: such paths would produce false pos-             analyses by incorporating our new contributions. Specifically,
itives; (2) due to missing links, some vulnerability-revealing           we propose a new notation called structured symbolic expres-
paths are not identified: this produces false negatives.                 sion (SSE) to facilitate the idea. SSE encodes the provenance
                                                                         of a variable using symbolic expression. However, different
Problems with existing work. As we will discuss in detail
                                                                         from symbolic values used in symbolic execution, it not only
shortly in Section 2, we found that the existing static binary
                                                                         encodes arithmetic operations, but also considers memory
analysis works on taint-style vulnerability hunting are still
                                                                         operations such as load and store. As such, multi-level pointer
very limited in achieving high accuracy, mostly due to the
                                                                         dereferences (e.g., x.y.z) can be expressed in an SSE as a
challenges they face in the aforementioned Step 1 and Step 3.
                                                                         concatenation of symbolic load/store/arithmetic statements
Their main limitations can be summarized as follows.
                                                                         with base and offset information. Our algorithm starts from
   (1) With two alias pointers being dereferenced to different           an interesting SSE-encoded variable. When we find a new use
addresses, works (e.g., KARONTE [35]) based on symbolic                  or define for any field in the SSE, a new alias SSE is gener-
execution may suffer from high false negative rate.                      ated by replacing that field. The new alias is then used in a
   (2) Works (e.g., Loongchecker [14]) based on VSA (value               new round of alias searching until a fixpoint is reached. SEE
set analysis) often provide an over-approximate pointer range.           tracking can easily traverse across function boundaries, mak-
As a result, many false positives could be produced.                     ing inter-procedural analysis straightforward. As such, our
   (3) Existing works for detecting taint-style vulnerability            approach is flow-, context-, and field-sensitive. This unique
do not consider indirect call resolution, partially due to the           design brings about the following desirable benefits. First,
associated difficulties. However, based on our study, indirect           alias searching can start at any program location without
calls are frequently used in Internet-facing embedded devices            losing alias information before it. Therefore, it finds a com-
(see Section 5.2). By missing many taint propagation links,              prehensive list of aliases for a given variable, regardless of the
many false negatives could be produced.                                  specified starting point. In this way, we do not need to rely on
Our solution. We argue that inadequate alias analysis and                expert input to start analysis from a firmware-specific point
lack of indirect call resolution are two technical issues for            near the sink [13, 35]. Rather, we can start from commonly
cost-effective embedded firmware analysis. Crossing these                used taint sources such as the recv function. Second, our ap-
barriers will unleash the capabilities of static analysis to find        proach does not need to analyze the program as a whole like
taint-style vulnerabilities. Note that the results of alias analy-       VSA. It focuses on a particular variable and only computes
sis can be directly used to recover indirect calls. Therefore, in        over relevant instructions. This on-demand feature allows for
this work, we propose a precise demand-driven flow-, context-            more efficient and scalable analysis for complex programs.
and field-sensitive alias analysis mechanism. We further lever-             We have implemented the proposed SSE-based demand-
age it to find the alias relationship between the indirect call          driven alias analysis and indirect call resolution based on
targets and address-taken functions, yielding an accurate set            Claripy. They were integrated into a prototype system called
of indirect-call targets. This is fundamentally different from           EmTaint to find taint-style vulnerabilities in Linux-based em-
existing type matching based approaches which suffer from                bedded firmware. We evaluated EmTaint with 35 real-world
high false-positives. Combined with sanitization rule check-             firmware samples. The result shows that EmTaint quickly
ing, our solution finds more bugs in less time with fewer false          produces a large number of alerts. Regarding indirect call res-
positives.                                                               olution, EmTaint recovered 12,022 of 12,446 (96.6%) indirect
   The proposed SSE-based alias analysis overcomes the                   calls in an hour. Regarding vulnerability discovery, EmTaint
drawbacks of existing VSA-based or symbolic-execution-                   discovered at least 192 bugs, including 41 n-day bugs and 151
based approaches in two aspects. First, we extend the sym-               0-day bugs. Each sample takes an average of 3 minutes. We
bolic values used in symbolic execution to incorporate                   have reported 151 0-day bugs to the relevant manufacturers
abstract memory operations, eliminating the need for the                 for responsible disclosure. 115 of which are confirmed by
information-losing concretization process. Second, our analy-            CVE/PSV at the time of writing. We also conducted a com-
sis is demand-driven in a sense that only interesting variables          parison with KARONTE [35] and SaTC [11]. The results of
need to be traced, avoiding the holistic analysis used in VSA            the comparison shown that EmTaint can find more bugs in
(while localized VSA is possible, it may miss many useful                less time.
information and lead to bogus links in general). Concretely,             Contributions. In summary, we make the following contri-

                                                                     2
Tainted object             Pointer alias            Execution flow               functions that are indirectly invoked. Given an embedded
                                                                            Multi-level         program, the standard procedure for detecting taint-style vul-
 source(x.y)                      a.b.c = x.y
                                                                            pointer alias       nerability consists of four steps. We list each step and discuss
                x.y                                    x.y      a.b.c                           the key requirements for them as follows.
                                                                        strcpy(d, a.b.c)           1) Recover the inter-procedural control flow graph (ICFG)
                      Inter-procedural alias
                                void fun(p)
                                                                                                of the program. ICFG is the union of all functions’ CFG by
    sink(p)                     void (*fun_ptr)(void*) = &fun                  Taint            connecting calling edges. Compared with direct calls, recov-
                                (*fun_ptr)(d)                               propagation         ering indirect calls is considered challenging in static analysis
                p         d                                             d
                                                                                                since the calling target is only determined at run-time. Recov-
Figure 1: Taint-style vulnerabilities and how they can be                                       ering the ICFG is necessary for inter-procedural alias analysis.
identified in general.                                                                             2) Identify attacker-controlled sources and security-
butions in this paper:                                                                          sensitive sinks. Both the source and sink are typically rec-
• We propose a new demand-driven alias analysis technique                                       ognized based on semantics of the relevant functions. For
  based on structured symbolic expressions. With structured                                     example, the function recv receives data from the network
  symbolic expressions, our approach achieves flow-, context-                                   and its second buffer pointer should be tainted. The function
  and field-sensitive alias analysis simultaneously.                                            system invokes a shell to execute arbitrary commands. There-
                                                                                                fore, its parameter is considered as a sink. This step requires
• We observe that resolving indirect calls is critical for finding
                                                                                                the accurate identification of the corresponding functions.
  taint-style vulnerabilities in embedded firmware. Therefore,
                                                                                                   3) In the recovered ICFG, find a path where the taint is prop-
  we propose a novel indirect call resolution scheme. Our
                                                                                                agated from the source to the sink. This step is the most crucial
  approach directly finds the alias relationship between the
                                                                                                but challenging. As shown in Figure 1, it should cross three
  indirect call targets and address-taken functions with high
                                                                                                barriers – multi-level pointer alias analysis, inter-procedural
  confidence.
                                                                                                alias analysis, and taint propagation analysis.
• We have implemented the proposed system and evaluated
                                                                                                   4) Check the constraints of the tainted data at sinks. If not
  it with extensive experiments. Our tool have identified 151
                                                                                                constrained, an alert about the vulnerability is raised.
  0-day vulnerabilities in 35 embedded firmware samples. At
                                                                                                   An ideal solution should achieve high accuracy in all the
  least 115 CVEs/PSVs have been allocated from a subset of
                                                                                                steps. When high accuracy is not achieved, the analysis usu-
  the reported bugs. For continuing research, we will open
                                                                                                ally suffers from two bad consequences: (1) a good portion of
  source our tool.
                                                                                                the paths found in Step 3 hold one or more bogus links: such
2     Background and Motivation                                                                 paths do not reveal real vulnerabilities, they produce false pos-
                                                                                                itives; (2) due to missing links, some vulnerability-revealing
2.1      Taint-style Vulnerability                                                              paths are not identified: this produces false negatives.
The taint-style vulnerability is a class of vulnerabilities in
which attacker-controlled data are passed from an input source                                  2.2     Limitations of Existing Techniques
to a security-sensitive sink without sanitization. Figure 1 il-                                 Unfortunately, existing works on taint-style vulnerability hunt-
lustrates the data flow of a representative taint-style vulner-                                 ing are still very limited in achieving high accuracy, mostly
ability. Attacker-controlled data enter the program via the                                     due to the challenges they face in the aforementioned Step 1
input function source() and the input buffer is pointed to                                      and Step 3.
by x.y. Since this buffer is controlled by the attacker, we
mark it as tainted. Then, through an assignment statement, the                                  2.2.1   Alias Issues
tainted pointer makes an alias a.b.c, which is also a multi-                                    Works based on symbolic execution may suffer from high
level pointer. Next, a strcpy function is used to copy the                                      false negative rate. Symbolic execution has been widely
data pointed to by a.b.c to another buffer pointed to by d.                                     used in taint analysis [17, 20, 34, 35]. Concretely, existing
Obviously, the pointer d should be tainted because it points                                    works taint the input and propagate the tainted data via for-
to the memory containting the same data as the taint source.                                    ward symbolic execution. An alert is raised when a feasible
Note that strcpy is not only used to propagate a taint but                                      path is found to have the taint in any of the sinks without saniti-
also a notable characteristic of the vulnerability. An indirect                                 zation. Apart from the well-recognized problems in symbolic
call is then invoked with d as an argument. Inside the indirect                                 execution (path explosion and timing-consuming constraint
call, d makes an alias p. Finally, p is used in a sensitive sink                                solving), the accuracy of these approaches depend on the con-
function.                                                                                       cretization strategy. Specifically, when a symbolic address
   As shown above, the data-flow of a real-world taint-style                                    is accessed, the symbolic execution engine has to concretize
vulnerability is quite complex. The taint could be propagated                                   the symbol to allow for continuous execution. Ideally, two
through multi-level pointer aliases and data movement func-                                     alias symbolic addresses should be concretized into the same
tions. Sometimes the propagation can even traverse through                                      concrete address. However, it is a non-trivial task to maintain

                                                                                            3
this information in symbolic execution. With two alias point-                           Firmware                  Reports
ers being dereferenced to different addresses, taints cannot
propagate, causing a high false negative rate.
   To mitigate path explosion issues, existing works [34, 35]                                                   Taint-style
                                                                                        Firmware            Vulnerability Check
use under-constrained symbolic execution [31], in which sym-                          Preprocessing
                                                                                                               Vulnerability
bolic execution starts from arbitrary functions, instead of the                                                Check Module
main entry point. In this way, the distance from the source to
the sink is reduced and thus the chance for indirect calls along                                             Taint Initialization
                                                                                        SSE-based             and Propagation
the path is decreased. However, this approach needs experts                           Demand-driven
                                                                                      Alias Analysis
to manually specify firmware specific source functions.                                                        Indirect Call
                                                                                    EmTaint                     Resolution
Works based on VSA often provide an over-approximate
pointer range. VSA has been widely used in many static                                     Figure 2: EmTaint Overview
analysis applications, ranging from recovering binary prop-
                                                                         a store-based approach to represent runtime memory loca-
erties [6, 7] to identifying binary vulnerabilities [14, 32].
                                                                         tions, which cannot accurately track indirect memory accesses
The high-level idea of VSA is to maintain a tight over-
                                                                         (i.e., x.y.z) whose address cannot be unambiguously repre-
approximation of the set of numeric values or addresses that
                                                                         sented. For example, when reading from an unknown location,
each register or variable might hold at a given program point.
                                                                         VSA conservatively models the response as any value (i.e., >),
By providing information about the register values that ap-
                                                                         while symbolic execution models the response as an uncon-
pear in an indirect memory operand, VSA can determine the
                                                                         strained symbolic value. Both introduce inaccuracy when this
addresses that are potentially accessed. This is particularly
                                                                         value is later used as an address (e.g., symbolic execution
useful for alias analysis which aims to determine whether two
                                                                         must concretize it using certain strategies). To address this
pointers refer to the same address [37]. However, when VSA
                                                                         problem, the store-less approach can be used, in which mem-
computes the pointer ranges by following a set of rules (e.g.,
                                                                         ory locations (or pointers) are represented by how they are
add operation), it can only provide an over-approximate range.
                                                                         accessed from an initial variable (i.e., provenance), instead
Alternatively, when VSA has no clue to constrain a pointer,
                                                                         of a value (e.g., a-Locs in VSA and symbolic or concrete val-
the pointer becomes even untrackable. This problem becomes
                                                                         ues in symbolic execution). This idea was initially proposed
noticeable when it comes to complex programs. Specifically,
                                                                         in access path to find aliases in source code [12]. We are
bogus dependency renders it expensive and unpractical for
                                                                         inspired by it and incorporate into it our new contributions
real-world programs [44].
                                                                         to handle binaries. The key contribution of this work is the
2.2.2   Indirect Call Issues                                             newly proposed structured symbolic expression (SSE), which
Existing works for detecting taint-style vulnerability do                works at instruction (or IR) level and can represent arbitrary
not consider indirect call resolution, partially due to the              computations, including how a pointer accesses a memory
associated difficulties. Most related works focus on resolv-             location. Therefore, it does not rely on the source code in-
ing indirect jumps (e.g., switch statements in C) or simple              formation and directly handles multi-level pointers. Using
indirect calls [16, 18, 27, 36], in which no cross-function refer-       SSE, we can derive new aliases of a variable of interests by
ences occurs. None of them can effectively handle complex                iteratively traversing the define-use and use-define chain in
situations such as indirect calls implemented by callbacks or            a binary. At each define or use point, a new alias is derived,
function tables. For example, X-Force [30] uses concrete exe-            represented by another SSE that encodes the corresponding
cution to force binary execution without requiring valid inputs          provenance information.
or proper environment. However, it is expensive and intro-
                                                                         3.2    Architecture Overview
duces many infeasible paths. TypeArmor [40] infers the func-
tion type information from binary and uses argument number               EmTaint takes an embedded firmware image as input and
to match callers and callees. However, it suffers from a high            reports potential taint-style vulnerabilities through static anal-
false-positive rate because many functions may share the same            ysis. As shown in Figure 2, the proposed system is comprised
type. To refine the indirect-call targets, TypeDive [26] lever-          of four major components.
ages multi-layer type information. However, this approach                Firmware preprocessing. The firmware preprocessing mod-
requires the source code.                                                ule utilizes Binwalk [25] to decompress and extract binaries
3 Overview                                                               from firmware and leverages the state-of-the-art reverse engi-
                                                                         neering tools to extract code and data from the binaries and
3.1 Key Insight                                                          converts them into intermediate representation (IR). It also
Both symbolic execution based and VSA based approaches                   builds the control flow graph (CFG) and a partial call graph
face challenges to find accurate alias information for real-             (CG) that facilitate static analysis (Section 4.1).
world programs. The root cause is that both techniques use               SSE-based demand-driven alias analysis. The SSE-based

                                                                     4
Table 1: Recursive definition of SSE.
alias analysis engine is a novel flow-, context- and field-
                                                                                        expr    ::=   expr ♦b expr | ♦u expr | var
sensitive alias analysis tool (Section 4.2). It is demand-driven,                        ♦b     ::=       +, −, ∗, /, , , ...
therefore it scales well for complex programs. This compo-                               ♦u     ::=             ∼ , !, ...
nent lays the foundation for efficient taint tracking and indirect                       var    ::=        τreg | τmem | τval
call resolution mechanisms.                                                              τreg   ::=                 ri
                                                                                         τval   ::=           {Integer}
Indirect call resolution. Leveraging the data dependence in-                            τmem    ::=    load(expr) | store(expr)
formation provided by the proposed SSE-based alias analysis                the data segment and collects all the immediate values as can-
engine, we further design an indirect call resolution algorithm            didates. To confirm a function pointer, a candidate must meet
that checks the data dependence between the referenced func-               two conditions. One is that its value has to fall into a code
tion pointers (or function table pointers) and target pointers             segment, and the other is that the instruction sequence where
in indirect callsites (see Section 4.3). Since indirect call res-          the candidate points to must match the signature of a function
olution allows tainted variables to traverse more function                 prologue. After obtaining all the potential functions, EmTaint
boundaries, it positively impacts the comprehensiveness of                 loads the binary with APIs provided by angr [39], converts
the data flow analysis in EmTaint and more importantly sub-                them into VEX IR by using pyvex [4] and generates control
stantially improves the discovery of taint-style vulnerabilities.          flow graphs (CFG) to facilitate further analyses. Note that
Given the pervasiveness of indirect calls in firmware, the indi-           at this stage, the call graph is incomplete due to the missing
rect call resolution module is one of the key enablers for the             calling relationships of indirect calls.
effectiveness of the proposed solution.
Taint-style vulnerability check. The taint-style vulnerability
                                                                           4.2    SSE-based Demand-driven Alias Analysis
check module is responsible for identifying the potential taint-           In this section, we describe the proposed demand-driven alias
style vulnerabilities. Specifically, it firstly taints the variables       analysis. We start with the definition of structured symbolic
at taint sources that are controlled by attackers (e.g., the recv          expressions (SSE), which is the basis of our approach. Then,
function). Then, it captures taint propagation via the demand-             we explain how to use SSE to find aliases of a given vari-
driven alias analysis module and taint propagation functions               able/pointer within a basic block. We also show some running
(e.g., strcpy). Finally, at security-sensitive sinks (e.g., the            examples. Finally, we illustrate how to implement intra- and
system function), if the corresponding variable is tainted and             inter-procedural alias analyses.
unconstrained, we mark the source-to-sink path as potentially              4.2.1 Definition of SSE
exploitable (see Section 4.4).                                             SSE is a new notation that uses abstract memory model to
                                                                           represent aliases of a variable by encoding the nested prove-
4     System Design and Implementation                                     nance information at relevant program points. In Table 1, we
In this section, we illustrate the detailed design and implemen-           recursively defines an SSE expression. Note that since our im-
tation for each component. The proposed analysis is based                  plementation is based VEX IR, our SSE definition is deeply
on the VEX intermediate representation (IR) [29]. The VEX                  influenced by its design, in particular the basic statements
IR is a popular IR widely used in many program analysis                    and memory model. An SSE could be any basic variable or
tools, including Valgrind [29] and angr [39]. It is architecture-          the results of a binary/unary arithmetic operation over basic
agnostic, so it can be translated from a number of target ma-              variables. The basic variable can be either a register (denoted
chine languages, including x86, ARM, MIPS, and PowerPC.                    as τreg ), a primitive immediate value (denoted as τval ), or a
Benefiting from this design choice, our tool is applicable to              memory access (denoted as τmem ). The memory access can
mainstream architectures used in embedded firmware, includ-                be a value loaded from memory (denoted as load(expr)) or a
ing ARM and MIPS.                                                          value stored to memory (denoted as store(expr)). Here, the
                                                                           expr is a pointer.
4.1    Firmware Preprocessing                                                 We have implemented SSE based on Claripy, a popular
                                                                           SMT solver engine used in angr. Claripy provides a unified
An embedded firmware image is typically a Binary Large
                                                                           way to represent concrete and symbolic expressions. It sup-
OBject (BLOB), which is a collection of binary data that
                                                                           ports all common arithmetic operations (+, -, *, /, etc.) with
includes both the (compressed) kernel image and the file sys-
                                                                           an extensible interface [1]. We leveraged this interface to add
tem. First, we use Binwalk [25] to recognize, unpack, and
                                                                           the support of the memory load and store operations.
extract executables of our interest (e.g., httpd). Then, we
use IDA Pro [23] to automatically identify code/functions for              An intuitive example. Now, we use an intuitive example
the selected executables. However, IDA Pro is not proficient               to explain how we leverage SSE to find aliases to a given
enough to locate some functions that are indirectly called.                pointer. In the code snippet in Figure 3, there are three instruc-
We therefore developed an IDA plugin to augment this ca-                   tions. Our goal is to find all aliases to R1 in line 2, which is
pability. Our observation is that many function pointers are               known to be a pointer. First, we initialize the alias set with
hardcoded in the data segment. Therefore, our plugin scans                 R1 in line 20 . By looking backward, we find a definition of

                                                                       5
Algorithm 1 Alias update within a basic block                           ward alias set from all currentblock’s successors (denoted as
 1: procedure T RACE B LOCK(PRE f , SUCb )                              SUCb ). At the initial basic block, PRE f and SUCb are empty.
 2:    TARGET f , TARGETb ← G ET C URRENT TARGET( )                     In line 2, G ET C URRENT TARGET obtains the manually speci-
 3:    IN f ← PRE f ∪ TARGET f                                          fied target pointers of the current basic block (e.g., the taint
 4:    INb ← SUCb ∪ TARGETb                                             source). Then, in line 3, IN f is initialized to be the union
 5:    do                                                               of PRE f and TARGET f . In line 4, INb is initialized to be
 6:        NEW f , NEWb ← F ORWARD U PDATE(IN f )                       the union of SUCb and TARGETb . Here, IN f contains all
 7:        OUT f ← OUT f ∪ NEW f                                        the SSEs needing forward tracking and INb contains all the
 8:        INb ← INb ∪ NEWb
                                                                        SSEs needing backward tracking. Forward and backward
 9:        NEW f , NEWb ← BACKWARD U PDATE(INb )
                                                                        tracking are implemented via F ORWARD U PDATE and BACK -
10:        OUTb ← OUTb ∪ NEWb
                                                                        WARD U PDATE respectively. F ORWARD U PDATE takes IN f as
11:        IN f ← NEW f
12:        INb ← 0/                                                     input and generates new forward SSEs, which are merged
13:    while new aliases can be generated                               into OUT f . F ORWARD U PDATE may also return new back-
14:    return OUT f , OUTb                                              ward SSEs, which are merged into INb for further process-
                                                                        ing. BACKWARD U PDATE takes INb as input and generates
                                                                        new backward SSEs, which are merged into OUTb . BACK -
R1 in line 1, which gets its value by loading from memory               WARD U PDATE may also return new forward SSEs, which
R3+0x8. Therefore, by replacing R1 with load(R3+0x8), we                are merged into IN f for further processing. The algorithm
add load(R3+0x8) in line 10 to the alias set. Now, if we look           runs iteratively and terminates when no new alias SSE can
forward from line 1, we find a usage of R3 in line 3. That is,          be generated (line 5-12). The output of the algorithm, OUT f
the value of R3 is stored in the memory R6+0x4. Therefore,              and OUTb , are used as parameters for tracking succeeding
by replacing R3 with store(R6+0x4) in load(R3+0x8), we                  basic blocks and preceding basic blocks respectively (see Sec-
add load(store(R6+0x4)+0x8) in line 30 to the alias set.                tion 4.2.3 for more details). In the following, we detail the
By doing so back and forth iteratively along the define-use             forward update and backward update processes respectively.
and use-define chain, we can eventually reach a fixpoint of the         Forward update. During forward analysis, EmTaint traverses
alias set for the given pointer. The full SSE update rules within       instructions following the instruction flow. In the simplest
basic blocks and the algorithm for intra- and inter-procedural          form, for a live variable v in the current SSE, if v is on the
analyses are explained in the following sections.                       RHS (right hand side) of an assignment statement d = v (i.e., v
    1 LDR R1, [R3, 0x8]                                                 is used in a define-use chain), a new SSE is generated by
    2 MOV R0, R1 // R1 is pointer                                       replacing the variable v in the original SSE with the variable
    3 STR R3, [R6, 0x4]                                                 d. Therefore, the new SSE becomes an alias to the old one, and
    1′ load(R3+0x8)                                                     the new SSE needs to continue to be tracked forwards. The
    2′ R1 // initialized SSE                                            full SSE update rules following the define-use chain are listed
    3′ load(store(R6+0x4)+0x8)                                          in entries 1-7 in Table 2. In the table, each entry is represented
                                                                                       rn
      Backward analysis      Forward analysis                           as statement −→ expr.replace(rn, r j), where statement is the
                                                                                       rm
Figure 3: An intuitive simple example to illustrate SSE-based           encountered instruction and expr is the tracking SSE. In the
alias analysis                                                          statement, if rm is true and rn exists in expr, then rn should be
   By using an SSE to encode pointer provenance information,            replaced with r j. Note that this process should be conducted
complex data structures (e.g., x.y.z) can be naturally traced.          over all the live variables in the current SSE.
Due to the on-demand feature, it does not need to analyze the              As mentioned before, a forward update operation following
program as a whole. Instead, we can focus on a particular vari-         the define-use chain can also generate SSEs needing back-
able and only compute over relevant instructions. Therefore,            ward tracking. Specifically, one type of assignment statement
our approach scales well for complex programs.                          is in the form of store(d) = v and v is a pointer. For such
4.2.2 Alias Update Algorithm within a Basic Block                       type, in addition to continuing forward tracking, the new SSE
                                                                        needs to be tracked backwards also to find the definitions
In this section, we describe how to find aliases for a given
                                                                        of the store address, i.e., d, which is because once such a
pointer within a basic block. Our algorithm, called T RACE -
                                                                        definition is found, a new alias can be generated.
B LOCK, is shown in Algorithm 1. As mentioned before, for
a given pointer, we search both forwards and backwards to               Backward update. During backward analysis, EmTaint tra-
enrich the alias set. As such, for each basic block, we main-           verses instructions following the instruction flow reversely. In
tain two different sets of aliases for forward and backward             the simplest form, for a live variable v in the current SSE, if
results respectively. When running T RACE B LOCK over a                 v is on the LHS (left hand side) of an assignment statement
block, it takes two parameters – one forward alias set from all         v = u (i.e., v is defined in a use-define chain), a new SSE is
currentblock’s predecessors (denoted as PRE f ) and one back-           generated by replacing the variable v in the original SSE with

                                                                    6
Table 2: Update rules for structured symbolic expressions
                                                                     SSE update with define-use chain
                                  rj                                                                                       rn♦ rm
                    (1)ri = r j −
                                → expr.replace (r j, ri)                                     (2)ri = Binop (rn, rm) −−−b−→ expr.replace (rn♦b rm, ri)
                                               rn                                                                        rm
                                               → expr.replace (rn, ri)§
                    (3)ri = IT E (r j, rn, rm) −                                             (4)ri = IT E (r j, rn, rm) −→ expr.replace (rm, ri)§
                                                     rj                                                                         !r j
                                            load(r j)                                                               rj
                    (5)ri = Load (r j) −−−−→ expr.replace (load (r j) , ri)                  (6)Store (ri) = r j → expr.replace (r j, store (ri))
                                            store(r j)
                    (7)ri = Load (r j) −−−−−→ expr.replace (store (r j) , ri)
                                                                  SSE update following use-define chain
                                  ri                                                                                       ri
                    (8)ri = r j −
                                → expr.replace (ri, r j)                                     (9)ri = Binop (rn, rm) −
                                                                                                                    → expr.replace (ri, rn♦btm)
                                                         ri                       §                                                ri
                    (10)ri = IT E (r j, rn, rm) −
                                                → expr.replace (ri, rn)                      (11)ri = IT E (r j, rn, rm) −→ expr.replace (ri, rm)§
                                                         rj                                                                   !r j
                                             ri                                                                       load(ri)
                    (12)ri = Load (r j) −
                                        → expr.replace (ri, load (r j))                      (13)Store (ri) = r j −−−−→ expr.replace (load (ri) , r j)
                                                                                                                         ∗
                                                                                      SSE kills
                                    ri                                                                                store(ri) or load(ri)
                    (14)ri = r j −
                                 → expr.kill ()                                              (15)Store (ri) = r j −−−−−−−−−−−→ expr.kill()
                                                                                                                                   +
§: The statement ri = IT E(r j, rn, rm) denotes that if r j is true, ri = rn, otherwise, ri = rm. *: Existing load(ri) in expr occurs after the newly encountered store(ri) statement.
+: Existing store(ri)/load(ri) in expr occurs before the newly encountered store(ri) statement.
                                                                                                            1 MOV R2, R6
the variable u. Therefore, the new SSE becomes an alias to                                                  2 STR R3, [R2]
                                                                                                            3 LDR R1, [R3, 0x8] // R1 is pointer
the old one. Now that u is live in both directions, the new SSE                                             4 LDR R0, [R6]
needs to be tracked both backwards and forwards to find the                                                 5 LDR R0, [R0, 0x8]
definitions and uses of the variable u. The full SSE update
                                                                                                          1′   load(store(R6)+0x8)
rules following the use-define chain are listed in entries 8-13                                           2′   load(store(R2)+0x8)
in Table 2.                                                                                               3′   load(R3+0x8) // initialized SSE
                                                                                                          4′   load(R0+0x8)
   Different from forward tracking, in backward tracking, we                                              5′   R0
should not only find definitions of a variable v (i.e., v = u as
                                                                                                           Backward analysis              Forward analysis
explained before), but also find its uses (i.e., d = v). If a use
is found, we follow entries 1-6 in Table 2 except for entry 7 to                                 Figure 4: A complex example to illustrate SSE-based alias
update new SSEs. However, the new SSE can only be tracked                                        analysis.
forward, not backward, because the variable d used to update
                                                                                                 R3, so it begins backward analysis. Searching backwards,
the original SSE is live only in forward direction. One type
                                                                                                 EmTaint finds a use of R3 in line 2. So we replace R3 in
of assignment statement is in the form of store(d) = v and v
                                                                                                 load(R3+0x8) with store(R2) following rule 6 in Table 2
is a pointer. Note that for such type, in addition to forward
                                                                                                 and then we get a new alias SSE load(store(R2)+0x8).
tracking, the new SSE needs to be tracked backwards also, to
                                                                                                 Continuing backward tracking, EmTaint finds a definition
find the definition of store address (i.e., d).
                                                                                                 of R2 in line 1. Then it follows rule 8 to replace R2 with
Conditions to kill an SSE. Each variable has its own live                                        R6, yielding a new alias SSE load(store(R6)+0x8). This
scope. The liveness of the corresponding register (i.e., τreg ) is                               ends the first iteration and both new alias SSEs need to be
killed if we encounter a register assignment statement. If this                                  tracked backwards and forwards for a second round. We start
happens in forward/backward analysis, the whole SSE with                                         with forward analysis for load(store(R6)+0x8) in line 1.
this register is killed. This corresponds to entry 14 in Table 2.                                EmTaint quickly finds a use of load(R6) in line 4. Following
Similarly, the liveness of the corresponding memory access                                       rule 7, EmTaint replaces store(R6) with R0 and gets a new
(i.e., τmem ) is killed if we encounter a store statement. If this                               alias load(R0+0x8) in line 40 . Finally, following rule 5 in
happens in forward analysis, the whole SSE with this variable                                    line 50 , EmTaint gets R0 as a new alias. This concludes that
is killed. This corresponds to entry 15 in Table 2.                                              R1 in line 3 is an alias to R0 in line 5.
A non-trivial running example. Following Algorithm 1, we
are able to find alias information for more complex programs.                                    4.2.3 Intra- and Inter-procedural Alias Analyses
We show such an example in Figure 4, in which R1 in line 3                                       We have described how to find aliases within a basic block.
and R0 in line 5 are actually aliases to each other.                                             In this section, we introduce intra- and inter-procedural alias
   Assuming that the analysis begins at line 3, in which R1 is                                   analysis, which we summarize in Algorithm 2. The procedure
load from the address R3+0x8. To find aliases of R1, EmTaint                                     for finding aliases for a function is called A NALYZE F UNC -
first initializes an SSE as load(R3+0x8) in line 30 . In the                                     TION , which takes the CFG of the function as input. First,
first round of forward analysis, it does not find any use of                                     a WorkList of basic blocks is obtained via postorder traver-

                                                                                           7
sal of the CFG (line 2), in which a block is visited after all            Algorithm 2 Intra- and inter-procedural alias analysis
its successor blocks have been visited. Then, the procedure                1: procedure A NALYZE F UNCTION(CFG)
F INDA LIAS is used to actually analyze each block. We use the             2:    WorkList ← Postorder(CFG)
same procedure over the WorkList forwards and backwards,                   3:    do
until no new alias can be found.                                           4:       F INDA LIAS(WorkList.reverse())                  . forward
   Inside F INDA LIAS, TraceBlock is applied to analyze                    5:       F INDA LIAS(WorkList)                          . backward
aliases within each basic block BB (line 16). Then the re-                 6:    while new aliases can be generated
                                                                           7:    M ERGE(Caller.CallSite, EntryBB.OUTb )
sults are merged to the successors and predecessors of the
                                                                           8:    M ERGE(Caller.ReturnSite, ExitBB.OUT f )
current BB (line 17-18). However, if the current basic block
is a callsite, we need to incorporate inter-procedural analy-              9: procedure F INDA LIAS(WorkList)
                                                                          10:    for BB ∈ WorkList do
sis. Note that in our design, a call instruction is treated as a
                                                                          11:        if BB is a callsite then
separated basic block, which may differ from the definition
                                                                          12:            PT Rs ← G ET ROOT P TR(PRE f , SUCb )
of basic blocks in other tools. Specifically, F INDA LIAS col-            13:            MOD, REF ← T RANSFER F UNC(PT Rs, callee)
lects modifications (MOD) and references (REF) to memory                  14:            U PDATE C ONTEXT(PRE f , SUCb , MOD, REF)
locations that are accessed directly or indirectly through pa-            15:        else
rameters, return values, or global pointers in the callee (line           16:            OUT f , OUTb ←T RACE B LOCK(PRE f , SUCb )
11-14). In other words, we summarize the modifications and                17:            M ERGE(BB.successors, BB.OUT f )
references to memory locations by callee. To do so, at first, the         18:            M ERGE(BB.predecessors, BB.OUTb )
algorithm checks the SSEs in the forward alias set PRE f and
backward alias set SUCb of the callsite BB, extracts the root
pointer of each SSE (e.g., the root pointer of load(R0+0x8)               which contains the address of the function, can be used di-
is R0), and stores them in a set called PT Rs (line 12). Then,            rectly as an operand in an indirect call. We denote this kind
we use a transfer function (T RANSFER F UN) [12] to actually              of references as f ptr and show an example in line 2 of List-
get MOD and REF. Our implementation of transfer function                  ing 1. A function table pointer which contains the address to
is quite standard so we omit the details. Finally, with MOD               a function table, can be used indirectly (i.e., by deferencing
and REF, F INDA LIAS updates SSEs in PRE f and SUCb of                    the function table first) to get the real function address. We
the callsite BB with the definitions in MOD and REF (line                 denote this kind of references as d ptr and show an example
14).                                                                      in line 7 of Listing 1. In this example, an entry in the table
   Back to A NALYZE F UNCTION, after using F INDA LIAS iter-              has two fields – a string pointer to the function name and the
atively to reach a fixpoint, the results of the current function          actual function pointer.
are merged to the caller. Specifically, SSEs associated with               1   void (* fun_ptr ) () = & fun ; // address - taken function
arguments or global pointers from the entry block’s OUTb are               2   fptr = fun_ptr;
                                                                           3   x.y.z = fptr ;
merged into SUCb of the caller’s call site (line 7), and SSEs              4   fp1 = x.y.z;
associated with return value from the exit block’s OUT f are               5   call fp1; // indirect call
                                                                           6
merged into PRE f of caller’s return site (line 8).                        7   dptr = 0x92C44; // function table address
                                                                           8   while ( strcmp (* dptr , name ) ){
                                                                           9     dptr += 0 x8 ;
                                                                          10     ...
4.3    Indirect Call Resolution                                           11   }
                                                                          12   fp2 = *( dptr +0 x4 );
EmTaint resolves indirect calls based on dependencies be-                 13   call fp2; // indirect call
tween the aliases of indirect call targets and the aliases of
                                                                                     Listing 1: Indirect call resolution example.
function pointer references (or function table pointers). First,
EmTaint identifies all the indirect call instructions by travers-            Third, EmTaint performs alias identification. Our goal is to
ing through the disassembled code. EmTaint treats all branch              find dependence between the indirect call targets (e.g., fp1
instructions with a register as operand to be indirect calls (e.g.,       and fp2 in line 5 and 13) and the original references
blx r0 in ARM and jalr $t9) in MIPS). EmTaint then uses                   (e.g., fptr and dptr in line 2 and 7). EmTaint back tracks the
IDA Pro to filter call instructions whose targets can be recog-           indirect call targets to find its all aliases. At the same time,
nized. The remaining ones were treated as unresolved indirect             EmTaint finds the aliases of f ptr and d ptr through both for-
calls. Since almost all the call instructions in MIPS use a reg-          ward and backward analyses. Then, EmTaint collects all the
ister as operand, MIPS programs have a considerably larger                alias SSEs in the entry block and exit block of every function,
number of indirect calls than ARM programs (see Table 5).                 checks the data dependency between aliases of f ptr/d ptr
   Second, EmTaint finds all address-taken functions by scan-             and aliases of indirect call target, and updates them. We de-
ning both the code segment and data segment, and locates                  note the alias SSEs of the indirect call targets (e.g., fp1 and
all the references to the address-taken functions. There are              fp2 in the listing) as CTexpr (call target expression), and the
mainly two ways to reference a function. A function pointer               alias SSEs of the f ptr or d ptr as Pexpr (pointer expression).

                                                                      8
Finally, EmTaint matches the aliases to eventually resolve           “function summaries”, which describes what variables are
indirect calls. In the simplest form, a CTexpr is an alias to            tainted and how these tainted variables are propagated within
Pexpr. EmTaint can calculate the call target directly from               a function, to handle common string functions. We have im-
CTexpr. This often indicates an indirect call by f ptr. If the           plemented summaries for 29 common functions from the
CTexpr can be represented by load(d ptr + i ∗ stride), where             Standard C Library (Table 9 in Appendix), such as the func-
the d ptr is the address of a function table, the stride is a            tion strcpy(*dest,*src), in which the taint is propagated
constant, and i is an index, it strongly indicates a function            from argument src to argument dest.
table deference. EmTaint reads values from addresses d ptr +
                                                                         Taint propagation. The taint analysis is implemented based
i ∗ stride, where the index i starts at 0 and increases by 1 at
                                                                         on the SSE-based demand-driven alias analysis. Therefore,
a time. It terminates when the retrieved value is greater than
                                                                         taints are propagated along with the alias analysis. For a given
the maximum address of the function table. If the returned
                                                                         source, EmTaint taints relevant pointers depending on the
value is a legitimate function address, EmTaint adds it to the
                                                                         semantic of the source function, which can either be function
set of indirect call targets.
                                                                         parameters or a return value. Then, EmTaint tracks the tainted
    For the indirect call targets that cannot be accurately traced
                                                                         SSE to find its aliases and propagates the taint to another
to a specific f ptr or d ptr, we indirectly find their depen-
                                                                         following arithmetic operations (i.e., a=b+1), library functions
dencies. Our observation is that f ptr and d ptr are often
                                                                         listed in Table 9 in Appendix B, and loop copy functions. This
stored in a multi-level data structure whose root pointer is
                                                                         is quite standard in all related works. During taint analysis,
a global pointer (denoted as gptr). Therefore, we often find
                                                                         EmTaint also collects constraints for the tainted SSE (e.g., x
For memcpy-like functions, if they copy data to stack buffers,           attempt to obtain an estimated true positive rate. Specifically,
EmTaint retrieves the address of the destination buffer. If              we acquired 10 physical devices and randomly sampled 100
min_len is larger than the distance from the buffer address              alerts out of 971 that correspond to these 10 devices. Then,
to the top of the stack or there is no constraint on the tainted         we manually analyzed these alerts. An alert is confirmed
SSE, an alert is raised. Likewise, for the command execution             as a true positive if (1) it matches a known vulnerability or
library functions (e.g. system), EmTaint reports an alert if             (2) can be verified by successfully constructing a PoC on
there is no constraint on the tainted SSE.                               the physical device. Table 4 shows the results. Out of 100
5     Evaluation                                                         alerts, we confirmed 86 true positives, including 13 known
                                                                         vulnerabilities and 73 successfully constructed PoCs. This
We have implemented a prototype of EmTaint, which con-                   indicates the high true positive rate of our approach. For the
sists of about 24,000 LoC in Python. We evaluated EmTaint                rest, which are false positives, we attribute them to either
from three aspects. (1) How effective is it in uncovering real-          inaccurate indirect call resolution or failing to find security
world taint-style vulnerabilities in embedded firmware (Sec-             checks (see Section 6 for details).
tion 5.1)? (2) How effective is it in indirect call resolution?
To what extent does the indirect call resolution play a role             False negative evaluation. To evaluate the false negative rate
in improving vulnerability discovery (Section 5.2)? (3) How              (i.e., the rate of failing to find a bug) of our prototype, we
effective is it compared with the state-of-the-art tools (Sec-           collected 42 known vulnerabilities with exposed details from
tion 5.3)?                                                               exploit-db and MITRE CVE for the 35 firmware samples in
Experiment setup. Our evaluation was conducted against 35                our dataset. EmTaint discovered 41 of them (see Table 10 in
different firmware samples from six major vendors of network             Appendix). The missing one (CVE-2019-6989) is a buffer
embedded systems: Cisco, D-Link, NETGEAR, TRENDnet,                      overflow bug caused by unsafe copy using loop, which our
TP-Link and Tenda. These samples were manually down-                     tool does not check currently. We will extend our tool in the
loaded from the vendor’s official websites. In order to com-             future to support more kinds of security checks at sink.
pare with the existing tools head-to-head, some samples are
outdated. However, as we will explain later, all the identified          N-day and 0-day vulnerabilities. In addition to verifying
bugs were tested against the latest firmware versions before             alerts through sampling as mentioned before, we have been
reporting them to the vendors. Table 3 summarizes the in-                working hard to verify more alerts. We prioritize alerts related
formation about each sample, including its vendor, firmware              to physical devices that we have access to. At the time of
version, architecture, the analyzed Internet-facing binary, its          writing, we have confirmed a total of 41 n-day vulnerabili-
size, etc. This sample set covers three architectures, ARM32,            ties and 151 0-day vulnerabilities, 115 of which have been
MIPS32 and MIPS64, which are the mainstream architec-                    assigned with CVE/PSV numbers. As mentioned before, the
tures used in embedded devices. Note that since our approach             41 n-day vulnerabilities were matched from exploit-db [3] or
works based on VEX IR, it can be easily extended to support              MITRE CVE [2], which correspond to 120 alerts. In Table 10
other architectures. All the experiments were conducted on a             of Appendix, we list how the identified 41 n-day vulnerabili-
Ubuntu 18.04.4 LTS OS running on PC with a 64-bit 8-core                 ties are distributed among the public exposure IDs. Note that
Intel(R) Core(TM) i7-8550U CPU and 24 GB RAM.                            one CVE ID may correspond to multiple alerts. For example,
                                                                         CVE-2019-13278 describes a command injection vulnerabil-
5.1    Vulnerability Discovery                                           ity, which can be triggered at 33 different sinks.
Our current prototype, EmTaint, checks unsafe data copy
functions (e.g., strcpy) and command execution functions                    To confirm 0-day vulnerabilities, we tried to craft PoCs
(e.g., system). Therefore, EmTaint reports vulnerabilities re-           against the latest firmware versions of physical devices. In
lated to buffer overflow or command injection. We summarize              total, we confirmed 151 0-day vulnerabilities, including 38
the results in Table 3. In total, EmTaint reached 9,346 different        command injection bugs and 113 buffer overflow bugs. Ta-
sinks where there are tainted parameters to the sink functions.          ble 11 in Appendix summaries these bugs and lists relevant
1,887 of them were identified as alerts because no security              CVEs/PSVs. As mentioned before, some of the 35 samples
sanitization was detected. The average time to analyze each              are old-versioned for comparative evaluation, therefore, we
sample is about 180 seconds.                                             found that lots of alerts generated by EmTaint have been fixed
                                                                         in the latest version. These bugs were probably found by the
True-positive evaluation. Given the large amount of raised
                                                                         vendor themselves, therefore no public detail is available. For
alerts, it is important to understand how many of them are
                                                                         example, EmTaint produced 119 alerts in NETGEAR R7800
true positives. However, this would require substantial human
                                                                         v1.0.2.32, but we only verified one 0-day vulnerability in its
efforts to manually craft a proof-of-concept (PoC) for each
                                                                         latest version v1.0.2.68. By reverse-engineering both samples,
of them on real devices. Reverse-engineering each sample
                                                                         we found that many bugs were fixed by replacing strcpy
is also impractical because we have no ground truth of the
                                                                         with strncpy and setting the string length parameter to a
indirect calls. Therefore, we adopted random sampling in an
                                                                         constant.

                                                                    10
Table 3: Alerts produced by EmTaint for 35 samples
   Vendor                    ID          Firmware Version                        Arch          Binary          Size (KB)   Ana. Func     Tainted Sinks      Alerts      Time (s)
                              1           RV320_v1.4.2.20                      MIPS64              ssi.cgi         1,820         1,567            1450          335        634.64
   Cisco (2)
                              2           RV130_v1.0.3.44                      ARM32                httpd            612           796             402          150         60.96
                              3          DIR-825_B_2.10                        MIPS32               httpd            531           447             198           36         95.02
   D-Link (7)
                              4         DAP-1860_A1_B03                        MIPS32              uhttpd          1,129         1,030             106           12         97.02
                              10     TEW632BRP_1.010B32                        MIPS32              httpd             314          315              149           26         40.08
   TRENDnet (2)
                              11     TEW827DRU_2.04B03                         MIPS32               ssi              998          622              289          142         58.20
                              12           R7800_v1.0.2.32                     ARM32               net-cgi           542         1,286             291          119         88.91
   NETGEAR (17)
                              13           R8000_v1.0.4.4                      ARM32                httpd          1,508         1,088             428           36        167.48
   TP-Link (3)                29      WR940NV4_us.3.16.9                       MIPS32              httpd           1,691         3,481             225           31        343.16
   Tenda (4)                  32      AC9V3.0_v15.03.06.42                     MIPS32              httpd           2,039        1,201              172          84        433.62
   Total (35)§                 -               -                                  -                  -                 -       38,983            9,346       1,887       179.81†
§: The row labelled “Total” shows aggregated results for 35 samples. Due to page limit, we only list 10 representative samples in the table. The full list can be found in Table 12 in
Appendix.      †: The average execution time per sample.

      Table 4: True positive evaluation by random sampling                                                   time per sample is about 96 seconds. We also calculated the
         ID        Model                        Alerts      # of Samples       # of TP                       total number of target functions called at indirect callsites dur-
         1         Cisco RV320                   335             34               32                         ing this process, which is listed in the column I-Call targets.
         2         Cisco RV130                   150             15               15                         In total, our tool added 14,920 target functions that are called
         3         D-Link DIR-825                 36               4               3
         4         D-Link DAP-1860                12               2               2                         indirectly, which substantially improved taint-style vulnera-
         10        TRENDnet TEW632BRP             26               3               2                         bility discovery. Since we do not have the ground truth for
         11        TRENDnet TEW827DRU            142              14               7
         12        NETGEAR R7800                 119              12              10                         indirect call targets, we cannot accurately evaluate the failed
         13        NETGEAR R8000                  36               4               4                         resolutions. However, by reverse-engineering some samples,
         29        TP-Link WR940NV4               31               4               4
         32        Tenda AC9                      84               8               7
                                                                                                             we did find some missed targets because some address-taken
         Total     10                            971             100              86                         functions were not recognized. Interestingly, our tool also
                                                                                                             resolved some indirect call targets to be NULL. It turned out
                 Table 5: Results of indirect call resolution                                                that the firmware later correctly performs checking before
                                         All    Resolved     I-Call    % of resolved
                                                                                                             dereferenceing them. Therefore, our resolution was correct.
 ID            Model                                                                    Time (s)
                                      I-Calls    I-Calls    targets           I-Calls
 1
 2
               Cisco RV320
               Cisco RV130
                                         638
                                          17
                                                     620
                                                      14
                                                               794
                                                               475
                                                                               97.2%
                                                                               82.4%
                                                                                         288.04
                                                                                          27.83
                                                                                                             Importance of indirect call resolution. We analyzed the
 3             D-Link DIR-825             82          80       239             97.5%      33.44              same 35 samples with and without indirect call resolution.
 4             D-Link DAP-1860            27          21       327             77.8%      37.15
 10            TRENDnet TEW632BRP         43          41       182             95.3%      20.40              Due to page limit, we show the results of 10 representative
 11            TRENDnet TEW827RU          51          48       381             94.1%      25.56
 12            NETGEAR R7800              17          14       692             82.3%      40.27              samples with real devices to conduct this comparative exper-
 13            NETGEAR R8000               3           2       491             66.6%      40.35
 29            TP-Link WR940NV4          389         309       653             79.4%     409.10
                                                                                                             iment. The results are shown in Table 6. We illustrate how
 32            Tenda AC9V3.0              88          65       286             73.9%     204.55              the indirect call resolution enhances taint analysis and fur-
 Total (35)§   -                      12,446      12,022    14,920             96.6%     95.55†
                                                                                                             ther improves vulnerability discovery from four metrics: the
§: The row labelled “Total” shows aggregated results for 35 samples. Due to page
limit, we only list 10 representative samples in the table. The full list can be found in                    number of covered basic blocks, tainted basic blocks, tainted
Table 13 in Appendix. †: The average execution time per sample.                                              sinks, and generated alerts. As clearly shown by the results
5.2        Indirect Call Resolution and Its Impor-                                                           depicted in Table 6, the number of covered basic blocks in-
                                                                                                             creased from 356,585 to 412,688, and the number of tainted
           tance                                                                                             basic blocks increased from 19,218 to 56,488. This means
Indirect call resolution. Table 5 shows the results of indi-                                                 that indirect call resolution enables EmTaint to propagate the
rect call resolution over the 35 samples. The targets of our                                                 tainted data to functions where they were not able to reach
analysis are the binaries from Table 3. We first identified                                                  previously and taints more variables which were missed be-
all the callsites of indirect calls in the binaries and then at-                                             fore subject to indirect calls. The number of tainted sinks
tempted to find the called targets. Note that IDA pro already                                                increased from 1,234 to 3,710, which proves that indirect call
has some limited indirect call resolution capability. If IDA                                                 resolution makes it possible for tainted data to arrive unsafe
pro was able to resolve an indirect call, the corresponding                                                  sinks that were unreachable before. What are the benefits of
callsite was removed. For each sample, the number of indi-                                                   applying indirect call resolution from the perspective of vul-
rect callsites is denoted as All I-Calls. If at least one target                                             nerability discovery? As shown in the table, 763 more alerts
was found, we mark it as resolved. We denote this number as                                                  were produced after resolving functions that were invoked
Resolved I-Calls. Our approach was able to resolve 12,022                                                    indirectly. These alerts in turn correspond to 131 real bugs we
indirect callsites (out of 12,446) and the average execution                                                 introduced before.

                                                                                                     11
Table 6: The impact of indirect call resolution on vulnerability discovery.
                                                  w/o I-Call Resolution                                  I-Call Resolution
           Model
                                    Ana. Block   Tainted Block Tainted Sink     Alerts   Ana. Block   Tainted Block Tainted Sink   Alerts
           Cisco RV320                  74,778            976             98       16        89,915         16,307         1,450     335
           Cisco RV130                  26,210          1,234             87        2        30,919          4,585           402     150
           D-Link DIR-825               19,661          1,994            161       18        21,169          2,553           198      36
           D-Link DAP-1860              34,741          2,303            100        8        34,881          2,326           106      12
           TRENDnet TEW632BRP           10,756          1,556            126       13        11,672          1,953           149      26
           TRENDnet TEW827RU            21,348          2,788            286      142        22,714          2,860           289     142
           NETGEAR R7800                29,640          2,382            118        8        32,262          5,217           291     119
           NETGEAR R8000                46,053          1,997            166        0        56,907          7,373           428      36
           TP-Link WR940NV4             59,786          1,987             50        1        69,759          7,258           225      31
           Tenda AC9V3.0                33,612          2,001             42        0        42,490          6,056           172      84
           Total (10)                  356,585         19,218          1,234      208       412,688         56,488         3,710     971

   For different binaries, indirect call resolution has varied              to produce 2,084 alerts, among which 683 were true positives;
impacts on vulnerability detection. In most cases, indirect call            EmTaint reported 1,583 alerts in less than 4 hours, 1,518 of
resolution helps report more alerts. For Cisco, NERTEAR,                    which were true positives. The result shows that EmTaint can
Tenda, and TP-Link in our dataset, almost all alerts were dis-              find more true positives in less time than KARONTE and
covered with the help of indirect call resolution. For example,             SaTC. Because of the improvement over existing work, our
our prototype produced 0 alert originally without indirect call             tool found 22 new 0-day vulnerabilities in the already exten-
resolution and 120 alerts in total afterwards for product R8000             sively tested samples. They are included as part of the 151
and AC9V3.0. There is no change in the amount of alerts in                  0-day vulnerabilities we mentioned before.
firmware sample TEW827RU, which can be attributed to the                       Apart from the lack of indirect call resolution and less ac-
fact that the propagation of tainted data from source to sink               curate alias analysis, we found that KARONTE and SaTC fre-
does not involve any indirect calls in this sample.                         quently raise false alerts because of the incorrectly specified
   To further evaluate the effectiveness of indirect call res-              taint sources. Specifically, due to the path explosion problem
olution, we manually analyzed the 162 real bugs (11 n-day                   of symbolic execution, they cannot directly use the commonly
and 151 0-day) in the 10 samples, Among them, the trigger                   recognized taint sources such as recv. Instead, to shorten
path of 131 bugs involves indirect calls. Although we do not                the path from the sources to sinks, KARONTE infers the
have the ground truth of indirect call targets in any of these              taint source through the interaction between the back-end pro-
samples (indirect call resolution is widely believed to be dif-             grams in the firmware with a preset list of network-encoding
ficult, even if the source code is available) and thus cannot               strings (e.g., “soap” or “HTTP”), and SaTC infers the taint
evaluate the accuracy of the recovered targets, these 131 real              sources by the keywords shared between the front-end files
bugs demonstrate the importance of indirect call resolution.                and the back-end programs. When these keywords are unre-
5.3    Comparison with KARONTE and SaTC                                     lated to user inputs, false positives occur. In contrast, EmTaint
Three works are closely related to ours, including CodeS-                   starts taint analysis directly from the commonly used sources
onar [21], KARONTE [35] and SaTC [11]. However, CodeS-                      summarized in Table 8, which helps us achieve much higher
onar is a commercial product of GrammaTech and we were                      true positive rates. We discuss false positives introduced by
unable to use it in our evaluation. KARONTE and SaTC are                    EmTaint in Section 6.
both open-sourced. They perform taint analysis to detect the
taint-style vulnerability in embedded firmware through the                  6     Limitations
under-constrained symbolic execution on top of angr [39].
Due to the very similar scope, we reused the dataset [24] re-               In this section, we discuss the limitations of the proposed
leased by KARONTE, which includes 49 firmware samples                       approach. SSE, the fundamental technique of the proposed
from 4 embedded vendors (i.e., NETGEAR, D-Link, TP-Link                     system, works well to track pointer between indirect memory
and Tenda). In fact, SaTC also used this dataset. This made                 accesses. However, it falls short when the pointer involves
our experiment a head-to-head comparison.                                   bitwise operations or the offset of the indirect memory access
   Table 7 shows the results, where the information for                     is not a constant. As such, false negatives could occur for
KARONTE and SaTC are copied directly from the original                      aliases introduced by these operations. Further study is needed
papers [11,35]. To be fair, we adopt the same definition of true            to improve SSE to handle this situation.
positive in KARONTE (cf. §X.D in [35]). Specially, an alert                    Second, the current prototype supports discovering buffer
is a true positive if the tainted data that reaches the sink is pro-        overflow and command injection vulnerabilities. In our ex-
vided by the user. This applies to SaTC too. KARONTE took                   periment, most discovered buffer overflow bugs were stack
approximately 451 hours to produce 74 alerts, among which                   based. Our tool stayed silent in the presence of many heap
46 were true positives; SaTC took approximately 459 hours                   overflow bugs (false negative). To more accurately predict

                                                                       12
You can also read