1 99:59:59,999 --> 99:59:59,999 *preroll music* [filler, please remove in amara] 2 99:59:59,999 --> 99:59:59,999 Herald: Our next speaker for today is a computer science PhD student at UC Santa 3 99:59:59,999 --> 99:59:59,999 Barbara. He is a member of the Shellfish Hacking Team and he's also the organizer 4 99:59:59,999 --> 99:59:59,999 of the IECTF Hacking Competition. Please give a big round of applause to Nilo 5 99:59:59,999 --> 99:59:59,999 Redini. [filler, please remove in amara] 6 99:59:59,999 --> 99:59:59,999 *applause* [filler, please remove in amara] 7 99:59:59,999 --> 99:59:59,999 Nilo: Thanks for the introduction, hello to everyone. My name is Nilo, and today 8 99:59:59,999 --> 99:59:59,999 I'm going to present you my work Koronte: identifying multi-binary vulnerabilities 9 99:59:59,999 --> 99:59:59,999 in embedded firmware at scale. This work is a co-joint effort between me and 10 99:59:59,999 --> 99:59:59,999 several of my colleagues at University of Santa Barbara and ASU. This talk is going 11 99:59:59,999 --> 99:59:59,999 to be about IoT devices. So before starting, let's see an overview about IoT 12 99:59:59,999 --> 99:59:59,999 devices. IoT devices are everywhere. As the research suggests, they will reach the 13 99:59:59,999 --> 99:59:59,999 20 billion units by the end of the next year. And a recent study conducted this 14 99:59:59,999 --> 99:59:59,999 year in 2019 on 16 million households showed that more than 70 percent of homes 15 99:59:59,999 --> 99:59:59,999 in North America already have an IoT network connected device. IoT devices make 16 99:59:59,999 --> 99:59:59,999 everyday life smarter. You can literally say "Alexa, I'm cold" and Alexa will 17 99:59:59,999 --> 99:59:59,999 interact with the thermostat and increase the temperature of your room. Usually the 18 99:59:59,999 --> 99:59:59,999 way we interact with the IoT devices is through our smartphone. We send a request 19 99:59:59,999 --> 99:59:59,999 to the local network, to some device, router or door lock, or we might send the 20 99:59:59,999 --> 99:59:59,999 same request through a cloud endpoint, which is usually managed by the vendor of 21 99:59:59,999 --> 99:59:59,999 the IoT device. Another way is through the IoT hubs, smartphone will send the request 22 99:59:59,999 --> 99:59:59,999 to some IoT hub, which in turn will send the request to some other IoT devices. As 23 99:59:59,999 --> 99:59:59,999 you can imagine, IoT devices use and collect our data and some data is more 24 99:59:59,999 --> 99:59:59,999 sensitive than other. For instance, think of all the data that is collected by my 25 99:59:59,999 --> 99:59:59,999 lightbulb or data that is collected by our security camera. As such, IoT devices can 26 99:59:59,999 --> 99:59:59,999 compromise people's safety and privacy. Things, for example, about the security 27 99:59:59,999 --> 99:59:59,999 implication of a faulty smartlock or the brakes of your smart car. So the question 28 99:59:59,999 --> 99:59:59,999 that we asked is: Are IoT devices secure? Well, like everything else, they are not. 29 99:59:59,999 --> 99:59:59,999 OK, in 2016 the Mirai botnet compromised and leveraged millions of IoT devices to 30 99:59:59,999 --> 99:59:59,999 disrupt core Internet services such as Twitter, GitHub and Netflix. And in 2018, 31 99:59:59,999 --> 99:59:59,999 154 vulnerabilities affecting IoT devices were published, which represented an 32 99:59:59,999 --> 99:59:59,999 increment of 15% compared to 2017 and an increase of 115% compared to 2016. So then 33 99:59:59,999 --> 99:59:59,999 we wonder: So why is it hard to secure IoT devices? To answer this question we have 34 99:59:59,999 --> 99:59:59,999 to look up how IoT devices work and they are made. Usually when you remove all the 35 99:59:59,999 --> 99:59:59,999 plastic and peripherals IoT devices look like this. A board with some chips laying 36 99:59:59,999 --> 99:59:59,999 on it. Usually you can find the big chip, the microcontroller which runs the 37 99:59:59,999 --> 99:59:59,999 firmware and one or more peripheral controllers which interact with external 38 99:59:59,999 --> 99:59:59,999 peripherals such as the motor of, your smart lock or cameras. Though the design 39 99:59:59,999 --> 99:59:59,999 is generic, implementations are very diverse. For instance, firmware may run on 40 99:59:59,999 --> 99:59:59,999 several different architectures such as ARM, MIPS, x86, PowerPC and so forth. And 41 99:59:59,999 --> 99:59:59,999 sometimes they are even proprietary, which means that if a security analyst wants to 42 99:59:59,999 --> 99:59:59,999 understand what's going on in the firmware, he'll have a hard time if he 43 99:59:59,999 --> 99:59:59,999 doesn't have the vendor specifics. Also, they're operating in environments with 44 99:59:59,999 --> 99:59:59,999 limited resources, which means that they run small and optimized code. For 45 99:59:59,999 --> 99:59:59,999 instance, vendors might implement their own version of some known algorithm in an 46 99:59:59,999 --> 99:59:59,999 optimized way. Also, IoT devices manage external peripherals that often use custom 47 99:59:59,999 --> 99:59:59,999 code. Again, with peripherals we mean like cameras, sensors and so forth. The 48 99:59:59,999 --> 99:59:59,999 firmware of IoT devices can be either Linux based or a blob firmware, Linux 49 99:59:59,999 --> 99:59:59,999 based are by far the most common. A study showed that 86% of firmware are based on 50 99:59:59,999 --> 99:59:59,999 Linux and on the other hand, blobs firmware are usually operating systems and 51 99:59:59,999 --> 99:59:59,999 user applications packaged in a single binary. In any case, firmware samples are 52 99:59:59,999 --> 99:59:59,999 usually made of multiple components. For instance, let's say that you have your 53 99:59:59,999 --> 99:59:59,999 smart phone and you send a request to your IoT device. This request will be received 54 99:59:59,999 --> 99:59:59,999 by a binary which we term as body binary, which in this example is an webserver. The 55 99:59:59,999 --> 99:59:59,999 request will be received, parsed, and then it might be sent to another binary code, 56 99:59:59,999 --> 99:59:59,999 the handler binary, which will take the request, work on it, produce an answer, 57 99:59:59,999 --> 99:59:59,999 send it back to the webserver, which in turn would produce a response to send to 58 99:59:59,999 --> 99:59:59,999 the smartphone. So to come back to the question why is it hard to secure IoT 59 99:59:59,999 --> 99:59:59,999 devices? Well, the answer is because IoT devices are in practice very diverse. Of 60 99:59:59,999 --> 99:59:59,999 course, there have been various work that have been proposed to analyze and secure 61 99:59:59,999 --> 99:59:59,999 firmware for IoT devices. Some of them using static analysis. Others using 62 99:59:59,999 --> 99:59:59,999 dynamic analysis and several others using a combination of both. Here I wrote 63 99:59:59,999 --> 99:59:59,999 several of them. Again at the end of the presentation there is a bibliography with 64 99:59:59,999 --> 99:59:59,999 the title of these works. Of course, all these approaches have some problems. For 65 99:59:59,999 --> 99:59:59,999 instance, the current dynamic analysis are hard to apply to scale because of the 66 99:59:59,999 --> 99:59:59,999 customized environments that IoT devices work on. Usually when you try to 67 99:59:59,999 --> 99:59:59,999 dynamically execute a firmware, it's gonna check if the peripherals are connected and 68 99:59:59,999 --> 99:59:59,999 are working properly. In a case where you can't have the peripherals, it's gonna be 69 99:59:59,999 --> 99:59:59,999 hard to actually run the firmware. Also current static analysis approaches are 70 99:59:59,999 --> 99:59:59,999 based on what we call the single binary approach, which means that binaries from a 71 99:59:59,999 --> 99:59:59,999 firmware are taken individually and analysed. This approach might produce many 72 99:59:59,999 --> 99:59:59,999 false positives. For instance, so let's say again that we have our two binaries. 73 99:59:59,999 --> 99:59:59,999 This is actually an example that we found on one firmware, so the web server will 74 99:59:59,999 --> 99:59:59,999 take the user request, will parse the request and produce some data, will set 75 99:59:59,999 --> 99:59:59,999 this data to an environment variable and eventually will execute the handle binary. 76 99:59:59,999 --> 99:59:59,999 Now, if you see the parsing function contains a string compare which checks if 77 99:59:59,999 --> 99:59:59,999 some keyword is present in the request. And if so, it just returns the whole 78 99:59:59,999 --> 99:59:59,999 request. Otherwise, it will constrain the size of the request to 128 bytes and 79 99:59:59,999 --> 99:59:59,999 return it. The handler binary in turn when spawned will receive the data by doing a 80 99:59:59,999 --> 99:59:59,999 getenv on the query string, but also will getenv on another environment variable 81 99:59:59,999 --> 99:59:59,999 which in this case is not user controlled and they user cannot influence the content 82 99:59:59,999 --> 99:59:59,999 of this variable. Then it's gonna call function process_request. This function 83 99:59:59,999 --> 99:59:59,999 eventually will do two string copies. One from the user data, the other one from the 84 99:59:59,999 --> 99:59:59,999 log path on two different local variables that are 128 bytes long. Now in the first 85 99:59:59,999 --> 99:59:59,999 case, as we have seen before, the data can be greater than 128 bytes and this string 86 99:59:59,999 --> 99:59:59,999 copy may result in a bug. While in the second case it will not. Because here we 87 99:59:59,999 --> 99:59:59,999 assume that the system handles its own data in a good manner. So throughout this 88 99:59:59,999 --> 99:59:59,999 work, we're gonna call the first type of binary, the setter binary, which means 89 99:59:59,999 --> 99:59:59,999 that it is the binary that takes the data and set the data for another binary to be 90 99:59:59,999 --> 99:59:59,999 consumed. And the second type of binary we called them the getter binary. So the 91 99:59:59,999 --> 99:59:59,999 current bug finding tools are inadequate because other bugs are left undiscovered 92 99:59:59,999 --> 99:59:59,999 if the analysis only consider those binaries that received network requests or 93 99:59:59,999 --> 99:59:59,999 they're likely to produce many false positives if the analysis considers all of 94 99:59:59,999 --> 99:59:59,999 them individually. So then we wonder how these different components actually 95 99:59:59,999 --> 99:59:59,999 communicate. They communicate through what are called interprocess communication, 96 99:59:59,999 --> 99:59:59,999 which basically it's a finite set of paradigms used by binaries to communicate 97 99:59:59,999 --> 99:59:59,999 such as files, environment variables, MMIO and so forth. All these pieces are 98 99:59:59,999 --> 99:59:59,999 represented by data keys, which are file names, or in the case of the example 99 99:59:59,999 --> 99:59:59,999 before here on the right, it's the query string environment variable. Each binary 100 99:59:59,999 --> 99:59:59,999 that relies on some shared data must know the endpoint where such data will be 101 99:59:59,999 --> 99:59:59,999 available, for instance, again, like a file name or like even a socket endpoint 102 99:59:59,999 --> 99:59:59,999 or the environment variable. This means that usually, data keys are coded in the 103 99:59:59,999 --> 99:59:59,999 program itself, as we saw before. To find bugs in firmware, in a precise manner, we 104 99:59:59,999 --> 99:59:59,999 need to track how user data is introduced and propagated across the different 105 99:59:59,999 --> 99:59:59,999 binaries. Okay, let's talk about our work. Before you start talking about Karonte, we 106 99:59:59,999 --> 99:59:59,999 define our threat model. We hypotesized that attacker sends arbitrary requests 107 99:59:59,999 --> 99:59:59,999 over the network, both LAN and WAN directly to the IoT device. Though we said 108 99:59:59,999 --> 99:59:59,999 before that sometimes IoT device can communicate through the clouds, research 109 99:59:59,999 --> 99:59:59,999 showed that some form of local communication is usually available, for 110 99:59:59,999 --> 99:59:59,999 instance, during the setup phase of the device. Karonte is defined as a static 111 99:59:59,999 --> 99:59:59,999 analysis tool that tracks data flow across multiple binaries, to find 112 99:59:59,999 --> 99:59:59,999 vulnerabilities. Let's see how it works. So the first step, Karonte find those 113 99:59:59,999 --> 99:59:59,999 binaries that introduce the user input into the firmware. We call these border 114 99:59:59,999 --> 99:59:59,999 binaries, which are the binaries, that basically interface the device to the 115 99:59:59,999 --> 99:59:59,999 outside world. Which in the example is our web server. Then it tracks how a data is 116 99:59:59,999 --> 99:59:59,999 shared with other binaries within the firmware sample. Which we'll understand in 117 99:59:59,999 --> 99:59:59,999 this example, the web server communicates with the handle binary, and builds what we 118 99:59:59,999 --> 99:59:59,999 call the BDG. BDG which stands for binary dependency graph. It's basically a graph 119 99:59:59,999 --> 99:59:59,999 representation of the data dependencies among different binaries. Then we detect 120 99:59:59,999 --> 99:59:59,999 vulnerabilities that arise from the misuse of the data using the BDG. This is an 121 99:59:59,999 --> 99:59:59,999 overview of our system. We start by taking a packed firmware, we unpack it. We find 122 99:59:59,999 --> 99:59:59,999 the border binaries. Then we build the binary dependency graph, which relies on a 123 99:59:59,999 --> 99:59:59,999 set of CPFs, as we will see soon. CPF stands for Communication Paradigm Finder. 124 99:59:59,999 --> 99:59:59,999 Then we find the specifics of the communication, for instance, like the 125 99:59:59,999 --> 99:59:59,999 constraints applied to the data that is shared through our module multi-binary 126 99:59:59,999 --> 99:59:59,999 data-flow analysis. Eventually we run our insecure interaction detection module, 127 99:59:59,999 --> 99:59:59,999 which basically takes all the information and produces alerts. Our system is 128 99:59:59,999 --> 99:59:59,999 completely static and relies on our static taint engine. So let's see each one of 129 99:59:59,999 --> 99:59:59,999 these steps, more in details. The unpacking procedure is pretty easy, we use 130 99:59:59,999 --> 99:59:59,999 the off-the-shelf firmware unpacking tool binwalk. And then we have to find the 131 99:59:59,999 --> 99:59:59,999 border binaries. Now we see that border binaries basically are binaries that 132 99:59:59,999 --> 99:59:59,999 receive data from the network. And we hypotesize that will contain parsers to 133 99:59:59,999 --> 99:59:59,999 validate the data that they received. So in order to find them, we have to find 134 99:59:59,999 --> 99:59:59,999 parsers which accept data from network and parse this data. To find parsers we rely 135 99:59:59,999 --> 99:59:59,999 on related work, which basically uses a few metrics and define through a number 136 99:59:59,999 --> 99:59:59,999 the likelihood for a function to contain parsing capabilities. These metrics that 137 99:59:59,999 --> 99:59:59,999 we used are number of basic blocks, number of memory comparison operations and number 138 99:59:59,999 --> 99:59:59,999 of branches. Now while these define parsers, we also have to find if a binary 139 99:59:59,999 --> 99:59:59,999 takes data from the network. As such, we define two more metrics. The first one, we 140 99:59:59,999 --> 99:59:59,999 check if binary contains any network related keywords as SOAP, http and so 141 99:59:59,999 --> 99:59:59,999 forth. And then we check if there exists a data flow between read from socket and a 142 99:59:59,999 --> 99:59:59,999 memory comparison operation. Once for each function, we got all these metrics, we 143 99:59:59,999 --> 99:59:59,999 compute what is called a parsing score, which basically is just a sum of products. 144 99:59:59,999 --> 99:59:59,999 Once we got a parsing score for each function in a binary, we represent the 145 99:59:59,999 --> 99:59:59,999 binary with its highest parsing score. Once we got that for each binary in the 146 99:59:59,999 --> 99:59:59,999 firmware we cluster them using the DBSCAN density based algorithm and consider the 147 99:59:59,999 --> 99:59:59,999 cluster with the highest parsing score as containing the set of border binaries. 148 99:59:59,999 --> 99:59:59,999 After this, we build the binary dependency graph. Again the binary dependency graph 149 99:59:59,999 --> 99:59:59,999 represents the data dependency among the binaries in a firmware sample. For 150 99:59:59,999 --> 99:59:59,999 instance, this simple graph will tell us that a binary A communicates with binary C 151 99:59:59,999 --> 99:59:59,999 using files and the same binary A communicates with another binary B using 152 99:59:59,999 --> 99:59:59,999 environment variables. Let's see how this works. So we start from the identified 153 99:59:59,999 --> 99:59:59,999 border binaries and then we taint the data compared against network related keywords 154 99:59:59,999 --> 99:59:59,999 that we found and run a static analysis, static taint analysis to detect whether 155 99:59:59,999 --> 99:59:59,999 the binary relies on any IPC paradigm to share the data. If we find that it does, 156 99:59:59,999 --> 99:59:59,999 we establish if the binary is a setter or a getter, which again means that if the 157 99:59:59,999 --> 99:59:59,999 binary is setting the data to be consumed by another binary, or if the binary 158 99:59:59,999 --> 99:59:59,999 actually gets the data and consumes it. Then we retrieve the employed data key 159 99:59:59,999 --> 99:59:59,999 which in the example before was the keyword QUERY_STRING. And finally we scan 160 99:59:59,999 --> 99:59:59,999 the firmware sample to find other binaries that may rely on the same data keys and 161 99:59:59,999 --> 99:59:59,999 schedule them for further analysis. To understand whether a binary relies on any 162 99:59:59,999 --> 99:59:59,999 IPC, we use what we call CPFs, which again means communication paradigm finder. We 163 99:59:59,999 --> 99:59:59,999 design a CPF for each IPC. And the CPFs are also used to find the same data keys 164 99:59:59,999 --> 99:59:59,999 within the firmware sample. We also provide Karonte with a generic CPF to 165 99:59:59,999 --> 99:59:59,999 cover those cases where the IPC is unknown. Or those cases were the vendor 166 99:59:59,999 --> 99:59:59,999 implemented their own versions of some IPC. So for example they don't use the 167 99:59:59,999 --> 99:59:59,999 setenv. But they implemented their own setenv. The idea behind this generic CPF 168 99:59:59,999 --> 99:59:59,999 that we call the semantic CPF is that data keys has to be used as index to set, or to 169 99:59:59,999 --> 99:59:59,999 get some data in this simple example. So let's see how the BDG algorithm works. We 170 99:59:59,999 --> 99:59:59,999 start from the body binary, which again will start from the server request and 171 99:59:59,999 --> 99:59:59,999 will pass the URI and we see that here. it runs a string comparison against some 172 99:59:59,999 --> 99:59:59,999 network related keyword. As such, we taint the variable P. And we see that the 173 99:59:59,999 --> 99:59:59,999 variable P is returned from the function to these two different points. As such, we 174 99:59:59,999 --> 99:59:59,999 continue. And now we see that data gets tainted and the variable data, it's passed 175 99:59:59,999 --> 99:59:59,999 to the function setenv. At this point, the environment CPF will understand that 176 99:59:59,999 --> 99:59:59,999 tainted data is passed, is set to an environment variable and will understand 177 99:59:59,999 --> 99:59:59,999 that this binary is indeed the setter binary that uses the environment. Then we 178 99:59:59,999 --> 99:59:59,999 retrieve the data key QUERY_STRING and we'll search within the firmware sample 179 99:59:59,999 --> 99:59:59,999 all the other binaries that rely on the same data key. And it will find that this 180 99:59:59,999 --> 99:59:59,999 binary relies on the same data key and will schedule this for further analysis. 181 99:59:59,999 --> 99:59:59,999 After this algorithm we build the BDG by creating edges between setters and getters 182 99:59:59,999 --> 99:59:59,999 for each data key. The multi binary data flow analysis uses the BDG to find and 183 99:59:59,999 --> 99:59:59,999 propagate the data constraints from a setter to a getter. Now, through this we 184 99:59:59,999 --> 99:59:59,999 apply only the least three constraints, which means that ideally between two 185 99:59:59,999 --> 99:59:59,999 program points, there might be an infinite number of parts and ideally in theory an 186 99:59:59,999 --> 99:59:59,999 infinite amount of constraints that we can propagate to the setter binary to the 187 99:59:59,999 --> 99:59:59,999 getter binary. But since our goal here is to find bugs, we only propagate the least 188 99:59:59,999 --> 99:59:59,999 strict set of constraints. Let's see an example. So again, we have our two 189 99:59:59,999 --> 99:59:59,999 binaries and we see that the variable that is passed to the setenv function is data, 190 99:59:59,999 --> 99:59:59,999 which comes from two different parts from the parse URI function. In the first case, 191 99:59:59,999 --> 99:59:59,999 the data that its passed is unconstrained one in the second case, a line 8 is 192 99:59:59,999 --> 99:59:59,999 constrained to be at most 128 bytes. As such, we only propagate the constraints of 193 99:59:59,999 --> 99:59:59,999 the first guy. In turn, the getter binary will retrieve this variable from the 194 99:59:59,999 --> 99:59:59,999 environment and set the variable query. Oh, sorry. Which in this case will be 195 99:59:59,999 --> 99:59:59,999 unconstrained. Insecure interaction detection run a static taint analysis and 196 99:59:59,999 --> 99:59:59,999 check whether tainted data can reach a sink in an unsafe way. We consider as 197 99:59:59,999 --> 99:59:59,999 sinks memcpy like functions which are functions that implement semantically 198 99:59:59,999 --> 99:59:59,999 equivalent memcyp, strcpy and so forth. We raise alert if we see that there is a 199 99:59:59,999 --> 99:59:59,999 dereference of a tainted variable and if we see there are comparisons of tainted 200 99:59:59,999 --> 99:59:59,999 variables in loop conditions to detect possible DoS vulnerabilities. Let's see an 201 99:59:59,999 --> 99:59:59,999 example again. So we got here. We know that our query variable is tainted and 202 99:59:59,999 --> 99:59:59,999 it's unconstrained. And then we follow the taint in the function process_request, 203 99:59:59,999 --> 99:59:59,999 which we see will eventually copy the data from q to arg. Now we see that arg is 128 204 99:59:59,999 --> 99:59:59,999 bytes long while q is unconstrained and therefore we generate an alert here. Our 205 99:59:59,999 --> 99:59:59,999 static taint engine is based on BootStomp and is completely based on symbolic 206 99:59:59,999 --> 99:59:59,999 execution, which means that the taint is propagated following the program data 207 99:59:59,999 --> 99:59:59,999 flow. Let's see an example. So assuming that we have this code, the first 208 99:59:59,999 --> 99:59:59,999 instruction takes the result from some seed function that might return for 209 99:59:59,999 --> 99:59:59,999 instance, some user input. And in a symbolic world, what we do is we create a 210 99:59:59,999 --> 99:59:59,999 symbolic variable ty and assign to it a tainted variable that we call TAINT_ty, 211 99:59:59,999 --> 99:59:59,999 which is the taint target. The next destruction X takes the value ty plus 5 212 99:59:59,999 --> 99:59:59,999 and a symbolic word. We just follow the data flow and x gets assigned TAINT_ty 213 99:59:59,999 --> 99:59:59,999 plus 5 which effectively taints also X. If at some point X is overwritten with some 214 99:59:59,999 --> 99:59:59,999 constant data, the taint is automatically removed. In its original design, 215 99:59:59,999 --> 99:59:59,999 BootStomp, the taint is removed also when data is constrained. For instance, here we 216 99:59:59,999 --> 99:59:59,999 can see that the variable n is tainted but then is constrained between two values 0 217 99:59:59,999 --> 99:59:59,999 and 255. And therefore, the taint is removed. In our taint engine we have two 218 99:59:59,999 --> 99:59:59,999 additions. We added a path prioritization strategy and we add taint dependencies. 219 99:59:59,999 --> 99:59:59,999 The path prioritization strategy valorizes paths that propagate the taint and 220 99:59:59,999 --> 99:59:59,999 deprioritizes those that remove it. For instance, say again that some user input 221 99:59:59,999 --> 99:59:59,999 comes from some function and the variable user input gets tainted. Gets tainted and 222 99:59:59,999 --> 99:59:59,999 then is passed to another function called parse. Here, if you see there are possibly 223 99:59:59,999 --> 99:59:59,999 an infinite number of symbolic parts in this while. But only 1 will return tainted 224 99:59:59,999 --> 99:59:59,999 data. While the others won't. So the path prioritization strategy valorizes this 225 99:59:59,999 --> 99:59:59,999 path instead of the others. This has been implemented by finding basic blocks within 226 99:59:59,999 --> 99:59:59,999 a function that return a nonconstant data. And if one is found, we follow its return 227 99:59:59,999 --> 99:59:59,999 before considering the others. Taint dependencies allows smart untaint 228 99:59:59,999 --> 99:59:59,999 strategies. Let's see again the example. So we know that user input here is 229 99:59:59,999 --> 99:59:59,999 tainted, is then parsed and then we see that it's length is checked and stored in 230 99:59:59,999 --> 99:59:59,999 a variable n. Its size is checked and if it's higher than 512 bytes, the function 231 99:59:59,999 --> 99:59:59,999 will return. Otherwise it copies the data. Now in this case, it might happen that if 232 99:59:59,999 --> 99:59:59,999 this strlen function is not analyzed because of some static analysis input 233 99:59:59,999 --> 99:59:59,999 decisions, the taint tag of cmd might be different from the taint tag of n and in 234 99:59:59,999 --> 99:59:59,999 this case, though, and gets untainted, cmd is not untainted and the strcpy can raise, 235 99:59:59,999 --> 99:59:59,999 sorry, carries a false positive. So to fix this problem. Basically we create a 236 99:59:59,999 --> 99:59:59,999 dependency between the taint tag of n and the taint tag of cmd. And when n gets 237 99:59:59,999 --> 99:59:59,999 untainted, cmd gets untainted as well. So we don't have more false positives. This 238 99:59:59,999 --> 99:59:59,999 procedure is automatic and we find functions that implement streamlined 239 99:59:59,999 --> 99:59:59,999 semantically equivalent code and create taint tag dependencies. OK. Let's see our 240 99:59:59,999 --> 99:59:59,999 evaluation. We ran 3 different evaluations on 2 different data sets. The first one 241 99:59:59,999 --> 99:59:59,999 composed by 53 latest firmware samples from seven vendors and a second one 899 242 99:59:59,999 --> 99:59:59,999 firmware gathered from related work. In the first case, we can see that the total 243 99:59:59,999 --> 99:59:59,999 number of binaries considered are 8.5k, few more than that. And our system 244 99:59:59,999 --> 99:59:59,999 generated 87 alerts of which 51 were found to be true positive and 34 of them were 245 99:59:59,999 --> 99:59:59,999 multibinary vulnerabilities, which means that the vulnerability was found by 246 99:59:59,999 --> 99:59:59,999 tracking the data flow from the setter to the getter binary. We also ran a 247 99:59:59,999 --> 99:59:59,999 comparative evaluation, which basically we tried to measure the effort that an 248 99:59:59,999 --> 99:59:59,999 analyst would go through in analyzing firmware using different strategies. In 249 99:59:59,999 --> 99:59:59,999 the first one, we consider each and every binary in the firmware sample 250 99:59:59,999 --> 99:59:59,999 independently and run the analysis for up to seven days for each firmware. The 251 99:59:59,999 --> 99:59:59,999 system generated almost 21000 alerts. Considering only almost 2.5k binaries. In 252 99:59:59,999 --> 99:59:59,999 the second case we found the border binaries, the parsers and we statically 253 99:59:59,999 --> 99:59:59,999 analyzed only them, and the system generated 9.3k alerts. Notice that in this 254 99:59:59,999 --> 99:59:59,999 case, since we don't know how the user input is introduced, like in this 255 99:59:59,999 --> 99:59:59,999 experiment, we consider every IPC that we find in the binary as a possible source of 256 99:59:59,999 --> 99:59:59,999 user input. And this is true for all of them. In the third case we ran the BDG but 257 99:59:59,999 --> 99:59:59,999 we consider each binaries independently. Which means that we don't propagate 258 99:59:59,999 --> 99:59:59,999 constraints and we run a static single corner analysis on each one of them. And 259 99:59:59,999 --> 99:59:59,999 the system generated almost 15000 alerts. Finally, we run Karonte and the generated 260 99:59:59,999 --> 99:59:59,999 alerts were only 74. We also run a larger scale analysis on 899 firmware samples. 261 99:59:59,999 --> 99:59:59,999 And we found that almost 40% of them were multi binary, which means that the network 262 99:59:59,999 --> 99:59:59,999 functionalities were carried on by more than one binary. And the system generated 263 99:59:59,999 --> 99:59:59,999 1000 alerts. Now, there is a lot going on in this table, like details are on the 264 99:59:59,999 --> 99:59:59,999 paper. Here in this presentation I just go through some as I'll motivate. So we found 265 99:59:59,999 --> 99:59:59,999 that on average, a firmware contains 4 border binaries. A BDG contains 5 binaries 266 99:59:59,999 --> 99:59:59,999 and some BDG have more than 10 binaries. Also, we plot some statistics and we found 267 99:59:59,999 --> 99:59:59,999 that 80% of the firmware were analysed within a day, as you can see from the top 268 99:59:59,999 --> 99:59:59,999 left figure. However, experiments presented a great variance which we found 269 99:59:59,999 --> 99:59:59,999 was due to implementation details. For instance we found that angr would take 270 99:59:59,999 --> 99:59:59,999 more than seven hours to build some CFGs. And sometimes they were due to a high 271 99:59:59,999 --> 99:59:59,999 number of data keys. Also, we found that the number of paths, as you can see from 272 99:59:59,999 --> 99:59:59,999 this second picture from the top, the number of paths do not have an impact on 273 99:59:59,999 --> 99:59:59,999 the total time. And as you can see from the bottom two pictures, performance not 274 99:59:59,999 --> 99:59:59,999 heavily affected by firmware size. Firmware size here we mean the number of 275 99:59:59,999 --> 99:59:59,999 binaries in a firmware sample and the total number of basic blocks. So let's see 276 99:59:59,999 --> 99:59:59,999 how to run Karonte. The procedure is pretty straightforward. So first you get a 277 99:59:59,999 --> 99:59:59,999 firmware sample. You create a configuration file containing information 278 99:59:59,999 --> 99:59:59,999 of the firmware sample and then you run it. So let's see how. So this is an 279 99:59:59,999 --> 99:59:59,999 example of a configuration file. It contains the information, but most of them 280 99:59:59,999 --> 99:59:59,999 are optional. The only ones that are not are this one: Firmware path, that is the 281 99:59:59,999 --> 99:59:59,999 path to your firmware. And this too, the architecture of the firmware and the base 282 99:59:59,999 --> 99:59:59,999 address if the firmware is a blob, is a firmware blob. All the other fields are 283 99:59:59,999 --> 99:59:59,999 optional. And you can set them if you have some information about the firmware. A 284 99:59:59,999 --> 99:59:59,999 detailed explanation of all of these fields are on our GitHub repo. Once you 285 99:59:59,999 --> 99:59:59,999 set the configuration file, you can run Karonte. Now we provide a Docker 286 99:59:59,999 --> 99:59:59,999 container, you can find the link on our GitHub repo. And I'm gonna run it, but 287 99:59:59,999 --> 99:59:59,999 it's not gonna finish because it's gonna take several hours. But all you have to do 288 99:59:59,999 --> 99:59:59,999 is merely... *typing noises* just run it on the configuration file and it's gonna 289 99:59:59,999 --> 99:59:59,999 do each step that we saw. Eventually I'm going to stop it because it's going to 290 99:59:59,999 --> 99:59:59,999 take several hours anyway. Eventually it will produce a result file that... I ran 291 99:59:59,999 --> 99:59:59,999 this yesterday so you can see it here. There is a lot going on here. I'm just 292 99:59:59,999 --> 99:59:59,999 gonna go through some important like information. So one thing that you can see 293 99:59:59,999 --> 99:59:59,999 is that these are the border binaries that Karonte found. Now, there might be some 294 99:59:59,999 --> 99:59:59,999 false positives. I'm not sure how many there are here. But as long as there are 295 99:59:59,999 --> 99:59:59,999 no false negatives or the number is very low, it's fine. It's good. In this case, 296 99:59:59,999 --> 99:59:59,999 wait. Oh, I might have removed something. All right, here, perfect. In this case, 297 99:59:59,999 --> 99:59:59,999 this guy httpd is a true positive, which is the web server that we were talking 298 99:59:59,999 --> 99:59:59,999 before. Then we have the BDG. In this case, we can see that Karonte found that 299 99:59:59,999 --> 99:59:59,999 httpd communicates with two different binaries, fileaccess.cgi and cgibin. Then 300 99:59:59,999 --> 99:59:59,999 we have information about the CPFs. For instance, here we can see that. Sorry. So 301 99:59:59,999 --> 99:59:59,999 we can see here that httpd has 28 data keys. And that the semantics CPF found 27 302 99:59:59,999 --> 99:59:59,999 of them and then there might be one other here or somewhere that I don't see . 303 99:59:59,999 --> 99:59:59,999 Anyway. And then we have a list of alerts. Now, thanks. Now, some of those may be 304 99:59:59,999 --> 99:59:59,999 duplicates because of loops, so you can go ahead and inspect all of them manually. 305 99:59:59,999 --> 99:59:59,999 But I wrote a utility that you can use, which is basically it's gonna filter out 306 99:59:59,999 --> 99:59:59,999 all the loops for you. Now to remember how I called it. This guy? Yeah. And you can 307 99:59:59,999 --> 99:59:59,999 see that in total it generated, the system generated 6... 7... 8 alerts. So let's see 308 99:59:59,999 --> 99:59:59,999 one of them. Oh, and I recently realized that the path that I'm reporting on the 309 99:59:59,999 --> 99:59:59,999 log. It's not the path from the setter binary to the getter binary, to the sink. 310 99:59:59,999 --> 99:59:59,999 But it's only related to the getter binary up to the sink. I'm gonna fix this in the 311 99:59:59,999 --> 99:59:59,999 next days and report the whole paths. Anyway. So here we can see that the key 312 99:59:59,999 --> 99:59:59,999 content type contains user input and it's passed in an unsafe way to the sink 313 99:59:59,999 --> 99:59:59,999 address at this address. Now. And the binary in question is called 314 99:59:59,999 --> 99:59:59,999 fileaccess.cgi. So we can see what happens there. *keyboard noises* If you see here, 315 99:59:59,999 --> 99:59:59,999 we have a string copy that copies the content of haystack to destination, 316 99:59:59,999 --> 99:59:59,999 haystack comes basically from this getenv. And if you see destination comes as 317 99:59:59,999 --> 99:59:59,999 parameter from this function and return and these and this by for it's as big as 318 99:59:59,999 --> 99:59:59,999 0x68 bytes. And this turned out to be actually a positive. OK. So in summary, we 319 99:59:59,999 --> 99:59:59,999 presented a strategy to track data flow across different binaries. We evaluated 320 99:59:59,999 --> 99:59:59,999 our system on 952 firmware samples and some takeaways. Analyzing firmware is not 321 99:59:59,999 --> 99:59:59,999 easy and vulnerabilities persist. We found out that firmware are made of 322 99:59:59,999 --> 99:59:59,999 interconnected components and static analysis can still be used to efficiently 323 99:59:59,999 --> 99:59:59,999 find vulnerabilities at scale and finding that communication is key for precision. 324 99:59:59,999 --> 99:59:59,999 Here's a list of bibliography that I use throughout the presentation and I'm gonna 325 99:59:59,999 --> 99:59:59,999 take questions. [filler, please remove in amara] 326 99:59:59,999 --> 99:59:59,999 *applause* [filler, please remove in amara] 327 99:59:59,999 --> 99:59:59,999 Herald: So thank you, Nilo, for a very interesting talk. If you have questions, 328 99:59:59,999 --> 99:59:59,999 we have three microphones one, two and three. If you have a question, please go 329 99:59:59,999 --> 99:59:59,999 head to the microphone and we'll take your question. Yes. Microphone number two. 330 99:59:59,999 --> 99:59:59,999 Q: Do you rely on imports from libc or something like that or do you have some 331 99:59:59,999 --> 99:59:59,999 issues with like statically linked binaries, stripped binaries or is it all 332 99:59:59,999 --> 99:59:59,999 semantic analysis of a function? Nilo: So. Okay. We use angr. So for 333 99:59:59,999 --> 99:59:59,999 example, if you have an indirect call, we use angr to figure out, what's the target? 334 99:59:59,999 --> 99:59:59,999 And to answer your question like if you use libc some CPFs do, for instance, then 335 99:59:59,999 --> 99:59:59,999 environment CPF do any checks, if the setenv or getenv functions are called. But 336 99:59:59,999 --> 99:59:59,999 also we use the semantic CPF, which basically in cases where information are 337 99:59:59,999 --> 99:59:59,999 missing like there is no such thing as libc or some vendors reimplemented their 338 99:59:59,999 --> 99:59:59,999 own functions. We use the CPF to actually try to understand the semantics of the 339 99:59:59,999 --> 99:59:59,999 function and understand if it's, for example, a custom setenv. 340 99:59:59,999 --> 99:59:59,999 Q: Yeah, thanks. Herald: Microphone number three. 341 99:59:59,999 --> 99:59:59,999 Q: In embedded environments you often have also that the getter might work on a DMA, 342 99:59:59,999 --> 99:59:59,999 some kind of vendor driver on a DMA. Are you considering this? And second part of 343 99:59:59,999 --> 99:59:59,999 the question, how would you then distinguish this from your generic IPC? 344 99:59:59,999 --> 99:59:59,999 Because I can imagine that they look very similar in the actual code. 345 99:59:59,999 --> 99:59:59,999 Nilo: So if I understand correctly your question, you mention a case of MMIO where 346 99:59:59,999 --> 99:59:59,999 some data is retrieved directly from some address in memory. So what we found is 347 99:59:59,999 --> 99:59:59,999 that these addresses are usually hardcoded somewhere. So the vendor knows that, for 348 99:59:59,999 --> 99:59:59,999 example, from this address A to this address B if some data is some data from 349 99:59:59,999 --> 99:59:59,999 this peripheral. So when we find that some hardcoded address, like we think that this 350 99:59:59,999 --> 99:59:59,999 is like some read from some interesting data. 351 99:59:59,999 --> 99:59:59,999 Q: Okay. And this would be also distinguishable from your sort of CPF, the 352 99:59:59,999 --> 99:59:59,999 generic CPF would be distinguishable... Nilo: Yeah. Yeah, yeah. 353 99:59:59,999 --> 99:59:59,999 Q: ...from a DMA driver by using this fixed address assuming. 354 99:59:59,999 --> 99:59:59,999 Nilo: Yeah. That's what the semantic CPF does, among the other things. 355 99:59:59,999 --> 99:59:59,999 Q: Okay. Thank you. Nilo: Sure. 356 99:59:59,999 --> 99:59:59,999 Herald: Another question for microphone number 3. 357 99:59:59,999 --> 99:59:59,999 Q: What's the license for Karonte? Nilo: Sorry? 358 99:59:59,999 --> 99:59:59,999 Q: I checked the software license, I checked the git repository and there is no 359 99:59:59,999 --> 99:59:59,999 license like at all. Nilo: That is a very good question. I 360 99:59:59,999 --> 99:59:59,999 haven't thought about it yet. I will. Herald: Any more questions from here or 361 99:59:59,999 --> 99:59:59,999 from the Internet? Okay. Then a big round of applause to Nilo again for your talk. 362 99:59:59,999 --> 99:59:59,999 *postroll music* [filler, please remove in amara] 363 99:59:59,999 --> 99:59:59,999 Subtitles created by many many volunteers and the c3subtitles.de team. Join us, and help us!