Finding Taint-Style Vulnerabilities in Linux-based Embedded Firmware with SSE-based Alias Analysis
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
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