WEBVTT
00:00:00.000 --> 00:00:09.804
preroll music
00:00:09.804 --> 00:00:24.745
Herald: Our next speaker for today is a
computer science PhD student at UC Santa
00:00:24.745 --> 00:00:30.805
Barbara. He is a member of the Shellfish
Hacking Team and he's also the organizer
00:00:30.805 --> 00:00:35.816
of the IECTF Hacking Competition. Please
give a big round of applause to Nilo
00:00:35.816 --> 00:00:36.228
Redini.
00:00:36.228 --> 00:00:39.510
applause
00:00:39.510 --> 00:00:46.671
Nilo: Thanks for the introduction, hello
to everyone. My name is Nilo, and today
00:00:46.671 --> 00:00:52.330
I'm going to present you my work Koronte:
identifying multi-binary vulnerabilities
00:00:52.330 --> 00:00:56.486
in embedded firmware at scale. This work
is a co-joint effort between me and
00:00:56.486 --> 00:01:02.101
several of my colleagues at University of
Santa Barbara and ASU. This talk is going
00:01:02.101 --> 00:01:08.247
to be about IoT devices. So before
starting, let's see an overview about IoT
00:01:08.247 --> 00:01:13.904
devices. IoT devices are everywhere. As
the research suggests, they will reach the
00:01:13.904 --> 00:01:19.762
20 billion units by the end of the next
year. And a recent study conducted this
00:01:19.762 --> 00:01:25.769
year in 2019 on 16 million households
showed that more than 70 percent of homes
00:01:25.769 --> 00:01:31.836
in North America already have an IoT
network connected device. IoT devices make
00:01:31.836 --> 00:01:37.660
everyday life smarter. You can literally
say "Alexa, I'm cold" and Alexa will
00:01:37.660 --> 00:01:43.573
interact with the thermostat and increase
the temperature of your room. Usually the
00:01:43.573 --> 00:01:49.610
way we interact with the IoT devices is
through our smartphone. We send a request
00:01:49.610 --> 00:01:55.164
to the local network, to some device,
router or door lock, or we might send the
00:01:55.164 --> 00:02:01.139
same request through a cloud endpoint,
which is usually managed by the vendor of
00:02:01.139 --> 00:02:07.290
the IoT device. Another way is through the
IoT hubs, smartphone will send the request
00:02:07.290 --> 00:02:13.663
to some IoT hub, which in turn will send
the request to some other IoT devices. As
00:02:13.663 --> 00:02:18.879
you can imagine, IoT devices use and
collect our data and some data is more
00:02:18.879 --> 00:02:23.376
sensitive than other. For instance, think
of all the data that is collected by my
00:02:23.376 --> 00:02:29.731
lightbulb or data that is collected by our
security camera. As such, IoT devices can
00:02:29.731 --> 00:02:37.081
compromise people's safety and privacy.
Things, for example, about the security
00:02:37.081 --> 00:02:44.330
implication of a faulty smartlock or the
brakes of your smart car. So the question
00:02:44.330 --> 00:02:53.126
that we asked is: Are IoT devices secure?
Well, like everything else, they are not.
00:02:53.126 --> 00:03:00.953
OK, in 2016 the Mirai botnet compromised
and leveraged millions of IoT devices to
00:03:00.953 --> 00:03:06.965
disrupt core Internet services such as
Twitter, GitHub and Netflix. And in 2018,
00:03:06.965 --> 00:03:13.294
154 vulnerabilities affecting IoT devices
were published, which represented an
00:03:13.294 --> 00:03:20.915
increment of 15% compared to 2017 and an
increase of 115% compared to 2016. So then
00:03:20.915 --> 00:03:27.710
we wonder: So why is it hard to secure IoT
devices? To answer this question we have
00:03:27.710 --> 00:03:33.635
to look up how IoT devices work and they
are made. Usually when you remove all the
00:03:33.635 --> 00:03:40.415
plastic and peripherals IoT devices look
like this. A board with some chips laying
00:03:40.415 --> 00:03:45.604
on it. Usually you can find the big chip,
the microcontroller which runs the
00:03:45.604 --> 00:03:50.535
firmware and one or more peripheral
controllers which interact with external
00:03:50.535 --> 00:03:57.188
peripherals such as the motor of, your
smart lock or cameras. Though the design
00:03:57.188 --> 00:04:03.445
is generic, implementations are very
diverse. For instance, firmware may run on
00:04:03.445 --> 00:04:08.775
several different architectures such as
ARM, MIPS, x86, PowerPC and so forth. And
00:04:08.775 --> 00:04:14.349
sometimes they are even proprietary, which
means that if a security analyst wants to
00:04:14.349 --> 00:04:20.041
understand what's going on in the
firmware, he'll have a hard time if he
00:04:20.041 --> 00:04:26.060
doesn't have the vendor specifics. Also,
they're operating in environments with
00:04:26.060 --> 00:04:30.563
limited resources, which means that they
run small and optimized code. For
00:04:30.563 --> 00:04:38.041
instance, vendors might implement their
own version of some known algorithm in an
00:04:38.041 --> 00:04:45.265
optimized way. Also, IoT devices manage
external peripherals that often use custom
00:04:45.265 --> 00:04:51.245
code. Again, with peripherals we mean like
cameras, sensors and so forth. The
00:04:51.245 --> 00:04:57.479
firmware of IoT devices can be either
Linux based or a blob firmware, Linux
00:04:57.479 --> 00:05:03.127
based are by far the most common. A study
showed that 86% of firmware are based on
00:05:03.127 --> 00:05:07.900
Linux and on the other hand, blobs
firmware are usually operating systems and
00:05:07.900 --> 00:05:15.010
user applications packaged in a single
binary. In any case, firmware samples are
00:05:15.010 --> 00:05:20.020
usually made of multiple components. For
instance, let's say that you have your
00:05:20.020 --> 00:05:26.410
smart phone and you send a request to your
IoT device. This request will be received
00:05:26.410 --> 00:05:33.190
by a binary which we term as body binary,
which in this example is an webserver. The
00:05:33.190 --> 00:05:37.990
request will be received, parsed, and then
it might be sent to another binary code,
00:05:37.990 --> 00:05:43.150
the handler binary, which will take the
request, work on it, produce an answer,
00:05:43.150 --> 00:05:48.130
send it back to the webserver, which in
turn would produce a response to send to
00:05:48.130 --> 00:05:54.100
the smartphone. So to come back to the
question why is it hard to secure IoT
00:05:54.100 --> 00:06:01.060
devices? Well, the answer is because IoT
devices are in practice very diverse. Of
00:06:01.060 --> 00:06:05.890
course, there have been various work that
have been proposed to analyze and secure
00:06:05.890 --> 00:06:11.500
firmware for IoT devices. Some of them
using static analysis. Others using
00:06:11.500 --> 00:06:15.910
dynamic analysis and several others using
a combination of both. Here I wrote
00:06:15.910 --> 00:06:19.690
several of them. Again at the end of the
presentation there is a bibliography with
00:06:19.690 --> 00:06:28.990
the title of these works. Of course, all
these approaches have some problems. For
00:06:28.990 --> 00:06:33.850
instance, the current dynamic analysis are
hard to apply to scale because of the
00:06:33.850 --> 00:06:39.430
customized environments that IoT devices
work on. Usually when you try to
00:06:39.430 --> 00:06:45.400
dynamically execute a firmware, it's gonna
check if the peripherals are connected and
00:06:45.400 --> 00:06:49.780
are working properly. In a case where you
can't have the peripherals, it's gonna be
00:06:49.780 --> 00:06:55.390
hard to actually run the firmware. Also
current static analysis approaches are
00:06:55.390 --> 00:07:00.580
based on what we call the single binary
approach, which means that binaries from a
00:07:00.580 --> 00:07:05.620
firmware are taken individually and
analysed. This approach might produce many
00:07:05.620 --> 00:07:11.530
false positives. For instance, so let's
say again that we have our two binaries.
00:07:11.530 --> 00:07:17.320
This is actually an example that we found
on one firmware, so the web server will
00:07:17.320 --> 00:07:22.990
take the user request, will parse the
request and produce some data, will set
00:07:22.990 --> 00:07:27.430
this data to an environment variable and
eventually will execute the handle binary.
00:07:27.430 --> 00:07:33.670
Now, if you see the parsing function
contains a string compare which checks if
00:07:33.670 --> 00:07:37.930
some keyword is present in the request.
And if so, it just returns the whole
00:07:37.930 --> 00:07:43.780
request. Otherwise, it will constrain the
size of the request to 128 bytes and
00:07:43.780 --> 00:07:51.790
return it. The handler binary in turn when
spawned will receive the data by doing a
00:07:51.790 --> 00:07:59.380
getenv on the query string, but also will
getenv on another environment variable
00:07:59.380 --> 00:08:04.060
which in this case is not user controlled
and they user cannot influence the content
00:08:04.060 --> 00:08:10.480
of this variable. Then it's gonna call
function process_request. This function
00:08:10.480 --> 00:08:16.690
eventually will do two string copies. One
from the user data, the other one from the
00:08:16.690 --> 00:08:22.930
log path on two different local variables
that are 128 bytes long. Now in the first
00:08:22.930 --> 00:08:28.360
case, as we have seen before, the data can
be greater than 128 bytes and this string
00:08:28.360 --> 00:08:33.460
copy may result in a bug. While in the
second case it will not. Because here we
00:08:33.460 --> 00:08:40.810
assume that the system handles its own
data in a good manner. So throughout this
00:08:40.810 --> 00:08:45.550
work, we're gonna call the first type of
binary, the setter binary, which means
00:08:45.550 --> 00:08:50.530
that it is the binary that takes the data
and set the data for another binary to be
00:08:50.530 --> 00:08:57.700
consumed. And the second type of binary we
called them the getter binary. So the
00:08:57.700 --> 00:09:01.570
current bug finding tools are inadequate
because other bugs are left undiscovered
00:09:01.570 --> 00:09:08.080
if the analysis only consider those
binaries that received network requests or
00:09:08.080 --> 00:09:12.750
they're likely to produce many false
positives if the analysis considers all of
00:09:12.750 --> 00:09:19.410
them individually. So then we wonder how
these different components actually
00:09:19.410 --> 00:09:23.430
communicate. They communicate through what
are called interprocess communication,
00:09:23.430 --> 00:09:28.890
which basically it's a finite set of
paradigms used by binaries to communicate
00:09:28.890 --> 00:09:36.660
such as files, environment variables, MMIO
and so forth. All these pieces are
00:09:36.660 --> 00:09:42.150
represented by data keys, which are file
names, or in the case of the example
00:09:42.150 --> 00:09:49.440
before here on the right, it's the query
string environment variable. Each binary
00:09:49.440 --> 00:09:53.280
that relies on some shared data must know
the endpoint where such data will be
00:09:53.280 --> 00:09:57.540
available, for instance, again, like a
file name or like even a socket endpoint
00:09:58.080 --> 00:10:02.910
or the environment variable. This means
that usually, data keys are coded in the
00:10:02.910 --> 00:10:10.770
program itself, as we saw before. To find
bugs in firmware, in a precise manner, we
00:10:10.770 --> 00:10:14.100
need to track how user data is introduced
and propagated across the different
00:10:14.100 --> 00:10:22.680
binaries. Okay, let's talk about our work.
Before you start talking about Karonte, we
00:10:22.680 --> 00:10:27.930
define our threat model. We hypotesized
that attacker sends arbitrary requests
00:10:27.930 --> 00:10:33.360
over the network, both LAN and WAN
directly to the IoT device. Though we said
00:10:33.360 --> 00:10:38.640
before that sometimes IoT device can
communicate through the clouds, research
00:10:38.640 --> 00:10:42.690
showed that some form of local
communication is usually available, for
00:10:42.690 --> 00:10:50.040
instance, during the setup phase of the
device. Karonte is defined as a static
00:10:50.040 --> 00:10:54.270
analysis tool that tracks data flow across
multiple binaries, to find
00:10:54.270 --> 00:11:00.690
vulnerabilities. Let's see how it works.
So the first step, Karonte find those
00:11:00.690 --> 00:11:04.590
binaries that introduce the user input
into the firmware. We call these border
00:11:04.590 --> 00:11:09.180
binaries, which are the binaries, that
basically interface the device to the
00:11:09.180 --> 00:11:15.570
outside world. Which in the example is our
web server. Then it tracks how a data is
00:11:15.570 --> 00:11:20.760
shared with other binaries within the
firmware sample. Which we'll understand in
00:11:20.760 --> 00:11:25.170
this example, the web server communicates
with the handle binary, and builds what we
00:11:25.170 --> 00:11:30.630
call the BDG. BDG which stands for binary
dependency graph. It's basically a graph
00:11:30.630 --> 00:11:39.720
representation of the data dependencies
among different binaries. Then we detect
00:11:39.720 --> 00:11:45.360
vulnerabilities that arise from the misuse
of the data using the BDG. This is an
00:11:45.360 --> 00:11:52.650
overview of our system. We start by taking
a packed firmware, we unpack it. We find
00:11:52.650 --> 00:11:58.740
the border binaries. Then we build the
binary dependency graph, which relies on a
00:11:58.740 --> 00:12:04.800
set of CPFs, as we will see soon. CPF
stands for Communication Paradigm Finder.
00:12:04.800 --> 00:12:10.320
Then we find the specifics of the
communication, for instance, like the
00:12:10.320 --> 00:12:16.140
constraints applied to the data that is
shared through our module multi-binary
00:12:16.140 --> 00:12:20.550
data-flow analysis. Eventually we run our
insecure interaction detection module,
00:12:20.550 --> 00:12:26.040
which basically takes all the information
and produces alerts. Our system is
00:12:26.040 --> 00:12:32.430
completely static and relies on our static
taint engine. So let's see each one of
00:12:32.430 --> 00:12:37.320
these steps, more in details. The
unpacking procedure is pretty easy, we use
00:12:37.320 --> 00:12:42.600
the off-the-shelf firmware unpacking tool
binwalk. And then we have to find the
00:12:42.600 --> 00:12:47.730
border binaries. Now we see that border
binaries basically are binaries that
00:12:47.730 --> 00:12:54.150
receive data from the network. And we
hypotesize that will contain parsers to
00:12:54.150 --> 00:12:57.930
validate the data that they received. So
in order to find them, we have to find
00:12:57.930 --> 00:13:04.170
parsers which accept data from network and
parse this data. To find parsers we rely
00:13:04.170 --> 00:13:12.900
on related work, which basically uses a
few metrics and define through a number
00:13:12.900 --> 00:13:18.000
the likelihood for a function to contain
parsing capabilities. These metrics that
00:13:18.000 --> 00:13:22.470
we used are number of basic blocks, number
of memory comparison operations and number
00:13:22.470 --> 00:13:29.070
of branches. Now while these define
parsers, we also have to find if a binary
00:13:29.070 --> 00:13:34.110
takes data from the network. As such, we
define two more metrics. The first one, we
00:13:34.110 --> 00:13:39.480
check if binary contains any network
related keywords as SOAP, http and so
00:13:39.480 --> 00:13:45.240
forth. And then we check if there exists a
data flow between read from socket and a
00:13:45.240 --> 00:13:51.660
memory comparison operation. Once for each
function, we got all these metrics, we
00:13:51.660 --> 00:13:56.070
compute what is called a parsing score,
which basically is just a sum of products.
00:13:56.070 --> 00:14:01.710
Once we got a parsing score for each
function in a binary, we represent the
00:14:01.710 --> 00:14:07.680
binary with its highest parsing score.
Once we got that for each binary in the
00:14:07.680 --> 00:14:14.370
firmware we cluster them using the DBSCAN
density based algorithm and consider the
00:14:14.370 --> 00:14:18.240
cluster with the highest parsing score as
containing the set of border binaries.
00:14:18.240 --> 00:14:25.620
After this, we build the binary dependency
graph. Again the binary dependency graph
00:14:25.620 --> 00:14:29.790
represents the data dependency among the
binaries in a firmware sample. For
00:14:29.790 --> 00:14:35.430
instance, this simple graph will tell us
that a binary A communicates with binary C
00:14:35.430 --> 00:14:40.770
using files and the same binary A
communicates with another binary B using
00:14:40.770 --> 00:14:47.310
environment variables. Let's see how this
works. So we start from the identified
00:14:47.310 --> 00:14:53.010
border binaries and then we taint the data
compared against network related keywords
00:14:53.010 --> 00:14:58.320
that we found and run a static analysis,
static taint analysis to detect whether
00:14:58.320 --> 00:15:04.680
the binary relies on any IPC paradigm to
share the data. If we find that it does,
00:15:04.680 --> 00:15:09.360
we establish if the binary is a setter or
a getter, which again means that if the
00:15:09.360 --> 00:15:13.320
binary is setting the data to be consumed
by another binary, or if the binary
00:15:13.320 --> 00:15:20.520
actually gets the data and consumes it.
Then we retrieve the employed data key
00:15:20.520 --> 00:15:25.860
which in the example before was the
keyword QUERY_STRING. And finally we scan
00:15:25.860 --> 00:15:30.450
the firmware sample to find other binaries
that may rely on the same data keys and
00:15:30.450 --> 00:15:35.820
schedule them for further analysis. To
understand whether a binary relies on any
00:15:35.820 --> 00:15:42.510
IPC, we use what we call CPFs, which again
means communication paradigm finder. We
00:15:42.510 --> 00:15:52.290
design a CPF for each IPC. And the CPFs
are also used to find the same data keys
00:15:52.290 --> 00:15:56.280
within the firmware sample. We also
provide Karonte with a generic CPF to
00:15:56.280 --> 00:16:00.390
cover those cases where the IPC is
unknown. Or those cases were the vendor
00:16:00.390 --> 00:16:06.090
implemented their own versions of some
IPC. So for example they don't use the
00:16:06.090 --> 00:16:13.350
setenv. But they implemented their own
setenv. The idea behind this generic CPF
00:16:13.350 --> 00:16:19.740
that we call the semantic CPF is that data
keys has to be used as index to set, or to
00:16:19.740 --> 00:16:27.870
get some data in this simple example. So
let's see how the BDG algorithm works. We
00:16:27.870 --> 00:16:31.890
start from the body binary, which again
will start from the server request and
00:16:31.890 --> 00:16:38.250
will pass the URI and we see that here. it
runs a string comparison against some
00:16:38.250 --> 00:16:44.850
network related keyword. As such, we taint
the variable P. And we see that the
00:16:44.850 --> 00:16:52.800
variable P is returned from the function
to these two different points. As such, we
00:16:52.800 --> 00:16:57.180
continue. And now we see that data gets
tainted and the variable data, it's passed
00:16:57.180 --> 00:17:02.310
to the function setenv. At this point, the
environment CPF will understand that
00:17:02.310 --> 00:17:08.460
tainted data is passed, is set to an
environment variable and will understand
00:17:08.460 --> 00:17:13.680
that this binary is indeed the setter
binary that uses the environment. Then we
00:17:13.680 --> 00:17:18.540
retrieve the data key QUERY_STRING and
we'll search within the firmware sample
00:17:18.540 --> 00:17:28.066
all the other binaries that rely on the
same data key. And it will find that this
00:17:28.066 --> 00:17:29.880
binary relies on the same data key and
will schedule this for further analysis.
00:17:29.880 --> 00:17:37.020
After this algorithm we build the BDG by
creating edges between setters and getters
00:17:37.020 --> 00:17:45.150
for each data key. The multi binary data
flow analysis uses the BDG to find and
00:17:45.150 --> 00:17:51.270
propagate the data constraints from a
setter to a getter. Now, through this we
00:17:51.270 --> 00:17:56.610
apply only the least three constraints,
which means that ideally between two
00:17:56.610 --> 00:18:02.760
program points, there might be an infinite
number of parts and ideally in theory an
00:18:02.760 --> 00:18:06.690
infinite amount of constraints that we can
propagate to the setter binary to the
00:18:06.690 --> 00:18:11.790
getter binary. But since our goal here is
to find bugs, we only propagate the least
00:18:11.790 --> 00:18:17.040
strict set of constraints. Let's see an
example. So again, we have our two
00:18:17.040 --> 00:18:24.060
binaries and we see that the variable that
is passed to the setenv function is data,
00:18:24.060 --> 00:18:29.490
which comes from two different parts from
the parse URI function. In the first case,
00:18:29.490 --> 00:18:35.040
the data that its passed is unconstrained
one in the second case, a line 8 is
00:18:35.040 --> 00:18:40.470
constrained to be at most 128 bytes. As
such, we only propagate the constraints of
00:18:40.470 --> 00:18:49.980
the first guy. In turn, the getter binary
will retrieve this variable from the
00:18:49.980 --> 00:18:55.830
environment and set the variable query.
Oh, sorry. Which in this case will be
00:18:55.830 --> 00:19:03.390
unconstrained. Insecure interaction
detection run a static taint analysis and
00:19:03.390 --> 00:19:07.650
check whether tainted data can reach a
sink in an unsafe way. We consider as
00:19:07.650 --> 00:19:12.660
sinks memcpy like functions which are
functions that implement semantically
00:19:12.660 --> 00:19:19.050
equivalent memcyp, strcpy and so forth. We
raise alert if we see that there is a
00:19:19.050 --> 00:19:23.100
dereference of a tainted variable and if
we see there are comparisons of tainted
00:19:23.100 --> 00:19:31.620
variables in loop conditions to detect
possible DoS vulnerabilities. Let's see an
00:19:31.620 --> 00:19:37.260
example again. So we got here. We know
that our query variable is tainted and
00:19:37.260 --> 00:19:43.770
it's unconstrained. And then we follow the
taint in the function process_request,
00:19:43.770 --> 00:19:52.740
which we see will eventually copy the data
from q to arg. Now we see that arg is 128
00:19:52.740 --> 00:20:01.050
bytes long while q is unconstrained and
therefore we generate an alert here. Our
00:20:01.050 --> 00:20:04.980
static taint engine is based on BootStomp
and is completely based on symbolic
00:20:04.980 --> 00:20:09.750
execution, which means that the taint is
propagated following the program data
00:20:09.750 --> 00:20:14.430
flow. Let's see an example. So assuming
that we have this code, the first
00:20:14.430 --> 00:20:19.620
instruction takes the result from some
seed function that might return for
00:20:19.620 --> 00:20:25.755
instance, some user input. And in a
symbolic world, what we do is we create a
00:20:25.755 --> 00:20:33.630
symbolic variable ty and assign to it a
tainted variable that we call TAINT_ty,
00:20:33.630 --> 00:20:40.290
which is the taint target. The next
destruction X takes the value ty plus 5
00:20:40.290 --> 00:20:46.890
and a symbolic word. We just follow the
data flow and x gets assigned TAINT_ty
00:20:46.890 --> 00:20:54.300
plus 5 which effectively taints also X. If
at some point X is overwritten with some
00:20:54.300 --> 00:21:00.900
constant data, the taint is automatically
removed. In its original design,
00:21:00.900 --> 00:21:07.860
BootStomp, the taint is removed also when
data is constrained. For instance, here we
00:21:07.860 --> 00:21:11.880
can see that the variable n is tainted but
then is constrained between two values 0
00:21:11.880 --> 00:21:19.770
and 255. And therefore, the taint is
removed. In our taint engine we have two
00:21:19.770 --> 00:21:26.610
additions. We added a path prioritization
strategy and we add taint dependencies.
00:21:26.610 --> 00:21:32.430
The path prioritization strategy valorizes
paths that propagate the taint and
00:21:33.030 --> 00:21:39.030
deprioritizes those that remove it. For
instance, say again that some user input
00:21:39.030 --> 00:21:46.110
comes from some function and the variable
user input gets tainted. Gets tainted and
00:21:46.110 --> 00:21:51.180
then is passed to another function called
parse. Here, if you see there are possibly
00:21:51.180 --> 00:21:57.930
an infinite number of symbolic parts in
this while. But only 1 will return tainted
00:21:57.930 --> 00:22:05.490
data. While the others won't. So the path
prioritization strategy valorizes this
00:22:05.490 --> 00:22:09.990
path instead of the others. This has been
implemented by finding basic blocks within
00:22:09.990 --> 00:22:16.140
a function that return a nonconstant data.
And if one is found, we follow its return
00:22:16.140 --> 00:22:21.870
before considering the others. Taint
dependencies allows smart untaint
00:22:21.870 --> 00:22:26.310
strategies. Let's see again the example.
So we know that user input here is
00:22:26.310 --> 00:22:33.900
tainted, is then parsed and then we see
that it's length is checked and stored in
00:22:33.900 --> 00:22:40.755
a variable n. Its size is checked and if
it's higher than 512 bytes, the function
00:22:40.755 --> 00:22:48.210
will return. Otherwise it copies the data.
Now in this case, it might happen that if
00:22:48.210 --> 00:22:53.535
this strlen function is not analyzed
because of some static analysis input
00:22:53.535 --> 00:23:00.780
decisions, the taint tag of cmd might be
different from the taint tag of n and in
00:23:00.780 --> 00:23:07.380
this case, though, and gets untainted, cmd
is not untainted and the strcpy can raise,
00:23:07.380 --> 00:23:15.540
sorry, carries a false positive. So to fix
this problem. Basically we create a
00:23:15.540 --> 00:23:21.360
dependency between the taint tag of n and
the taint tag of cmd. And when n gets
00:23:21.360 --> 00:23:28.410
untainted, cmd gets untainted as well. So
we don't have more false positives. This
00:23:28.410 --> 00:23:33.330
procedure is automatic and we find
functions that implement streamlined
00:23:33.330 --> 00:23:40.140
semantically equivalent code and create
taint tag dependencies. OK. Let's see our
00:23:40.140 --> 00:23:48.240
evaluation. We ran 3 different evaluations
on 2 different data sets. The first one
00:23:48.240 --> 00:23:55.140
composed by 53 latest firmware samples
from seven vendors and a second one 899
00:23:55.140 --> 00:24:02.340
firmware gathered from related work. In
the first case, we can see that the total
00:24:02.340 --> 00:24:09.720
number of binaries considered are 8.5k,
few more than that. And our system
00:24:09.720 --> 00:24:15.900
generated 87 alerts of which 51 were found
to be true positive and 34 of them were
00:24:15.900 --> 00:24:21.960
multibinary vulnerabilities, which means
that the vulnerability was found by
00:24:21.960 --> 00:24:27.990
tracking the data flow from the setter to
the getter binary. We also ran a
00:24:27.990 --> 00:24:32.010
comparative evaluation, which basically we
tried to measure the effort that an
00:24:32.010 --> 00:24:37.260
analyst would go through in analyzing
firmware using different strategies. In
00:24:37.260 --> 00:24:41.280
the first one, we consider each and every
binary in the firmware sample
00:24:41.280 --> 00:24:49.050
independently and run the analysis for up
to seven days for each firmware. The
00:24:49.050 --> 00:24:57.390
system generated almost 21000 alerts.
Considering only almost 2.5k binaries. In
00:24:57.390 --> 00:25:04.020
the second case we found the border
binaries, the parsers and we statically
00:25:04.020 --> 00:25:11.070
analyzed only them, and the system
generated 9.3k alerts. Notice that in this
00:25:11.070 --> 00:25:15.630
case, since we don't know how the user
input is introduced, like in this
00:25:15.630 --> 00:25:21.120
experiment, we consider every IPC that we
find in the binary as a possible source of
00:25:21.120 --> 00:25:28.470
user input. And this is true for all of
them. In the third case we ran the BDG but
00:25:28.470 --> 00:25:33.060
we consider each binaries independently.
Which means that we don't propagate
00:25:33.060 --> 00:25:37.800
constraints and we run a static single
corner analysis on each one of them. And
00:25:37.800 --> 00:25:45.750
the system generated almost 15000 alerts.
Finally, we run Karonte and the generated
00:25:45.750 --> 00:25:55.230
alerts were only 74. We also run a larger
scale analysis on 899 firmware samples.
00:25:55.230 --> 00:26:01.380
And we found that almost 40% of them were
multi binary, which means that the network
00:26:01.380 --> 00:26:08.220
functionalities were carried on by more
than one binary. And the system generated
00:26:08.220 --> 00:26:16.620
1000 alerts. Now, there is a lot going on
in this table, like details are on the
00:26:16.620 --> 00:26:21.660
paper. Here in this presentation I just go
through some as I'll motivate. So we found
00:26:21.660 --> 00:26:27.360
that on average, a firmware contains 4
border binaries. A BDG contains 5 binaries
00:26:27.360 --> 00:26:34.050
and some BDG have more than 10 binaries.
Also, we plot some statistics and we found
00:26:34.050 --> 00:26:39.030
that 80% of the firmware were analysed
within a day, as you can see from the top
00:26:39.030 --> 00:26:46.350
left figure. However, experiments
presented a great variance which we found
00:26:46.350 --> 00:26:51.300
was due to implementation details. For
instance we found that angr would take
00:26:51.300 --> 00:26:56.220
more than seven hours to build some CFGs.
And sometimes they were due to a high
00:26:56.220 --> 00:27:01.650
number of data keys. Also, we found that
the number of paths, as you can see from
00:27:01.650 --> 00:27:09.480
this second picture from the top, the
number of paths do not have an impact on
00:27:09.480 --> 00:27:15.030
the total time. And as you can see from
the bottom two pictures, performance not
00:27:15.870 --> 00:27:23.610
heavily affected by firmware size.
Firmware size here we mean the number of
00:27:23.610 --> 00:27:29.610
binaries in a firmware sample and the
total number of basic blocks. So let's see
00:27:29.610 --> 00:27:35.190
how to run Karonte. The procedure is
pretty straightforward. So first you get a
00:27:35.190 --> 00:27:38.790
firmware sample. You create a
configuration file containing information
00:27:38.790 --> 00:27:45.150
of the firmware sample and then you run
it. So let's see how. So this is an
00:27:45.150 --> 00:27:51.450
example of a configuration file. It
contains the information, but most of them
00:27:51.450 --> 00:27:55.290
are optional. The only ones that are not
are this one: Firmware path, that is the
00:27:55.290 --> 00:28:00.300
path to your firmware. And this too, the
architecture of the firmware and the base
00:28:00.300 --> 00:28:07.170
address if the firmware is a blob, is a
firmware blob. All the other fields are
00:28:07.170 --> 00:28:12.381
optional. And you can set them if you have
some information about the firmware. A
00:28:12.381 --> 00:28:18.330
detailed explanation of all of these
fields are on our GitHub repo. Once you
00:28:18.330 --> 00:28:23.981
set the configuration file, you can run
Karonte. Now we provide a Docker
00:28:23.981 --> 00:28:28.666
container, you can find the link on our
GitHub repo. And I'm gonna run it, but
00:28:28.666 --> 00:28:41.402
it's not gonna finish because it's gonna
take several hours. But all you have to do
00:28:41.402 --> 00:28:53.225
is merely... typing noises just run it
on the configuration file and it's gonna
00:28:53.225 --> 00:28:57.630
do each step that we saw. Eventually I'm
going to stop it because it's going to
00:28:57.630 --> 00:29:02.537
take several hours anyway. Eventually it
will produce a result file that... I ran
00:29:02.537 --> 00:29:07.857
this yesterday so you can see it here.
There is a lot going on here. I'm just
00:29:07.857 --> 00:29:14.780
gonna go through some important like
information. So one thing that you can see
00:29:14.780 --> 00:29:21.923
is that these are the border binaries that
Karonte found. Now, there might be some
00:29:21.923 --> 00:29:26.360
false positives. I'm not sure how many
there are here. But as long as there are
00:29:26.360 --> 00:29:32.131
no false negatives or the number is very
low, it's fine. It's good. In this case,
00:29:32.131 --> 00:29:38.879
wait. Oh, I might have removed something.
All right, here, perfect. In this case,
00:29:38.879 --> 00:29:45.444
this guy httpd is a true positive, which
is the web server that we were talking
00:29:45.444 --> 00:29:52.185
before. Then we have the BDG. In this
case, we can see that Karonte found that
00:29:52.185 --> 00:30:00.252
httpd communicates with two different
binaries, fileaccess.cgi and cgibin. Then
00:30:00.252 --> 00:30:10.799
we have information about the CPFs. For
instance, here we can see that. Sorry. So
00:30:10.799 --> 00:30:19.775
we can see here that httpd has 28 data
keys. And that the semantics CPF found 27
00:30:19.775 --> 00:30:26.823
of them and then there might be one other
here or somewhere that I don't see .
00:30:26.823 --> 00:30:35.835
Anyway. And then we have a list of alerts.
Now, thanks. Now, some of those may be
00:30:35.835 --> 00:30:44.135
duplicates because of loops, so you can go
ahead and inspect all of them manually.
00:30:44.135 --> 00:30:50.982
But I wrote a utility that you can use,
which is basically it's gonna filter out
00:30:50.982 --> 00:31:02.100
all the loops for you. Now to remember how
I called it. This guy? Yeah. And you can
00:31:02.100 --> 00:31:13.368
see that in total it generated, the system
generated 6... 7... 8 alerts. So let's see
00:31:13.368 --> 00:31:20.579
one of them. Oh, and I recently realized
that the path that I'm reporting on the
00:31:20.579 --> 00:31:25.970
log. It's not the path from the setter
binary to the getter binary, to the sink.
00:31:25.970 --> 00:31:31.426
But it's only related to the getter binary
up to the sink. I'm gonna fix this in the
00:31:31.426 --> 00:31:37.552
next days and report the whole paths.
Anyway. So here we can see that the key
00:31:37.552 --> 00:31:43.395
content type contains user input and it's
passed in an unsafe way to the sink
00:31:43.395 --> 00:31:49.688
address at this address. Now. And the
binary in question is called
00:31:49.688 --> 00:32:02.416
fileaccess.cgi. So we can see what happens
there. keyboard noises If you see here,
00:32:02.416 --> 00:32:12.480
we have a string copy that copies the
content of haystack to destination,
00:32:12.480 --> 00:32:20.751
haystack comes basically from this getenv.
And if you see destination comes as
00:32:20.751 --> 00:32:30.001
parameter from this function and return
and these and this by for it's as big as
00:32:30.001 --> 00:32:38.895
0x68 bytes. And this turned out to be
actually a positive. OK. So in summary, we
00:32:38.895 --> 00:32:46.529
presented a strategy to track data flow
across different binaries. We evaluated
00:32:46.529 --> 00:32:52.972
our system on 952 firmware samples and
some takeaways. Analyzing firmware is not
00:32:52.972 --> 00:32:58.156
easy and vulnerabilities persist. We found
out that firmware are made of
00:32:58.156 --> 00:33:02.660
interconnected components and static
analysis can still be used to efficiently
00:33:02.660 --> 00:33:07.730
find vulnerabilities at scale and finding
that communication is key for precision.
00:33:07.730 --> 00:33:12.229
Here's a list of bibliography that I use
throughout the presentation and I'm gonna
00:33:12.229 --> 00:33:12.956
take questions.
00:33:12.956 --> 00:33:18.431
applause
00:33:18.431 --> 00:33:27.366
Herald: So thank you, Nilo, for a very
interesting talk. If you have questions,
00:33:27.366 --> 00:33:32.470
we have three microphones one, two and
three. If you have a question, please go
00:33:32.470 --> 00:33:37.684
head to the microphone and we'll take your
question. Yes. Microphone number two.
00:33:37.684 --> 00:33:41.995
Q: Do you rely on imports from libc or
something like that or do you have some
00:33:41.995 --> 00:33:46.733
issues with like statically linked
binaries, stripped binaries or is it all
00:33:46.733 --> 00:33:51.895
semantic analysis of a function?
Nilo: So. Okay. We use angr. So for
00:33:51.895 --> 00:33:57.277
example, if you have an indirect call, we
use angr to figure out, what's the target?
00:33:57.277 --> 00:34:02.627
And to answer your question like if you
use libc some CPFs do, for instance, then
00:34:02.627 --> 00:34:08.313
environment CPF do any checks, if the
setenv or getenv functions are called. But
00:34:08.313 --> 00:34:12.873
also we use the semantic CPF, which
basically in cases where information are
00:34:12.873 --> 00:34:17.687
missing like there is no such thing as
libc or some vendors reimplemented their
00:34:17.687 --> 00:34:21.977
own functions. We use the CPF to actually
try to understand the semantics of the
00:34:21.977 --> 00:34:25.888
function and understand if it's, for
example, a custom setenv.
00:34:25.888 --> 00:34:29.900
Q: Yeah, thanks.
Herald: Microphone number three.
00:34:29.900 --> 00:34:36.905
Q: In embedded environments you often have
also that the getter might work on a DMA,
00:34:36.905 --> 00:34:43.233
some kind of vendor driver on a DMA. Are
you considering this? And second part of
00:34:43.233 --> 00:34:47.793
the question, how would you then
distinguish this from your generic IPC?
00:34:47.793 --> 00:34:52.502
Because I can imagine that they look very
similar in the actual code.
00:34:52.502 --> 00:34:58.752
Nilo: So if I understand correctly your
question, you mention a case of MMIO where
00:34:58.752 --> 00:35:03.956
some data is retrieved directly from some
address in memory. So what we found is
00:35:03.956 --> 00:35:08.434
that these addresses are usually hardcoded
somewhere. So the vendor knows that, for
00:35:08.434 --> 00:35:13.280
example, from this address A to this
address B if some data is some data from
00:35:13.280 --> 00:35:18.857
this peripheral. So when we find that some
hardcoded address, like we think that this
00:35:18.857 --> 00:35:21.688
is like some read from some interesting
data.
00:35:21.688 --> 00:35:28.073
Q: Okay. And this would be also
distinguishable from your sort of CPF, the
00:35:28.073 --> 00:35:32.180
generic CPF would be distinguishable...
Nilo: Yeah. Yeah, yeah.
00:35:32.180 --> 00:35:35.775
Q: ...from a DMA driver by using this
fixed address assuming.
00:35:35.775 --> 00:35:39.827
Nilo: Yeah. That's what the semantic CPF
does, among the other things.
00:35:39.827 --> 00:35:41.336
Q: Okay. Thank you.
Nilo: Sure.
00:35:41.336 --> 00:35:43.856
Herald: Another question for microphone
number 3.
00:35:43.856 --> 00:35:46.117
Q: What's the license for Karonte?
Nilo: Sorry?
00:35:46.117 --> 00:35:51.130
Q: I checked the software license, I
checked the git repository and there is no
00:35:51.130 --> 00:35:53.440
license like at all.
Nilo: That is a very good question. I
00:35:53.440 --> 00:36:00.610
haven't thought about it yet. I will.
Herald: Any more questions from here or
00:36:00.610 --> 00:36:04.410
from the Internet? Okay. Then a big round
of applause to Nilo again for your talk.
00:36:04.410 --> 00:36:24.820
postroll music
00:36:24.820 --> 00:36:31.630
Subtitles created by many many volunteers and
the c3subtitles.de team. Join us, and help us!
99:59:59.999 --> 99:59:59.999