our speaker is Mattias payer Mattias is
a brilliant professor in computer
science to go along with all the rest of
the brilliant faculty we have so we will
get him started in just a second and
appreciate your attention and we'll
continue on citize I I know I'm standing
between you and lunch but I hope I can
still make it interesting and worthwhile
for you to listen to me this will be a
more technical talk as cherry introduced
me I'm Matthias Pryor I'm faculty here
at Purdue and a TS Department and
affiliated with Sirius and my group we
are protecting applications in the
presence of vulnerabilities so this is
an interesting and slightly different
approach from what we heard before so in
comparison what antivirus systems or
tripwire do they detect the modification
of files or they detect if there's some
integrity violation what we are focusing
on is on the other hand we detect an
active exploit before the attacker can
actually do any harm if you think about
it if the attacker is modifying files or
modifying data on your system
the attacker already has a set of
capabilities you might need these
additional checks and they are
complementing the work that we do but we
protect an active exploitation before
the attacker gets any computational
guarantees or can do any harm on your
system the a lot of the work is done by
the students in my group and I would
redirect all the praise to them so
there's 12 awesome students working in
different forms of both compiler based
and binary-only based mechanisms where
we try to complement these if you think
about it if you develop defense
mechanisms if you work on a compiler you
get more information you get source code
you get all these other capabilities
that you can work on but there's always
this one single binary or library that
you are using there is a custom
off-the-shelf library so for an
effective and efficient solution you
have to support both
and actually support the binary-only
solutions that's all for this talk here
I wanted to use the time to tell you a
little bit about control flow hi
checking at different forms of attacks
and we've looked at defense mechanisms
that we have deployed on our systems and
you likely know address space layout
randomization or aslr data execution
prevention or DEP and stack canneries
which are common defense mechanisms that
have been deployed in our systems over
the last 10 or 15 years and we've made
likely some progress but can we quantify
the process how much harder is it now
for an attacker to gain some form of
code execution capabilities in our
system and it see if you look around and
if you read the news there's pretty much
bugs everywhere in our system and
they're being exploited again and again
and again and it's really hard to
quantify to know or to see if you're
actually making any progress at all and
we try to look into this and on one hand
quantify the progress that we are making
and on the other hand try to figure out
what kind of next defense mechanisms
we'll use in our systems on a broad
scale well in my research group we are
developing the next generation of
defenses we'll focus more on defenses
here that are on the verge of being
deployed but I will still give you a
peek into some of these future defenses
as we go on the pictures I'm showing
here are from a recent from the recent
point to own competition that showed
that Microsoft edge has been poned 15
times Microsoft Windows has been
exploited a good 20 times and so on and
they're bugs pretty much everywhere so
in the example here we see that there's
a calculator that has been popped on a
MacBook Air and the hackers received
additional fame for controlling the LED
keyboard as well and scrolling a message
you have been hacked by the group of
people that is that there's Ubuntu
system that has been exploited as well
popping up a calculator showing that an
exploit has successfully triggered and
the attacker got code execution
there's Microsoft Windows
running Windows 10 with a with an open
shell where the attackers would
compromise it and also are a more
interesting bug you see here VMware
image or VMware instance that runs
browser and the attackers first
compromised the browser in a second step
compromise the Windows operating system
and then the third step compromised the
VMware instance to pop to gain control
over all the running running servers so
is it hopeless is everything we have
deployed not effective or are we
actually making progress and let me go
into detail for the question that I just
asked there's strength in memory safety
errors especially all these exploits are
being used rely on some form of memory
corruption at one point in time memory
corruption is if you're thinking C or
C++ some type safety violation or some
memory safety violation if you read out
of bounds if you have a function pointer
that is not being used in the right way
or if you have any other corruption that
is happening and here this is worked by
victor von neumann at friar university
in amsterdam he plotted the cold of
vulnerabilities and exploits that were
reported over the last 16 years and we
can see trends how they change and how
they develop over time so we see there's
different groups for maturing
vulnerabilities pointer vulnerabilities
with some form of pointer modifications
integral vulnerabilities heap
vulnerabilities and snack
vulnerabilities and we see that around
2006 2008 there was a huge peak in stack
based vulnerabilities I mean saw a large
amount of exploits and conveniently
right around that time we started
deploying data execution prevention and
stack Canaries on a white scale and you
see that all these stack based attacks
have been taking off since sensitive
the attacks are actually affected our
defenses are effective against the
attacks and if you look at what kind of
exploits are being used nowadays it's
mostly heat based exploits use after
free different forms of
is up to three constellations where
different memory objects are used again
after they have been freed at a later
point in time so can we say that
software is unsafe and insecure at any
time in ghent what we are using on our
systems we have pretty much everything
implemented in c and c++ and tnt plus
starts our programming languages that
create type safety and memory safety for
let's just say some potential of
performance that you can gain you likely
don't want to write your highly critical
program in a high-level language like
Java or something else and to get the
most performance out of it
unfortunately the systems that we have
are too complex and too big to formally
verify and you've likely been at the
panel yesterday about formal methods and
formal verification by it but I strongly
believe that we should do as much formal
verification as we can there's just too
much code out there to protect
everything we have that's why we need to
have some form of defenses to protect
the integrity and availability of our
systems and data even in the presence of
vulnerabilities because if you think
about it
for one piece of software Google Chrome
has 76 million lines of code
this is a humongous beast this is all
implemented in C++ and while Google has
very strong coding standards and
enforces code reviews testing fuzzing
and so on there's still a large amount
of vulnerabilities in these product but
it such as Google Chrome that you are
running with T 76 million lines of code
but in addition to that you are using
the let's say the standard library -
let's see and the Linux kernel which
adds another bunch of code so even if
you're running your safe system let's
say you say I'll program everything in a
safe way we'll do Python or Java but
still the runtime system that you are
using for Python and Java is implemented
in c and c++ and it relies on the
standard libraries on your system
and on your operating system kernel and
on the hypervisor in addition to that so
you're building the trust of your system
on all these other systems that are
implemented with a low-level language
and as we know the next vulnerability is
shipped out ahead to bite us and we are
now in a in a time where vulnerability
scale and can be used to compromise a
large amount of systems so you might ask
yourself how do attackers get onto our
systems and most of the exploits rely on
some form of control flow hijack attack
so the attacker gained some form of code
execution capabilities where the
attacker tries to execute arbitrary code
on top of your system and many of you
will likely know the code injection
scenario where the attacker inject new
code into the process this is a simple
scenario that uses a memory corruption
to set up the initial attack and the
attacker overrides either some part of
the heap or some part of the stack and
in the second step redirect the control
flow from the executing program hood is
newly inject the data that is then
interpreted as data all attacks until
around 2005 followed this principle but
we have introduced a large amount of
defense mechanisms we as a security
community not not V azzam in my group
but these defenses protect against these
attack vector so this is no longer a
thing attacks has become much more
complicated and attackers have resorted
to a form of code reuse so instead of
injecting new code because this is no
longer possible giving the defenses that
you have attackers have to reuse
existing pieces of the code think of it
as a jigsaw puzzle you have all the
existing code of your application and
you're grabbing little pieces of your
application and then stitching them
together in a new way that then executes
the attackers desired behavior and a
code reuse attack
works with an additional step it's kind
of similar to the code injection attack
that I described earlier
an attacker first has to find the
addresses of sketches think again of the
jigsaw analogy they're trying to scan
the code to find different forms of
pieces of codes that can be stitched
together to then execute the attackers
desired behavior the second step the
attack of forces the memory corruption
similar to the first attack and set up
the individual data in a third step
redirect the control flow to get your
chain there's currently no active
defense that we have that protects
against this form of attack on the other
hand there's a mechanism that is on the
verge of being deployed and it's called
control flow integrity that then protect
against these forms of attacks a quick
question who of you has already heard of
control flow integrity a show of hand
actually a whole bunch of people did you
use a product control flow integrity
product in your systems to use the
microsoft visual studio control flow
guard anybody using that anybody using
the LLVM CFI one person two persons that
are both my students that's interesting
anybody else anybody using cf fine
practice so far not yet
but this is this is the defense
mechanism that is on the verge of being
deployed so you all will you see if I
I'm going to tell you in the future
right now security products or compilers
have TFI mechanisms that are available
you simply need to switch a compiler
flag on and then use this defense
mechanism in practice so it's easy to
deploy even on existing code bases you
simply switch flag and then you are
protected for the others that didn't
raise the hand you might ask what is
control flow integrity so control flow
integrity tries to precisely limit the
control flow of your application
to the set of control flows that are
well known and can be statically
determined so you restrict the programs
dynamic control flow so it can be
executed at runtime to whatever is
intended by the programmers you're
trying to get closer to the programmer
intention with this mechanism it
requires a static analysis that figures
out all the valid targets for each in
their controls work transfer limit the
amount of targets that can be used at
one time to the set of static term
targets and then a dynamic enforcement
mechanism that executes this actual
check and we distinguish between two
kind of edges the forward edge and the
backward edge a forward edge is for
example of function calls and these are
indirect function calls for example a
function pointer in C or a virtual call
in C++ they're focusing on systems
languages like C and C++ here because
they are on one hand the ones that are
most used for all the software and on
the other hand the ones that are most
exploitable for these kind of
vulnerabilities and the backward edge is
any function return like the return
instruction pointer on the stack and
we've looked at all the research that
has been published over the last couple
of years so this is a highly active
research area over the last 15 years and
it's slowly the research is slowly
winding down as we are progressing into
active products that are used on a wider
scale and there have been 60 plus
research papers at top conferences that
publish different aspect of CFI
mechanisms and it's an immensely complex
topic so we figured we look at all these
mechanisms and try to figure out what
their actual differences are are your
opportunities for deployment that we can
make it easier are there opportunities
for optimization are there opportunities
for higher precision to get more
guarantees and I'll try to tell you a
little bit about these aspects that we
looked at but let me first start with
what control flow integrity actually
does so for each indirect control flow
transfer we figure out the set of
allowed targets so if you have a call
through a function pointer or
virtual call we through the static
analysis remembered Jesus these are the
only targets that are allowed to
transfer control to all the other
targets we'll raise an error now how do
we do this if you think about the free
program or a C++ program at one point in
time it will be compiled down into a
machine code program and the computer
CPU doesn't care where you're
transferring control to the C the CPU
does not have any semantics on what
targets are being allowed or not allowed
so if you have an indirect function call
or a function return if you're returning
from a function you can reach or the CPU
is happy to transfer control flow to any
addressable byte in memory right so this
is this is this gives the attacker a
large amount of different targets so
what does see if I do it restricts those
targets instead of just following this
transfer we verify the integrity of the
function pointer before we use it to
transfer control so we add during the
compilation or during the binary
rewriting we add an additional check
that says check the current value of
this function pointer to see if it is in
the set of valid target if it is not in
the set of valid target we will stop the
execution of the program and we'll do
the same for the return instruction and
limited to a benign caller so you are
only allowed to call the return to v9
caller of the of this function so the
attacker may still corrupt memory but
whenever we do a control flow transfer
we will verify the code pointers before
they are actually used
we protect against any illegal
modifications often the CFI does provide
some incremental security the advantages
it can be implemented at very low
overhead but it gives us integrity of
the control flow if
look at the different TFI mechanisms the
strengths of the CFI the precision
depends on the power of the analysis
how small are gives targeted if you
think about it you can implement a CFI
mechanism that is highly imprecise that
we will just allow any single function
that is out there and this is precisely
what coarse-grained TFI mechanisms do
they say we will allow you to transfer
control to any function this is still
better than transferring control to any
arbitrary executable fight in your
memory instead of having a target set of
every single executable by t as a
targeted of every function this reduces
the attack surface or you can have a
fine-grained mechanism that further
reduces the attack surface to a smaller
set and we'll get into detail for that
so we evaluated all these different TFI
mechanisms using a qualitative analysis
and we distinguish them according to
what kind of transfers they allow and I
have a simple example and I hope it's
readable in the back that where we are
using they're using C++ here we allocate
a new object and of type a and we then
do a virtual dispatch to function foo
and function food takes two parameters
integer B and integer C now the question
we ask ourselves for CFI mechanism what
kind of targets are reachable without C
fi any addressable byte is reachable so
any CFI mechanism will protect us
against reaching an arbitrary byte there
are other CFI mechanisms that check for
the guarantee so this function here has
a different number of argument so
there's a class of TFI mechanisms that
checks the argument such a class of CFI
mechanism to checks the return value
avoid is different from an int and we
expect that it returns an int there's if
I mechanism to check this is a C
function while we are doing a C++ style
dispatch so we can remove this function
there are other mechanisms that say hey
this is a C++ function but B
is not related to Class A therefore
there's no valid way how we could
dispatch over here and there's other
precision
details that can further detect
fine-grain class higher key differences
and hopefully you will only have one
single class that remains so we looked
through these 5060 different research
papers and try to distill it into some
form of spider plots that give you some
intuitive answer about the security
guarantees of the different TFI
mechanisms and intuitively the larger
the size of the spider plot the higher
the security gains that you get and this
is some qualitative analysis that gives
you an intuition of the different
mechanisms and in our paper we have one
and a half pages of all these different
mechanisms and compare them against each
other so we compare for the different
control flows they support the precision
of the forward edge the precision of the
backward edge the reported performance
and the quantitative security guarantees
that they give and what we learned is we
as a security community and this goes
back to the panel from yesterday we are
doing a terrible job at actually
quantifying and telling people what kind
of static analysis we are using most of
these papers focused heavily on
performance and in the end all of these
papers have similar comparable
performance and what we would like to
know is what kind of security guarantees
does it give you only if we know both
the security guarantees and the
performance overhead we can assess if it
is actually worthwhile to deploy these
defenses so we have to know both factors
performance overhead and security
guarantees because maybe in a higher
security environment we are more
sensitive in my own you are willing to
pay more of the overhead cost compared
to a low security environment you have
to distinguish between those and there's
also a set of existing quantitative
mechanism like the average indirect
target reduction which did not really
work out
because the they pretty much counted how
much they reduced the attack surface
from reaching any arbitrary location and
it is the average sum over all the
indirect control flow transfer locations
divided by the number of bytes that are
addressable and if you think about it
this metric if you only look at the
let's see the standard library and you
allow any exported function to be
reached you already get an air value
this is the old metric of 99.9 percent
and we have shown that such a TFI
mechanism can still be exploited and
there have been a set of blackhat Hawks
and other kind of talks that showed it
with was this was not powerful enough so
what we did is we actually try to
compare mechanisms on a more realistic
scale so we compared actual mechanisms
against each other on the same hardware
on the same machine with the same
benchmarks so you can actually compare
these different mechanisms something
that other the papers the research
papers has not yet done yet so we
introduce new quantitative metrics the
number of the equivalence classes and
the size of the largest class now if you
look at this and this is likely a little
bit overwhelming so let me walk you
through this through this figure we
measured the classes the size of the
classes and the differences in the
classes what we are showing here are
spider plots for the different
mechanisms that we have evaluated on a
logarithmic scale and the red bar shows
the average amount of allowed transfers
on a per control flow transfer location
the bar shows the 25 and 75 percentile
of the of the target set and the
whiskers shows the individual outliers
so what we see here is a visual
representation of how tight the static
analysis is
allowing us to compare the static
analysis against each other in addition
to that we have a dynamic bar that shows
what is actually used at run time so the
closer we are to this dynamic bar the
higher the security game also what we
see there's several mechanisms that have
a couple of locations where they are
allowing more than a thousand target so
an attacker if you put on your attacker
hat an attacker would try to figure out
which locations are imprecise where is
your dynamic analysis sailing and then
pick these locations to attack the
application so this still gives the
attacker some opportunity but we see
that sometimes even one single location
dynamically uses a large amount of
different target so there's a trade-off
of how precise we can actually get for
this mechanism we also looked at the
number of equivalent classes so how many
individual classes should you have
remember an equivalence classes ideally
you have one equivalent class for each
indirect control flow transfer curl and
class meaning for each indirect control
flow transfer that you have for each
virtual call you remember this virtual
call has this exact set of targets all
the mechanisms that we looked at merged
these set so they have been reusing the
set either for performance constraints
or for other kind of optimizations so
they don't have individual sets and
often have a lower number of equivalent
classes which leads to some ambiguity
and additional attack surface for the
attacker so I talked a lot about the
forward edge for indirect calls and
virtual dispatch but what what about the
return instruction return instructions
push a valid return instruction target
on the stack and it can be overwritten
through a simple buffer overflow and
what we figured out is that defenses
without any form of stack integrity are
inherently broken so we need to protect
the stack as well and we have actually
developed the defense making
that has now made it into LLVM as well
which is not yet actively being used but
again this is only a compiler switch
that you can activate to get stronger
defenses in the future if you think
about the attack let's say you have two
calls to bar to the same function now as
an attacker in the second call to bar
you can override the return instruction
pointer and instead of returning to this
location here you're returning to this
location and the attacker has already
implemented a loop if there's some
computation inside here that the
attacker can modify the attacker gains
some computational capability so we have
to use some form of stack integrity some
mechanism that enforces deck integrity
so the attacker is restricted to
arbitrary targets on the stack and
cannot arbitrarily swap and loop through
different targets but each target can
only be called once and in sequence so
this concludes my part about TFI CFI is
the mechanism that you all will use in
the future in the product you develop
and it's as simple as switching on a
compiler flag but you would also like to
take at least one or two minutes to talk
about an other upcoming problem
especially as we are moving towards
larger and larger applications pipe
safety is becoming a very pressing
concern and we need to figure out a
mechanism to protect our systems in the
presence of type safety vulnerabilities
type violations and most of you will be
programming in C++ in industry which is
inherent inherently prone to this kind
of type safety vulnerabilities and we
are working actively working on
solutions for that there all will all
hopefully end up in some form of
compiler at one point in time the type
confusion vulnerability and this is
exactly what is being used to compromise
chrome edge Firefox all these browsers
out there on a day to day basis type
Confucius confusion arises through
illegal down costs so for example
converting a base class
pointer to a derived cloth and has a
short example here that illustrates this
problem we have two classes Class B and
Class V whereas V is the derived class
of P so G specializes Class B Class B
only has an integer food while Class B
has an additional integer and a virtual
call for example - and the problem that
we are seeing is that initially a class
of type B has been allocated which only
allocates the small memory area here but
then at a later point a static cost to
the object happens and the D pointer
then ends up being here and whatever is
in memory and these other locations is
being used instead and this is what
attackers use to compromise all our
current software in these different
different mechanisms so we are working
on protecting against such
vulnerabilities and obviously low
overhead otherwise these mechanisms will
not be deployed in practice so what we
do is we replace through a compiler
based analysis each instance of a static
cost which is the source of the problem
here with a runtime check so static cost
for a come for a programmer super simple
right it just tells the compiler hey I
want you to call this object from this
cloth into the object of that clock
there is no check whatsoever and you can
cause from any object or any other
object and everything is fine so we
replace this compile time check with a
runtime check it forces us to track the
type of each individual object but we
can through this mechanism enforce type
safety for c++ and ensure that we are
protected against these forms of
vulnerabilities at low overhead and we
have one publication that I mentioned
here but there's other work ongoing
where we further reduce the overhead and
increase the precision so let me
conclude let's get back to the question
that I asked visually are we making
progress and to sin
I started to talk by giving a set of
examples of the pone to own competition
from this year and I'm ending the talk
with two pictures that compare ponchione
in 2007 and county-owned in 2017 ten
years later have we made progress ten
years ago it was a one-man show one
single person compromised the system
nowadays we have teams of 20 to 25
people that compromised the system so
yes we are making progress exploitation
has gotten much harder and requires a
large team of highly skilled people that
actually figure out all these
dependencies and generate these complete
exploit so yes we are making progress
attacks are much harder and require
large teams not just single-player and
TFI the mechanism that I talked to you
make attacks even harder and much harder
nowadays some attack surface remains so
this is not the end but it is something
that further at an order of magnitude of
different of difficulty for the attacker
and we can use stack integrity data
execution prevention ASLR and so on as
complementary mechanisms ongoing work as
a community we have to work on precision
for CFI to increase the effectiveness we
have to work on type safety and ideally
at one point in time in memory safety
with that I would like to end my talk
and ask if there any questions thank
[Applause]
how applicable is GFI to interpreted
languages like PHP this is a wonderful
question thank you and I try to tone it
down
so CFI gives you additional protection
makes attacks harder and your question
is excellent how effective is it in a
presence of an interpreter like an
interpreter allows you allows the
attackers through data only attacks to
stitch together a new program that is
then being interpreted now with the
difference without GFI the attacker
would simply inject code and execute
arbitrary system called
an arbitrary command with GFI the
attacker is restricted to modifying the
actual code that is then interpreted by
the interpreter let's make these let's
make the Java example that supports Java
security modules so you are restricted
to the Java security modules that you
have restricting the code that you can
actually execute so it still gives you
some additional guarantees the attacker
is then restricted to the interpreted
language and not to the machine code
language also the attack gets much
harder because the attacker has to work
on the machine code on the interpreted
language basis so then try to escalate
privileges accordingly and as I said CFI
is not a panic panic here you have to
you likely use additional system called
filtering system called policies other
integrity mechanisms as well put it on
to your question or less okay I'm
wondering how does the Intel control
flow enhancement technology fit into
kind of the big picture of what you're
talking about
wonderful question thank you yeah the
for Intel CT for those that don't know
it is a hardware implementation of CFI
let me split this question into two
components the forward edge and the
backward edge interestingly for the
backward edge Intel CT implements a
precise shadow stack this is
wonderful this is exactly what we need
to get stack integrity so I guess in the
future we will all use intel CIT on
Intel machines for the backward edge
because it gives a strong stack
integrity at negligible overhead this is
perfect for the forward edge the
precision is rather loose so Intel CT
allows you to tag functions that are a
valid target with a specific let's say
some form of tag right and then any
indirect control transfer is allowed to
reach any of these tag functions which
is highly imprecise this is again this
single target set mechanism which is
pretty much the same as Microsoft uses
in their control flow guard so this is
likely to weak in practice to be
effective so it makes the attack
somewhat harder but doesn't stop an
attacker and I've near we're close to
where see if I can go on a precision
basis so people will likely use it as
advantage of Intel CPUs that is very
fast so people may use it for the
forward edge in low security context and
in the backward edge for pretty much
every single context for the forward
edge people will likely switch to more
precise solutions that give you stronger
guarantees which are hard to accomplish
on a hardware level so Intel did as much
as they could without significantly
complicating the underlying hardware
design good question thank you all right
thank you
you