9:59:59.000,9:59:59.000 *preroll music*[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 Herald: Our next speaker for today is a[br]computer science PhD student at UC Santa 9:59:59.000,9:59:59.000 Barbara. He is a member of the Shellfish[br]Hacking Team and he's also the organizer 9:59:59.000,9:59:59.000 of the IECTF Hacking Competition. Please[br]give a big round of applause to Nilo 9:59:59.000,9:59:59.000 Redini.[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 *applause*[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 Nilo: Thanks for the introduction, hello[br]to everyone. My name is Nilo, and today 9:59:59.000,9:59:59.000 I'm going to present you my work Koronte:[br]identifying multi-binary vulnerabilities 9:59:59.000,9:59:59.000 in embedded firmware at scale. This work[br]is a co-joint effort between me and 9:59:59.000,9:59:59.000 several of my colleagues at University of[br]Santa Barbara and ASU. This talk is going 9:59:59.000,9:59:59.000 to be about IoT devices. So before[br]starting, let's see an overview about IoT 9:59:59.000,9:59:59.000 devices. IoT devices are everywhere. As[br]the research suggests, they will reach the 9:59:59.000,9:59:59.000 20 billion units by the end of the next[br]year. And a recent study conducted this 9:59:59.000,9:59:59.000 year in 2019 on 16 million households[br]showed that more than 70 percent of homes 9:59:59.000,9:59:59.000 in North America already have an IoT[br]network connected device. IoT devices make 9:59:59.000,9:59:59.000 everyday life smarter. You can literally[br]say "Alexa, I'm cold" and Alexa will 9:59:59.000,9:59:59.000 interact with the thermostat and increase[br]the temperature of your room. Usually the 9:59:59.000,9:59:59.000 way we interact with the IoT devices is[br]through our smartphone. We send a request 9:59:59.000,9:59:59.000 to the local network, to some device,[br]router or door lock, or we might send the 9:59:59.000,9:59:59.000 same request through a cloud endpoint,[br]which is usually managed by the vendor of 9:59:59.000,9:59:59.000 the IoT device. Another way is through the[br]IoT hubs, smartphone will send the request 9:59:59.000,9:59:59.000 to some IoT hub, which in turn will send[br]the request to some other IoT devices. As 9:59:59.000,9:59:59.000 you can imagine, IoT devices use and[br]collect our data and some data is more 9:59:59.000,9:59:59.000 sensitive than other. For instance, think[br]of all the data that is collected by my 9:59:59.000,9:59:59.000 lightbulb or data that is collected by our[br]security camera. As such, IoT devices can 9:59:59.000,9:59:59.000 compromise people's safety and privacy.[br]Things, for example, about the security 9:59:59.000,9:59:59.000 implication of a faulty smartlock or the[br]brakes of your smart car. So the question 9:59:59.000,9:59:59.000 that we asked is: Are IoT devices secure?[br]Well, like everything else, they are not. 9:59:59.000,9:59:59.000 OK, in 2016 the Mirai botnet compromised[br]and leveraged millions of IoT devices to 9:59:59.000,9:59:59.000 disrupt core Internet services such as[br]Twitter, GitHub and Netflix. And in 2018, 9:59:59.000,9:59:59.000 154 vulnerabilities affecting IoT devices[br]were published, which represented an 9:59:59.000,9:59:59.000 increment of 15% compared to 2017 and an[br]increase of 115% compared to 2016. So then 9:59:59.000,9:59:59.000 we wonder: So why is it hard to secure IoT[br]devices? To answer this question we have 9:59:59.000,9:59:59.000 to look up how IoT devices work and they[br]are made. Usually when you remove all the 9:59:59.000,9:59:59.000 plastic and peripherals IoT devices look[br]like this. A board with some chips laying 9:59:59.000,9:59:59.000 on it. Usually you can find the big chip,[br]the microcontroller which runs the 9:59:59.000,9:59:59.000 firmware and one or more peripheral[br]controllers which interact with external 9:59:59.000,9:59:59.000 peripherals such as the motor of, your[br]smart lock or cameras. Though the design 9:59:59.000,9:59:59.000 is generic, implementations are very[br]diverse. For instance, firmware may run on 9:59:59.000,9:59:59.000 several different architectures such as[br]ARM, MIPS, x86, PowerPC and so forth. And 9:59:59.000,9:59:59.000 sometimes they are even proprietary, which[br]means that if a security analyst wants to 9:59:59.000,9:59:59.000 understand what's going on in the[br]firmware, he'll have a hard time if he 9:59:59.000,9:59:59.000 doesn't have the vendor specifics. Also,[br]they're operating in environments with 9:59:59.000,9:59:59.000 limited resources, which means that they[br]run small and optimized code. For 9:59:59.000,9:59:59.000 instance, vendors might implement their[br]own version of some known algorithm in an 9:59:59.000,9:59:59.000 optimized way. Also, IoT devices manage[br]external peripherals that often use custom 9:59:59.000,9:59:59.000 code. Again, with peripherals we mean like[br]cameras, sensors and so forth. The 9:59:59.000,9:59:59.000 firmware of IoT devices can be either[br]Linux based or a blob firmware, Linux 9:59:59.000,9:59:59.000 based are by far the most common. A study[br]showed that 86% of firmware are based on 9:59:59.000,9:59:59.000 Linux and on the other hand, blobs[br]firmware are usually operating systems and 9:59:59.000,9:59:59.000 user applications packaged in a single[br]binary. In any case, firmware samples are 9:59:59.000,9:59:59.000 usually made of multiple components. For[br]instance, let's say that you have your 9:59:59.000,9:59:59.000 smart phone and you send a request to your[br]IoT device. This request will be received 9:59:59.000,9:59:59.000 by a binary which we term as body binary,[br]which in this example is an webserver. The 9:59:59.000,9:59:59.000 request will be received, parsed, and then[br]it might be sent to another binary code, 9:59:59.000,9:59:59.000 the handler binary, which will take the[br]request, work on it, produce an answer, 9:59:59.000,9:59:59.000 send it back to the webserver, which in[br]turn would produce a response to send to 9:59:59.000,9:59:59.000 the smartphone. So to come back to the[br]question why is it hard to secure IoT 9:59:59.000,9:59:59.000 devices? Well, the answer is because IoT[br]devices are in practice very diverse. Of 9:59:59.000,9:59:59.000 course, there have been various work that[br]have been proposed to analyze and secure 9:59:59.000,9:59:59.000 firmware for IoT devices. Some of them[br]using static analysis. Others using 9:59:59.000,9:59:59.000 dynamic analysis and several others using[br]a combination of both. Here I wrote 9:59:59.000,9:59:59.000 several of them. Again at the end of the[br]presentation there is a bibliography with 9:59:59.000,9:59:59.000 the title of these works. Of course, all[br]these approaches have some problems. For 9:59:59.000,9:59:59.000 instance, the current dynamic analysis are[br]hard to apply to scale because of the 9:59:59.000,9:59:59.000 customized environments that IoT devices[br]work on. Usually when you try to 9:59:59.000,9:59:59.000 dynamically execute a firmware, it's gonna[br]check if the peripherals are connected and 9:59:59.000,9:59:59.000 are working properly. In a case where you[br]can't have the peripherals, it's gonna be 9:59:59.000,9:59:59.000 hard to actually run the firmware. Also[br]current static analysis approaches are 9:59:59.000,9:59:59.000 based on what we call the single binary[br]approach, which means that binaries from a 9:59:59.000,9:59:59.000 firmware are taken individually and[br]analysed. This approach might produce many 9:59:59.000,9:59:59.000 false positives. For instance, so let's[br]say again that we have our two binaries. 9:59:59.000,9:59:59.000 This is actually an example that we found[br]on one firmware, so the web server will 9:59:59.000,9:59:59.000 take the user request, will parse the[br]request and produce some data, will set 9:59:59.000,9:59:59.000 this data to an environment variable and[br]eventually will execute the handle binary. 9:59:59.000,9:59:59.000 Now, if you see the parsing function[br]contains a string compare which checks if 9:59:59.000,9:59:59.000 some keyword is present in the request.[br]And if so, it just returns the whole 9:59:59.000,9:59:59.000 request. Otherwise, it will constrain the[br]size of the request to 128 bytes and 9:59:59.000,9:59:59.000 return it. The handler binary in turn when[br]spawned will receive the data by doing a 9:59:59.000,9:59:59.000 getenv on the query string, but also will[br]getenv on another environment variable 9:59:59.000,9:59:59.000 which in this case is not user controlled[br]and they user cannot influence the content 9:59:59.000,9:59:59.000 of this variable. Then it's gonna call[br]function process_request. This function 9:59:59.000,9:59:59.000 eventually will do two string copies. One[br]from the user data, the other one from the 9:59:59.000,9:59:59.000 log path on two different local variables[br]that are 128 bytes long. Now in the first 9:59:59.000,9:59:59.000 case, as we have seen before, the data can[br]be greater than 128 bytes and this string 9:59:59.000,9:59:59.000 copy may result in a bug. While in the[br]second case it will not. Because here we 9:59:59.000,9:59:59.000 assume that the system handles its own[br]data in a good manner. So throughout this 9:59:59.000,9:59:59.000 work, we're gonna call the first type of[br]binary, the setter binary, which means 9:59:59.000,9:59:59.000 that it is the binary that takes the data[br]and set the data for another binary to be 9:59:59.000,9:59:59.000 consumed. And the second type of binary we[br]called them the getter binary. So the 9:59:59.000,9:59:59.000 current bug finding tools are inadequate[br]because other bugs are left undiscovered 9:59:59.000,9:59:59.000 if the analysis only consider those[br]binaries that received network requests or 9:59:59.000,9:59:59.000 they're likely to produce many false[br]positives if the analysis considers all of 9:59:59.000,9:59:59.000 them individually. So then we wonder how[br]these different components actually 9:59:59.000,9:59:59.000 communicate. They communicate through what[br]are called interprocess communication, 9:59:59.000,9:59:59.000 which basically it's a finite set of[br]paradigms used by binaries to communicate 9:59:59.000,9:59:59.000 such as files, environment variables, MMIO[br]and so forth. All these pieces are 9:59:59.000,9:59:59.000 represented by data keys, which are file[br]names, or in the case of the example 9:59:59.000,9:59:59.000 before here on the right, it's the query[br]string environment variable. Each binary 9:59:59.000,9:59:59.000 that relies on some shared data must know[br]the endpoint where such data will be 9:59:59.000,9:59:59.000 available, for instance, again, like a[br]file name or like even a socket endpoint 9:59:59.000,9:59:59.000 or the environment variable. This means[br]that usually, data keys are coded in the 9:59:59.000,9:59:59.000 program itself, as we saw before. To find[br]bugs in firmware, in a precise manner, we 9:59:59.000,9:59:59.000 need to track how user data is introduced[br]and propagated across the different 9:59:59.000,9:59:59.000 binaries. Okay, let's talk about our work.[br]Before you start talking about Karonte, we 9:59:59.000,9:59:59.000 define our threat model. We hypotesized[br]that attacker sends arbitrary requests 9:59:59.000,9:59:59.000 over the network, both LAN and WAN[br]directly to the IoT device. Though we said 9:59:59.000,9:59:59.000 before that sometimes IoT device can[br]communicate through the clouds, research 9:59:59.000,9:59:59.000 showed that some form of local[br]communication is usually available, for 9:59:59.000,9:59:59.000 instance, during the setup phase of the[br]device. Karonte is defined as a static 9:59:59.000,9:59:59.000 analysis tool that tracks data flow across[br]multiple binaries, to find 9:59:59.000,9:59:59.000 vulnerabilities. Let's see how it works.[br]So the first step, Karonte find those 9:59:59.000,9:59:59.000 binaries that introduce the user input[br]into the firmware. We call these border 9:59:59.000,9:59:59.000 binaries, which are the binaries, that[br]basically interface the device to the 9:59:59.000,9:59:59.000 outside world. Which in the example is our[br]web server. Then it tracks how a data is 9:59:59.000,9:59:59.000 shared with other binaries within the[br]firmware sample. Which we'll understand in 9:59:59.000,9:59:59.000 this example, the web server communicates[br]with the handle binary, and builds what we 9:59:59.000,9:59:59.000 call the BDG. BDG which stands for binary[br]dependency graph. It's basically a graph 9:59:59.000,9:59:59.000 representation of the data dependencies[br]among different binaries. Then we detect 9:59:59.000,9:59:59.000 vulnerabilities that arise from the misuse[br]of the data using the BDG. This is an 9:59:59.000,9:59:59.000 overview of our system. We start by taking[br]a packed firmware, we unpack it. We find 9:59:59.000,9:59:59.000 the border binaries. Then we build the[br]binary dependency graph, which relies on a 9:59:59.000,9:59:59.000 set of CPFs, as we will see soon. CPF[br]stands for Communication Paradigm Finder. 9:59:59.000,9:59:59.000 Then we find the specifics of the[br]communication, for instance, like the 9:59:59.000,9:59:59.000 constraints applied to the data that is[br]shared through our module multi-binary 9:59:59.000,9:59:59.000 data-flow analysis. Eventually we run our[br]insecure interaction detection module, 9:59:59.000,9:59:59.000 which basically takes all the information[br]and produces alerts. Our system is 9:59:59.000,9:59:59.000 completely static and relies on our static[br]taint engine. So let's see each one of 9:59:59.000,9:59:59.000 these steps, more in details. The[br]unpacking procedure is pretty easy, we use 9:59:59.000,9:59:59.000 the off-the-shelf firmware unpacking tool[br]binwalk. And then we have to find the 9:59:59.000,9:59:59.000 border binaries. Now we see that border[br]binaries basically are binaries that 9:59:59.000,9:59:59.000 receive data from the network. And we[br]hypotesize that will contain parsers to 9:59:59.000,9:59:59.000 validate the data that they received. So[br]in order to find them, we have to find 9:59:59.000,9:59:59.000 parsers which accept data from network and[br]parse this data. To find parsers we rely 9:59:59.000,9:59:59.000 on related work, which basically uses a[br]few metrics and define through a number 9:59:59.000,9:59:59.000 the likelihood for a function to contain[br]parsing capabilities. These metrics that 9:59:59.000,9:59:59.000 we used are number of basic blocks, number[br]of memory comparison operations and number 9:59:59.000,9:59:59.000 of branches. Now while these define[br]parsers, we also have to find if a binary 9:59:59.000,9:59:59.000 takes data from the network. As such, we[br]define two more metrics. The first one, we 9:59:59.000,9:59:59.000 check if binary contains any network[br]related keywords as SOAP, http and so 9:59:59.000,9:59:59.000 forth. And then we check if there exists a[br]data flow between read from socket and a 9:59:59.000,9:59:59.000 memory comparison operation. Once for each[br]function, we got all these metrics, we 9:59:59.000,9:59:59.000 compute what is called a parsing score,[br]which basically is just a sum of products. 9:59:59.000,9:59:59.000 Once we got a parsing score for each[br]function in a binary, we represent the 9:59:59.000,9:59:59.000 binary with its highest parsing score.[br]Once we got that for each binary in the 9:59:59.000,9:59:59.000 firmware we cluster them using the DBSCAN[br]density based algorithm and consider the 9:59:59.000,9:59:59.000 cluster with the highest parsing score as[br]containing the set of border binaries. 9:59:59.000,9:59:59.000 After this, we build the binary dependency[br]graph. Again the binary dependency graph 9:59:59.000,9:59:59.000 represents the data dependency among the[br]binaries in a firmware sample. For 9:59:59.000,9:59:59.000 instance, this simple graph will tell us[br]that a binary A communicates with binary C 9:59:59.000,9:59:59.000 using files and the same binary A[br]communicates with another binary B using 9:59:59.000,9:59:59.000 environment variables. Let's see how this[br]works. So we start from the identified 9:59:59.000,9:59:59.000 border binaries and then we taint the data[br]compared against network related keywords 9:59:59.000,9:59:59.000 that we found and run a static analysis,[br]static taint analysis to detect whether 9:59:59.000,9:59:59.000 the binary relies on any IPC paradigm to[br]share the data. If we find that it does, 9:59:59.000,9:59:59.000 we establish if the binary is a setter or[br]a getter, which again means that if the 9:59:59.000,9:59:59.000 binary is setting the data to be consumed[br]by another binary, or if the binary 9:59:59.000,9:59:59.000 actually gets the data and consumes it.[br]Then we retrieve the employed data key 9:59:59.000,9:59:59.000 which in the example before was the[br]keyword QUERY_STRING. And finally we scan 9:59:59.000,9:59:59.000 the firmware sample to find other binaries[br]that may rely on the same data keys and 9:59:59.000,9:59:59.000 schedule them for further analysis. To[br]understand whether a binary relies on any 9:59:59.000,9:59:59.000 IPC, we use what we call CPFs, which again[br]means communication paradigm finder. We 9:59:59.000,9:59:59.000 design a CPF for each IPC. And the CPFs[br]are also used to find the same data keys 9:59:59.000,9:59:59.000 within the firmware sample. We also[br]provide Karonte with a generic CPF to 9:59:59.000,9:59:59.000 cover those cases where the IPC is[br]unknown. Or those cases were the vendor 9:59:59.000,9:59:59.000 implemented their own versions of some[br]IPC. So for example they don't use the 9:59:59.000,9:59:59.000 setenv. But they implemented their own[br]setenv. The idea behind this generic CPF 9:59:59.000,9:59:59.000 that we call the semantic CPF is that data[br]keys has to be used as index to set, or to 9:59:59.000,9:59:59.000 get some data in this simple example. So[br]let's see how the BDG algorithm works. We 9:59:59.000,9:59:59.000 start from the body binary, which again[br]will start from the server request and 9:59:59.000,9:59:59.000 will pass the URI and we see that here. it[br]runs a string comparison against some 9:59:59.000,9:59:59.000 network related keyword. As such, we taint[br]the variable P. And we see that the 9:59:59.000,9:59:59.000 variable P is returned from the function[br]to these two different points. As such, we 9:59:59.000,9:59:59.000 continue. And now we see that data gets[br]tainted and the variable data, it's passed 9:59:59.000,9:59:59.000 to the function setenv. At this point, the[br]environment CPF will understand that 9:59:59.000,9:59:59.000 tainted data is passed, is set to an[br]environment variable and will understand 9:59:59.000,9:59:59.000 that this binary is indeed the setter[br]binary that uses the environment. Then we 9:59:59.000,9:59:59.000 retrieve the data key QUERY_STRING and[br]we'll search within the firmware sample 9:59:59.000,9:59:59.000 all the other binaries that rely on the[br]same data key. And it will find that this 9:59:59.000,9:59:59.000 binary relies on the same data key and[br]will schedule this for further analysis. 9:59:59.000,9:59:59.000 After this algorithm we build the BDG by[br]creating edges between setters and getters 9:59:59.000,9:59:59.000 for each data key. The multi binary data[br]flow analysis uses the BDG to find and 9:59:59.000,9:59:59.000 propagate the data constraints from a[br]setter to a getter. Now, through this we 9:59:59.000,9:59:59.000 apply only the least three constraints,[br]which means that ideally between two 9:59:59.000,9:59:59.000 program points, there might be an infinite[br]number of parts and ideally in theory an 9:59:59.000,9:59:59.000 infinite amount of constraints that we can[br]propagate to the setter binary to the 9:59:59.000,9:59:59.000 getter binary. But since our goal here is[br]to find bugs, we only propagate the least 9:59:59.000,9:59:59.000 strict set of constraints. Let's see an[br]example. So again, we have our two 9:59:59.000,9:59:59.000 binaries and we see that the variable that[br]is passed to the setenv function is data, 9:59:59.000,9:59:59.000 which comes from two different parts from[br]the parse URI function. In the first case, 9:59:59.000,9:59:59.000 the data that its passed is unconstrained[br]one in the second case, a line 8 is 9:59:59.000,9:59:59.000 constrained to be at most 128 bytes. As[br]such, we only propagate the constraints of 9:59:59.000,9:59:59.000 the first guy. In turn, the getter binary[br]will retrieve this variable from the 9:59:59.000,9:59:59.000 environment and set the variable query.[br]Oh, sorry. Which in this case will be 9:59:59.000,9:59:59.000 unconstrained. Insecure interaction[br]detection run a static taint analysis and 9:59:59.000,9:59:59.000 check whether tainted data can reach a[br]sink in an unsafe way. We consider as 9:59:59.000,9:59:59.000 sinks memcpy like functions which are[br]functions that implement semantically 9:59:59.000,9:59:59.000 equivalent memcyp, strcpy and so forth. We[br]raise alert if we see that there is a 9:59:59.000,9:59:59.000 dereference of a tainted variable and if[br]we see there are comparisons of tainted 9:59:59.000,9:59:59.000 variables in loop conditions to detect[br]possible DoS vulnerabilities. Let's see an 9:59:59.000,9:59:59.000 example again. So we got here. We know[br]that our query variable is tainted and 9:59:59.000,9:59:59.000 it's unconstrained. And then we follow the[br]taint in the function process_request, 9:59:59.000,9:59:59.000 which we see will eventually copy the data[br]from q to arg. Now we see that arg is 128 9:59:59.000,9:59:59.000 bytes long while q is unconstrained and[br]therefore we generate an alert here. Our 9:59:59.000,9:59:59.000 static taint engine is based on BootStomp[br]and is completely based on symbolic 9:59:59.000,9:59:59.000 execution, which means that the taint is[br]propagated following the program data 9:59:59.000,9:59:59.000 flow. Let's see an example. So assuming[br]that we have this code, the first 9:59:59.000,9:59:59.000 instruction takes the result from some[br]seed function that might return for 9:59:59.000,9:59:59.000 instance, some user input. And in a[br]symbolic world, what we do is we create a 9:59:59.000,9:59:59.000 symbolic variable ty and assign to it a[br]tainted variable that we call TAINT_ty, 9:59:59.000,9:59:59.000 which is the taint target. The next[br]destruction X takes the value ty plus 5 9:59:59.000,9:59:59.000 and a symbolic word. We just follow the[br]data flow and x gets assigned TAINT_ty 9:59:59.000,9:59:59.000 plus 5 which effectively taints also X. If[br]at some point X is overwritten with some 9:59:59.000,9:59:59.000 constant data, the taint is automatically[br]removed. In its original design, 9:59:59.000,9:59:59.000 BootStomp, the taint is removed also when[br]data is constrained. For instance, here we 9:59:59.000,9:59:59.000 can see that the variable n is tainted but[br]then is constrained between two values 0 9:59:59.000,9:59:59.000 and 255. And therefore, the taint is[br]removed. In our taint engine we have two 9:59:59.000,9:59:59.000 additions. We added a path prioritization[br]strategy and we add taint dependencies. 9:59:59.000,9:59:59.000 The path prioritization strategy valorizes[br]paths that propagate the taint and 9:59:59.000,9:59:59.000 deprioritizes those that remove it. For[br]instance, say again that some user input 9:59:59.000,9:59:59.000 comes from some function and the variable[br]user input gets tainted. Gets tainted and 9:59:59.000,9:59:59.000 then is passed to another function called[br]parse. Here, if you see there are possibly 9:59:59.000,9:59:59.000 an infinite number of symbolic parts in[br]this while. But only 1 will return tainted 9:59:59.000,9:59:59.000 data. While the others won't. So the path[br]prioritization strategy valorizes this 9:59:59.000,9:59:59.000 path instead of the others. This has been[br]implemented by finding basic blocks within 9:59:59.000,9:59:59.000 a function that return a nonconstant data.[br]And if one is found, we follow its return 9:59:59.000,9:59:59.000 before considering the others. Taint[br]dependencies allows smart untaint 9:59:59.000,9:59:59.000 strategies. Let's see again the example.[br]So we know that user input here is 9:59:59.000,9:59:59.000 tainted, is then parsed and then we see[br]that it's length is checked and stored in 9:59:59.000,9:59:59.000 a variable n. Its size is checked and if[br]it's higher than 512 bytes, the function 9:59:59.000,9:59:59.000 will return. Otherwise it copies the data.[br]Now in this case, it might happen that if 9:59:59.000,9:59:59.000 this strlen function is not analyzed[br]because of some static analysis input 9:59:59.000,9:59:59.000 decisions, the taint tag of cmd might be[br]different from the taint tag of n and in 9:59:59.000,9:59:59.000 this case, though, and gets untainted, cmd[br]is not untainted and the strcpy can raise, 9:59:59.000,9:59:59.000 sorry, carries a false positive. So to fix[br]this problem. Basically we create a 9:59:59.000,9:59:59.000 dependency between the taint tag of n and[br]the taint tag of cmd. And when n gets 9:59:59.000,9:59:59.000 untainted, cmd gets untainted as well. So[br]we don't have more false positives. This 9:59:59.000,9:59:59.000 procedure is automatic and we find[br]functions that implement streamlined 9:59:59.000,9:59:59.000 semantically equivalent code and create[br]taint tag dependencies. OK. Let's see our 9:59:59.000,9:59:59.000 evaluation. We ran 3 different evaluations[br]on 2 different data sets. The first one 9:59:59.000,9:59:59.000 composed by 53 latest firmware samples[br]from seven vendors and a second one 899 9:59:59.000,9:59:59.000 firmware gathered from related work. In[br]the first case, we can see that the total 9:59:59.000,9:59:59.000 number of binaries considered are 8.5k,[br]few more than that. And our system 9:59:59.000,9:59:59.000 generated 87 alerts of which 51 were found[br]to be true positive and 34 of them were 9:59:59.000,9:59:59.000 multibinary vulnerabilities, which means[br]that the vulnerability was found by 9:59:59.000,9:59:59.000 tracking the data flow from the setter to[br]the getter binary. We also ran a 9:59:59.000,9:59:59.000 comparative evaluation, which basically we[br]tried to measure the effort that an 9:59:59.000,9:59:59.000 analyst would go through in analyzing[br]firmware using different strategies. In 9:59:59.000,9:59:59.000 the first one, we consider each and every[br]binary in the firmware sample 9:59:59.000,9:59:59.000 independently and run the analysis for up[br]to seven days for each firmware. The 9:59:59.000,9:59:59.000 system generated almost 21000 alerts.[br]Considering only almost 2.5k binaries. In 9:59:59.000,9:59:59.000 the second case we found the border[br]binaries, the parsers and we statically 9:59:59.000,9:59:59.000 analyzed only them, and the system[br]generated 9.3k alerts. Notice that in this 9:59:59.000,9:59:59.000 case, since we don't know how the user[br]input is introduced, like in this 9:59:59.000,9:59:59.000 experiment, we consider every IPC that we[br]find in the binary as a possible source of 9:59:59.000,9:59:59.000 user input. And this is true for all of[br]them. In the third case we ran the BDG but 9:59:59.000,9:59:59.000 we consider each binaries independently.[br]Which means that we don't propagate 9:59:59.000,9:59:59.000 constraints and we run a static single[br]corner analysis on each one of them. And 9:59:59.000,9:59:59.000 the system generated almost 15000 alerts.[br]Finally, we run Karonte and the generated 9:59:59.000,9:59:59.000 alerts were only 74. We also run a larger[br]scale analysis on 899 firmware samples. 9:59:59.000,9:59:59.000 And we found that almost 40% of them were[br]multi binary, which means that the network 9:59:59.000,9:59:59.000 functionalities were carried on by more[br]than one binary. And the system generated 9:59:59.000,9:59:59.000 1000 alerts. Now, there is a lot going on[br]in this table, like details are on the 9:59:59.000,9:59:59.000 paper. Here in this presentation I just go[br]through some as I'll motivate. So we found 9:59:59.000,9:59:59.000 that on average, a firmware contains 4[br]border binaries. A BDG contains 5 binaries 9:59:59.000,9:59:59.000 and some BDG have more than 10 binaries.[br]Also, we plot some statistics and we found 9:59:59.000,9:59:59.000 that 80% of the firmware were analysed[br]within a day, as you can see from the top 9:59:59.000,9:59:59.000 left figure. However, experiments[br]presented a great variance which we found 9:59:59.000,9:59:59.000 was due to implementation details. For[br]instance we found that angr would take 9:59:59.000,9:59:59.000 more than seven hours to build some CFGs.[br]And sometimes they were due to a high 9:59:59.000,9:59:59.000 number of data keys. Also, we found that[br]the number of paths, as you can see from 9:59:59.000,9:59:59.000 this second picture from the top, the[br]number of paths do not have an impact on 9:59:59.000,9:59:59.000 the total time. And as you can see from[br]the bottom two pictures, performance not 9:59:59.000,9:59:59.000 heavily affected by firmware size.[br]Firmware size here we mean the number of 9:59:59.000,9:59:59.000 binaries in a firmware sample and the[br]total number of basic blocks. So let's see 9:59:59.000,9:59:59.000 how to run Karonte. The procedure is[br]pretty straightforward. So first you get a 9:59:59.000,9:59:59.000 firmware sample. You create a[br]configuration file containing information 9:59:59.000,9:59:59.000 of the firmware sample and then you run[br]it. So let's see how. So this is an 9:59:59.000,9:59:59.000 example of a configuration file. It[br]contains the information, but most of them 9:59:59.000,9:59:59.000 are optional. The only ones that are not[br]are this one: Firmware path, that is the 9:59:59.000,9:59:59.000 path to your firmware. And this too, the[br]architecture of the firmware and the base 9:59:59.000,9:59:59.000 address if the firmware is a blob, is a[br]firmware blob. All the other fields are 9:59:59.000,9:59:59.000 optional. And you can set them if you have[br]some information about the firmware. A 9:59:59.000,9:59:59.000 detailed explanation of all of these[br]fields are on our GitHub repo. Once you 9:59:59.000,9:59:59.000 set the configuration file, you can run[br]Karonte. Now we provide a Docker 9:59:59.000,9:59:59.000 container, you can find the link on our[br]GitHub repo. And I'm gonna run it, but 9:59:59.000,9:59:59.000 it's not gonna finish because it's gonna[br]take several hours. But all you have to do 9:59:59.000,9:59:59.000 is merely... *typing noises* just run it[br]on the configuration file and it's gonna 9:59:59.000,9:59:59.000 do each step that we saw. Eventually I'm[br]going to stop it because it's going to 9:59:59.000,9:59:59.000 take several hours anyway. Eventually it[br]will produce a result file that... I ran 9:59:59.000,9:59:59.000 this yesterday so you can see it here.[br]There is a lot going on here. I'm just 9:59:59.000,9:59:59.000 gonna go through some important like[br]information. So one thing that you can see 9:59:59.000,9:59:59.000 is that these are the border binaries that[br]Karonte found. Now, there might be some 9:59:59.000,9:59:59.000 false positives. I'm not sure how many[br]there are here. But as long as there are 9:59:59.000,9:59:59.000 no false negatives or the number is very[br]low, it's fine. It's good. In this case, 9:59:59.000,9:59:59.000 wait. Oh, I might have removed something.[br]All right, here, perfect. In this case, 9:59:59.000,9:59:59.000 this guy httpd is a true positive, which[br]is the web server that we were talking 9:59:59.000,9:59:59.000 before. Then we have the BDG. In this[br]case, we can see that Karonte found that 9:59:59.000,9:59:59.000 httpd communicates with two different[br]binaries, fileaccess.cgi and cgibin. Then 9:59:59.000,9:59:59.000 we have information about the CPFs. For[br]instance, here we can see that. Sorry. So 9:59:59.000,9:59:59.000 we can see here that httpd has 28 data[br]keys. And that the semantics CPF found 27 9:59:59.000,9:59:59.000 of them and then there might be one other[br]here or somewhere that I don't see . 9:59:59.000,9:59:59.000 Anyway. And then we have a list of alerts.[br]Now, thanks. Now, some of those may be 9:59:59.000,9:59:59.000 duplicates because of loops, so you can go[br]ahead and inspect all of them manually. 9:59:59.000,9:59:59.000 But I wrote a utility that you can use,[br]which is basically it's gonna filter out 9:59:59.000,9:59:59.000 all the loops for you. Now to remember how[br]I called it. This guy? Yeah. And you can 9:59:59.000,9:59:59.000 see that in total it generated, the system[br]generated 6... 7... 8 alerts. So let's see 9:59:59.000,9:59:59.000 one of them. Oh, and I recently realized[br]that the path that I'm reporting on the 9:59:59.000,9:59:59.000 log. It's not the path from the setter[br]binary to the getter binary, to the sink. 9:59:59.000,9:59:59.000 But it's only related to the getter binary[br]up to the sink. I'm gonna fix this in the 9:59:59.000,9:59:59.000 next days and report the whole paths.[br]Anyway. So here we can see that the key 9:59:59.000,9:59:59.000 content type contains user input and it's[br]passed in an unsafe way to the sink 9:59:59.000,9:59:59.000 address at this address. Now. And the[br]binary in question is called 9:59:59.000,9:59:59.000 fileaccess.cgi. So we can see what happens[br]there. *keyboard noises* If you see here, 9:59:59.000,9:59:59.000 we have a string copy that copies the[br]content of haystack to destination, 9:59:59.000,9:59:59.000 haystack comes basically from this getenv.[br]And if you see destination comes as 9:59:59.000,9:59:59.000 parameter from this function and return[br]and these and this by for it's as big as 9:59:59.000,9:59:59.000 0x68 bytes. And this turned out to be[br]actually a positive. OK. So in summary, we 9:59:59.000,9:59:59.000 presented a strategy to track data flow[br]across different binaries. We evaluated 9:59:59.000,9:59:59.000 our system on 952 firmware samples and[br]some takeaways. Analyzing firmware is not 9:59:59.000,9:59:59.000 easy and vulnerabilities persist. We found[br]out that firmware are made of 9:59:59.000,9:59:59.000 interconnected components and static[br]analysis can still be used to efficiently 9:59:59.000,9:59:59.000 find vulnerabilities at scale and finding[br]that communication is key for precision. 9:59:59.000,9:59:59.000 Here's a list of bibliography that I use[br]throughout the presentation and I'm gonna 9:59:59.000,9:59:59.000 take questions.[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 *applause*[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 Herald: So thank you, Nilo, for a very[br]interesting talk. If you have questions, 9:59:59.000,9:59:59.000 we have three microphones one, two and[br]three. If you have a question, please go 9:59:59.000,9:59:59.000 head to the microphone and we'll take your[br]question. Yes. Microphone number two. 9:59:59.000,9:59:59.000 Q: Do you rely on imports from libc or[br]something like that or do you have some 9:59:59.000,9:59:59.000 issues with like statically linked[br]binaries, stripped binaries or is it all 9:59:59.000,9:59:59.000 semantic analysis of a function?[br]Nilo: So. Okay. We use angr. So for 9:59:59.000,9:59:59.000 example, if you have an indirect call, we[br]use angr to figure out, what's the target? 9:59:59.000,9:59:59.000 And to answer your question like if you[br]use libc some CPFs do, for instance, then 9:59:59.000,9:59:59.000 environment CPF do any checks, if the[br]setenv or getenv functions are called. But 9:59:59.000,9:59:59.000 also we use the semantic CPF, which[br]basically in cases where information are 9:59:59.000,9:59:59.000 missing like there is no such thing as[br]libc or some vendors reimplemented their 9:59:59.000,9:59:59.000 own functions. We use the CPF to actually[br]try to understand the semantics of the 9:59:59.000,9:59:59.000 function and understand if it's, for[br]example, a custom setenv. 9:59:59.000,9:59:59.000 Q: Yeah, thanks.[br]Herald: Microphone number three. 9:59:59.000,9:59:59.000 Q: In embedded environments you often have[br]also that the getter might work on a DMA, 9:59:59.000,9:59:59.000 some kind of vendor driver on a DMA. Are[br]you considering this? And second part of 9:59:59.000,9:59:59.000 the question, how would you then[br]distinguish this from your generic IPC? 9:59:59.000,9:59:59.000 Because I can imagine that they look very[br]similar in the actual code. 9:59:59.000,9:59:59.000 Nilo: So if I understand correctly your[br]question, you mention a case of MMIO where 9:59:59.000,9:59:59.000 some data is retrieved directly from some[br]address in memory. So what we found is 9:59:59.000,9:59:59.000 that these addresses are usually hardcoded[br]somewhere. So the vendor knows that, for 9:59:59.000,9:59:59.000 example, from this address A to this[br]address B if some data is some data from 9:59:59.000,9:59:59.000 this peripheral. So when we find that some[br]hardcoded address, like we think that this 9:59:59.000,9:59:59.000 is like some read from some interesting[br]data. 9:59:59.000,9:59:59.000 Q: Okay. And this would be also[br]distinguishable from your sort of CPF, the 9:59:59.000,9:59:59.000 generic CPF would be distinguishable...[br]Nilo: Yeah. Yeah, yeah. 9:59:59.000,9:59:59.000 Q: ...from a DMA driver by using this[br]fixed address assuming. 9:59:59.000,9:59:59.000 Nilo: Yeah. That's what the semantic CPF[br]does, among the other things. 9:59:59.000,9:59:59.000 Q: Okay. Thank you.[br]Nilo: Sure. 9:59:59.000,9:59:59.000 Herald: Another question for microphone[br]number 3. 9:59:59.000,9:59:59.000 Q: What's the license for Karonte?[br]Nilo: Sorry? 9:59:59.000,9:59:59.000 Q: I checked the software license, I[br]checked the git repository and there is no 9:59:59.000,9:59:59.000 license like at all.[br]Nilo: That is a very good question. I 9:59:59.000,9:59:59.000 haven't thought about it yet. I will.[br]Herald: Any more questions from here or 9:59:59.000,9:59:59.000 from the Internet? Okay. Then a big round[br]of applause to Nilo again for your talk. 9:59:59.000,9:59:59.000 *postroll music*[br][filler, please remove in amara] 9:59:59.000,9:59:59.000 Subtitles created by many many volunteers and[br]the c3subtitles.de team. Join us, and help us!