*35c3 Preroll music*
Herald: And now Ned Williamson's talk: "Attacking Chrome Inter Process
Communication: reliably finding bugs to escape the Chrome sandbox". He will be
talking about finding bugs in the Chrome inter-process communication in order to
escape from the sandbox using a fuzzing method to enumerate the attack surface of
the Chrome inter-process communication. Ned is a vulnerability researcher. He
likes C and C++ vulnerabilities. He did research for consoles and browsers and now
started to work on mobile devices. Please welcome him with a huge round of applause.
*Applause*
Ned: All right. Hello everyone. My name is Ned and today I'll be talking about Chrome
IPC. And actually as I was writing this talk, I kind of came up with this idea to
make it more usable for everyone and the way I ended up doing this was by trying to
start really general and then kind of going more and more specific all the way
down to the Chrome IPC fuzzing. So if you're really technical the end will be
still interesting and then if you're new to this stuff hopefully the beginning part
will show some of how to get started. So just a quick overview about me. I've
mostly been spending the last several years on low level vulnerability research
and my particular interest is on any kind of critical bugs. Meaning kind of the more
severe individual bug the more interesting to me. So I'm trying to kind of solve this
problem of: How do we make the bug finding process effective enough to bring out
these really rare hidden bugs? And you will see an example of how to do that by the
end. But just an overview. I've basically worked on four things the first being CTFs
then went to 3DS and Chrome. Now I'm starting on XNU, but just a month ago
so, not too much yet. Before we get into it I just want to give a little recap of what
happened since last time. So I was part of the Nintendo hacking talk two years ago
here and I presented two exploits called Soundhax and Fasthax and not to go into it
too much but I did want to share like what happened here because I was actually
surprised. I put Google analytics on the Soundhax website and I thought like maybe
a thousand people use it or something but I just looked at the stats like a couple
weeks ago and then turned out like 100k people used it or something. And then I
searched YouTube and found like these huge videos where they were copied so like I
wanted to have a screenshot but it's copyrighted so didn't do that, but
basically it looks like something on the order of about a million users which is
crazy because this is one of my intro projects really so I think like this
should show you that you don't have to be all the way up onto Chrome or whatever it
is to get into this and do some huge fun project. And then I just wanted to
publicly talk about the donations because I had a donation link on the Soundhax
website and we fortunately received about a thousand dollars in donations and then
half of that went to the emulator people because they, uh, that's how I eventually
wrote my exploit for Soundhax so it made sense to repay that, and then the other
half went to buying switches for like the toolchain developers who couldn't afford
it and so just wanted to thank everyone who used this or whoever donated. Just a
shout out. So we will get into the actual meat of the talk. So basically I want to
focus on the bug finding process, not exploitation necessarily because this
topic is pretty well explored I think and I think the bug hunting aspect is kind of
what's the most prohibitive for people to join in. And when I look at the number of
people who I play CTF with who are really good at exploitation and then a number of
like these prolific bug hunters it just seems like from what I see from how smart
people are there should be more people doing the bug hunting and I hope that if I
can talk about it more people can come over. So with that, the agenda will be
just overall how do you make a process to achieve any goal. Then next how do you
apply this kind of, some kind of strategy to bug hunting, then this new fuzzing
style I've been kind of developing, some other people out in the industry have
been working on. And then finally how does this all tie back to Chrome IPC. So also
just to mention - I should mention that the bug I'll be showing in this
presentation was used in a full chain exploit that I developed with a couple
other people and the details of the exploitation of that will be discussed at
OffensiveCon, so that's also here in Germany and hopefully people will check it
out. So how do you become an expert in anything and I kind of was thinking this
before I even started anything and I was like in the CTF stage and I was just kind
of curious like if I approach this with the mindset of there's this arbitrary
skill I want to learn. And if I approach it strategically like what's going to
happen. So I looked into this expert research and then there's kind of this
idea of pop psych like you need to study something deliberately for 10000 hours to
get good at it. And you know there's some debate about this number it's kind of made
up, I guess, but the essential idea of deliberate practice I think is very
useful. And it's exactly how I structured my study. And so what deliberate means is,
when you're learning you want to be thinking like purposefully like I want to
make sure that the project that I'm doing is making me get better. I want to be
actually thinking about how I'm structuring my training and then you want
to make sure that you're kind of always struggling because that's just how you're
growing. So essentially to do this you just need to keep picking projects that
have some like success and failure feedback mechanism that's tied to the real
world and you know with bug hunting like this is very obvious you know you're
either finding a bug or not. And as I mentioned you want something difficult but
achievable. And so this kind of order that I did the different projects I mentioned
in the beginning was specifically chosen so that each stage would be achievable to
me. But also like really really stretching what I could do. And there's a funny
anecdote there's this guy named Ben Franklin from American history. And I read
the story that he used to be really bad at writing and wanted to get better. So the
way he did it was he took an essay that looked perfect to him and then he took
notes on it and then a week later he rewrote the essay from the notes and then
he would just compare the goal versus what he had done and basically saw all the
shortcomings. And so that kind of just stuck in my head. And so I'll show like
how do you play this kind of trick to bug finding practice. And then just another
thing with setting goals for bug hunting. A lot of it is psychological. I think it's
almost psychological more than intelligence, for sure. And basically you
want to iteratively pick harder and harder project so that your tolerance for failure goes
up and up. And so by the time I was working on Chrome I worked on it every day for six
months, like right home from work until 1:00 AM, sleep, up, and then all day every
weekend and found nothing the whole time. And then just one day found something and
then from there all that accumulated struggle and effort, when the bug
precipitated it was just like a sign that all of these necessary skills were there
and then I was able to repeat it. And so now we'll talk about what that actually looked
like for bug hunting. So when you think about how to train the skill I think
there's kind of two constituent skills that are important and those are knowing
where to look and then recognizing the bug when you're looking at it. And this first
part is just from my own experience it seemed like just being a developer it's
pretty easy to get a sense for, you know, you can look at the Git logs like I'm
mentioning here, other crashes happening somewhere in the library, are bugs getting
reported publicly? Does the code look bad? You know it's not hard to tell that
something looks sketchy. But I think what's really hard is getting the bug to
kind of come out. And so that's where I'll talk about strategy kind of directly. And
so I kind of have this training idea, where essentially once you have this kind of
target in mind where it's a little bit of your skill range but you think it's
doable, you try to enumerate all the existing bug reports and then look through
each of them and then it's this like Ben Franklin idea like you take the bug and
then you look at. Usually there will be like you know this block of text and
they're mentioning like the file where it's happening and stuff and you can kind
of skim it and sense like, where the bug is. I went out without actually looking at what it
is. And so then you go over and you try to find it yourself. And you know it's really
important that you actively try to look for the bug yourself and kind of strain
yourself and when you've given up essentially then you look at what was the
bug. And then through that struggle it's usually pretty clear like what was the
fundamental thing you were missing. And you know just by repeating this process
constantly this is how you train. And so this is actually how I first ever started
on bug hunting was you know, some of you may know j00ru he's this like, really
talented researcher. He's been at it for a long time and I remember seeing this blog
post from him showing all these IDA pro bugs and it just kind of blew my mind.
Like wow someone took IDA and found like security vulnerabilities in it and then
when I looked at the bug reports they're pretty small so I thought OK how do I
practice and how could I have done this myself. So basically the first day, you
know they're all like integer overflow bugs I could barely even, like, I knew
what integer overflow was, but I hadn't actively looked for it before. And so I
was looking at the function and I couldn't find it. Basically I went to sleep feeling
like oh god like "I'll never be able to do this stuff." And then the next day I looked
at again. I was like "Oh yeah that's actually easy". And then I kind of failed
the second one and then by the third day I was like able to just see where they were
once you know I knew where to look. So that kind of made me think "OK, I'll just
keep doing this for a long time and keep doing harder and harder." So this is
essentially the strategy. Like, I think, you know, I'm probably the perfect example of
someone who was like an intermediate CTF player, really like insecure or whatever
like and just wanted to get into this but I had no idea what I was doing. And I just
kept thinking if I just believe in this kind of process you know hopefully it
works out. And so here's just like a little really basic roadmap if you want to
try to replicate what I did which is to focus on CTF because if you can do CTF
binary problems these are perfect examples of a kind of training where you try to do
something yourself. There's a write up and like once you can do these problems you
know all the kind of low level details that are needed. You know what a bug is.
Things like that. And then from there you just kind of progressively do harder and
harder targets. And so there's kind of this component where like you know, I
don't - you can't really assess your own ability like how much of this is innate or
something and it just seemed to me that regardless of that, like I'm saying here,
you know, "This isn't chess where you have people trained from birth with perfect
study and decades of, you know, like we're barely figuring this stuff out and it's
just kind of a huge mess." And so there's plenty of room for new people to join in.
And then also there's a lot of these kind of stories about people who are just
insanely naturally gifted and stuff. And I tried really hard to like, look into what
these people are actually doing and I haven't found a case where someone wasn't
working extremely hard. And so you know just keep that in mind. So just for the
sake of time I won't go into this too much but if you're looking at the slides later
I just kind of give more detail on like how I pick the mini projects and got down
to Chrome. So now let's talk about fuzzing and so before I get into it, I should
emphasize that you should really know how to do auditing and the first couple of
years like not until into the six months of failure on Chrome, you know, I was
doing auditing the whole time. And I think fuzzing gets a bad rap because people
think that these are unrelated strategies and people are only a fuzzer person or an
auditor person. And really I think these things are extremely like their work
really well together. But you can't really know why fuzzing is failing or how does it
even apply it or where to apply it without being able to audit yourself. And part of
this was like, I noticed on Chrome that I could audit things but essentially the bug
density was so low on the sandbox attack surface that I needed a way to kind of
automate what I was looking for in each subsystem I was looking at. So you know
you have like 20 subsystems that you want to read, well you know it takes about a
week each minimum to learn. It's a lot faster to try to fuzz for like a day or
two each thing and then... I don't know like it's... I can't explain it it just
did random things and then this is what worked. So. So how would you practice
fuzzing. It's really the same idea that I had about auditing where you take a bug
and just ask yourself like how would I have written a fuzzer in the first place
to hit the bug. How could I have known to write the fuzzer that would have triggered
this. Am I lacking something in auditing ability? Am I not able to write fuzzers
well enough and actually it took me probably like a year of fuzzer writing to
get good enough where I could actually act on my ideas, like, just it's kind of
tricky. And so we'll get back to it later but this exact idea of practicing fuzzing
on something that looks un-fuzzable is how I found this real exploitable sandbox
escape. So really quick, just for those of you who don't know too much about fuzzing,
at least in like the current meta. Essentially there's this tool called AFL
that came out in 2014 which I think really shifted how well fuzzing worked. And the
idea is essentially that you have some corpus of inputs that you want to Fuzz and
then as you're mutating them you're looking for coverage feedback which is
compiled into your code and then as you're mutating and running new test cases when
you find new coverage you take that input and put in your corpus and over time your
corpus kind of grows and grows as more coverage is hit. And so there's... this
just seems to work really well and then there's another version of this basically
called libFuzzer, and this is just written by the LLVM project and the same people
who wrote address sanitiser also wrote libFuzzer and just in my experience it's
written in a way that's a lot more extensible and easy to understand and play
with. And so it makes it kind of easier to audit and fuzz together. And so if you
want to think about what fuzzing is, essentially you're trying to replicate the
normal testing process, but kind of parameterizing like what a unit test would
be doing with some input bytes, that you're just feeding into something and seeing if
it crashes. And so what's interesting is there's kind of this gap in the middle of
like an end to end test which AFL will give you just feed [it] a binary or like
the unit test which libFuzzer will give you where you just keep stuffing bytes
into a parser and real security vulnerabilities are kind of logical in
nature. And I think that's why people think that fuzzing isn't applicable and I
think there's actually kind of this part in the middle where if you see a few
components that look suspicious and then you can integrate them and fuzz them in
isolation, but have the complexity that you'd kind of see in the real program,
that's where a lot of bugs come out. And so how we do this is using a grammar. And
so essentially it's combining generative fuzzing with coverage guided fuzzing and
so we'll touch on how that works in a minute, but just for some more evidence on
why does this work well, like I'm not the only person who is doing this. Kind of
simultaneously myself and two other people I guess seem to have stumbled across this
idea last year or two years ago, and those are Syzkaller and Lokihardt. So Syzkaller
is a kind of fully automated Linux kernel fuzzer. And if you guys haven't seen this
it's kind of hilarious like essentially they are automatically generating zero day
bugs, like tens per month at least and they automatically generate the test case,
like submit the report when the commit comes in it's like automatically tracked.
It's basically this 0-day generator sitting there. *laughter* Yeah I know! And
I see this. I'm like OK there's 3000 bugs that are being found. There's a web app
for it and you can just download it, you know. And I saw the Linux talk from the
author of Syzkaller and the YouTube videos has like 100 views and stuff. I'm just
like OK, so people need to reiterate how important this stuff is. So then there's
Lokihardt as well who's like a famous, extremely talented, kind of canonical
auditing person and he seems to be doing a very similar thing with Chakra and V8 and
he's finding like tens of interesting exploitable bugs. And then there's me who
applied this on the Chrome sandbox and found over 30 bugs, about half of which
are security relevant, and then five of which were a sandbox escape without render
code execution. So you know this is just to emphasize like we're finding really
important things with this technique. And since I discussed this the first time a
couple of months ago at PoC conference it's been used by someone in their Chrome
security team to fuzz SQLite, and they're already finding new bugs in the first
week. So just more of the evidence like here's the kind of the breakdown of some
of the bugs I found with this strategy. So just to highlight a couple of them, or
maybe three of them. So the first one was an out of bounds read - just an integer
overflow in blobs. And this lets you... you can make a blob and then ask to read
part of it, and then the offset could have been negative and there's integer overflow
- they got the check wrong so it was a full memory disclosure from the browser
process. There's also this AppCache use after free which is what I used in the
exploit this year. And then finally... I guess the critical bugs are pretty
interesting so, two of these I guess the first pair are in QUIC and the first
one is a stack buffer overflow with just a bad packet that comes in over the network.
So you just browse to an attacker site and they stack buffer overflow Chrome browser
process which is outside the sandbox and it jumped over the stack cookie so that
was bad. And then then these block file cache problems. These were in the HTTP
caching mechanism which is also in the privileged process and these were actually
crashing in the wild for three years and they didn't know how to... I guess I don't
know if they didn't have resources or they didn't know how to address the problem or
something, but I sent them the test case and then they closed like four bug reports
in ancient bugs. So you know it just goes to show that this kind of technique works
in a variety of really interesting places that are really important. And so now
let's get to the boring stuff. So what's Protobuf. Well Protobuf is this data
serialisation format from Google, and it doesn't really matter that it's Protobuf,
just this idea is you want some kind of... you want to encode like a little language
for yourself that expresses what you want to fuzz a kind of a higher abstraction
layer than just fuzzing bytes randomly. And so if any of you have done functional
programming like, I had been doing stuff with OCaml and quickcheck for a couple of
years, and then when I saw this I just immediately recognized the pattern.
Essentially what you can do is, you can create this little tree structure of just
basic types like enum, you create these messages, you can just kind of specify
actions you want your fuzzer to take. And then next what libprotobuf-mutator will do
is, it will take the specification you've written and link it into libFuzzer so that
it will automatically fuzz and create these, like, trees that are these kind of
random ASTs from this little language you wrote and then you can kind of parse this
language which sounds crazy, or more hard than it is, but you essentially you can
generate this highly structured input which makes it a lot easier to explore
like, logical type of bugs. So I just really want to emphasize this strategy can
be used to fuzz anything and so kind of this same exact idea is being used to find
bugs in caching APIs, encrypted networking protocols, kernels, sandbox, serialisation
code, stateful systems that have IPC and network interaction and timing as part of
it, which is what we'll show at the end. And so like what's what's common here. You
know we just fuzz all of these different systems in the same way. The idea is, as
an auditor what you do is you kind of notice like "okay there's some subsystem,
like some caching mechanism with a simple API" and you look at how it's implemented
and it looks complicated, so you think "okay, you know if I can write a fuzzer in
like a few hours for this, you know it seems like high value". So once you kind
of play with the API a bit and understand like how the API works you know you can
just write this little specification for the API in Protobuf, and go ahead and
write the fuzzer. So basically I'll show how this works on Chrome. So just to make
sure I cover all of the background knowledge, for those of you that don't
really care about fuzzing or don't care about anything else at least you can get
bootstrapped on Chrome IPC research. The basic idea of how the Chrome sandboxing
situation works is - when I'm saying I'm finding bugs in the sandbox, like it's really
finding bugs in the browser process which are reachable from a sandboxed process and
so the sandbox itself is just constraining these render tab processes. So they can't
really do much and then what you want to do is jump from there to the browser
process which can do anything. It's a very common model. Like almost... on 3DS you
have userland kernel then security code processor you have a Linux like you might
have a userland process and then in the sandbox there's some APIs in the kernel
you can hit - syscalls you can hit and basically everything just keeps boiling
down to "there's some API that you can look at from the less privileged context".
And then if you can trigger a bug in that API you escape, and you know, this kind of
applies everywhere. And so this idea of understanding self-contained chunks of
syscalls in Linux - like hundreds - but being able to look at and say like okay,
here are 10 related syscalls. This is a subsystem that I want to fuzz in isolation
- like this is kind of how you think about it. And so if you just want to get started
on Chrome what you want to do is look at, "OK what are these endpoints in the
browser process that I can reach from the render", and then.. you don't really have
to understand how IPC works to do this. You just have to be able to recognize what
you're allowed to hit from the render to the browser and what's actually in the
browser. And so fortunately the Chrome codebase is pretty well organized so they
just tell you if you just don't really go into any folder that says browser in it.
All of this is outside the sandbox and prone to sandbox escape. And so most of my
bugs I found were in this content browser subsystem kind of thing. But you can look
anywhere and I think like all these results I've had the in last year were
just in one folder. And so you know there's so many other places where bugs
can manifest that I didn't even look at. So basically there's plenty of room for
more. So just to go in on what I did is - in this kind of content stuff - is: you
just want to see where APIs are reachable from the renderer are enumerated and those
are in this RenderProcessHostImpl::Init function. So yeah C++ kind of wordy but
you get used to it! Basically there's there's two places where the APIs are set
up, or the interfaces are exposed. Those are CreateMessageFilters() and
RegisterMojoInterfaces() and it took me a while to realize where these were. Like a
year or something. But like those are the key functions to look at. And so I'll skip
over old style IPC because it's going away, but it's pretty easy to figure out
what's going on if you look at it. So I'll talk a bit about Mojo. So essentially this
is a new IPC platform that the Chrome team has developed and the idea is they want
to, I guess, simplify this process for developers in terms of defining an
interface that you want to expose to a render or some other client somewhere
else, and essentially you write these little interface files called .mojom and
then the build system will generate all the C++ glue for you - you can just like
subclass something and then it handles all the mechanics of actually exposing this to
other processes and so on. And so as a security researcher you know you don't
really care about that. All you care about is "what can I reach" and "how do I know
what to fuzz" or something. So what I guess I looked at is just: what are some
of the .mojom files that are subclassed in this content/browser and you can
just do little grep to check this. So essentially the AppCache is one of the
bugs I found this year. And here's the API that the render can... these are all the
messages that the render can send to the browser and along with the types of
documents. And so you know that's pretty straightforward. So in the browser process
this is the code that we're trying to attack which is the actual C++
implementation code for this API. And so you can see they're subclassing there and
then they just make sure to override all these virtual functions that actually
implements the API. And so I won't go too into detail on this part because it's a
little boring, but essentially: how does a render get from it to all the way over to
this kind of browser C++ code? Well it essentially goes through this request
mechanism where the render tells the browser process "Hey I have this kind of
request to access this interface" and then it'll actually just create that
DispatcherHost implementation object and just feed in that request over there. So
essentially stuff gets glued together somehow. And then there's this stuff which
is kind of ugly, but I mean here's where you're actually exposing the ability to do
this. So here's here's where the request comes in, and then where this requests
handler function gets fed in as that thing I mentioned earlier - the
RegisterMojoInterfaces. So it's named pretty well it's kind of easy to follow.
And they're adding new stuff constantly all of this stuff is on the attack
surface. Like I think I stopped Chrome a couple of months ago, I think I looked and
there's like you know five new APIs in there, they're constantly adding things.
So just a quick point about this. Essentially you want to do fuzzing
in-process with this LibFuzzer+protobuf- mutator strategy, and you don't want to be
actually doing IPC - it's just very brittle and weird. So what you really want
to do is just like here's the C++ object I want to just instantiate it and call those
functions myself and then this whole thing is just very lightweight and easy to play
with which is... you know having a lightweight and very easy to rebuild,
tweak something and play with it, print things... like the faster you can iterate
the better so anything that's too complicated, like the success rate goes
way down. So essentially you know the fuzzer that I made open source is the way
you should do it. But the way I actually did it was: I just made the object...
commented out the private... I don't know if you can see it on here... Yeah, so just
commented out the private, created the object, started calling these things
randomly, it would crash and I would just hand fix things and you know it's kind of
sloppy but you're testing something in a very small unit that's not really exposed
to that kind of testing. So now let's kind of put together everything I've talked
about so far. So this exploitable AppCache Use-After-Free I found this year was found
using this same idea of deliberate practice. So I looked at this AppCache
subsystem in the browser process, and I noticed that there were three old bug
reports that were triggering memory corruption and they were pretty
interesting because they involved different kind of ways of attacking and
these things had clearly been audited and I had actually seen these bugs a couple of
years ago and I kind of used it as evidence to myself at the time that
fuzzing doesn't work and you need auditing. But it kind of stuck in my head
and I kept thinking "Someday I'll come back to this and like I'll overcome it"
you know. So essentially what's interesting is - I've already talked
about - you know - it's easy to specify the API and just feed IPC messages into
it. And I think everyone kind of understands that, who does any IPC
fuzzing. But then there's also this idea that you've got some remote server that
the AppCache thing like creates a network request, some server's serving the request
and doing different things, and so on the second bug it actually matters when things
were - like when the server was returning data. Because some jobs like stay alive
and then if you send an IPC message to close your session and then the job is
still alive, there's like a raw pointer somewhere, and you know something going on
that it matters that the server keeps the connection open. And then the last thing
is just kind of a logical issue. And if the server returns these HTTP codes in the
headers of the response in this kind of weird order, you trigger some logical bug
that actually leads to memory corruption. And so you know I looked at this and I
said OK, well, so what do we need to test to cover all this? Basically IPC, Network,
and that timing. And so not only that, but this is kind of a stateful thing. So we
want to make sure that for each fuzzing session that we kind of reset the state
completely. And fortunately in C++ this isn't too hard because you just destroy
the object and if it doesn't exist anymore what state is there. So you just make sure
that you don't leave things lingering. So yes I just had this basic idea: we'll call
random IPCs with this fuzzed input, we return random data from the network, and
then, we reset the state of the cache on every iteration. And then part of it was
thinking OK, if I can repro these old bugs if I reintroduce them by editing the
source, this is kind of appealing to this deliberate practice idea that I could have
written a fuzzer that would trigger these old things, and this is a kind of the idea
I was pursuing when actually triggered a new bug. So now what's tricky about this
is if you just return random data from the network you're not going to make much
progress. And this is kind of where the auditing background comes in. You want to
think about what is expressive enough of... Like, how do I make my fuzzer
expressive enough that I can hit everything, but then not so generic that
it's just spraying... like it's just noise. And so I'll show he did that in
this specification and so at a high level I kind of root node in the AST or the tree
of the fuzzier message is this session message, and then this just contains a
sequence of commands and so... Commands are something I also made up, and so the
first ten of them are all the different IPC calls I can do, the eleventh one is
handling any pending requests or pre- caching like a response to any new
requests that comes in. So that handles like both the asynchronous case where it
makes a request and it's waiting for the server and also like the synchronous
version where the response comes immediately. And then lastly this run
until idle thing which essentially just... it helps you... like if you place these
RunUntilIdles randomly as you're doing these IPC messages, you're kind of
flushing the queue of accumulated work. And so, what this lets you do, is kind of
identify these race condition type things. Because you can do something like do a
bunch of IPCs that come in and are handled at the same time without actually
serving... like actually doing the work yet, and then you do this RunUntilIdle and
then all the work happens. And you know I didn't think of this a priori in some
smart way, I just looked at the unit tests and I just tried to think about like "OK
how are these developers already testing it". And this is what it looked like
they're doing. So these messages are very easy to write, essentially just provide
for each IPC message that I could have sent to this thing, just make sure all the
arguments are correct, and then there's a little bit of cleverness which is like the
HostID also breaks down to just an enum of like 0,1,2 because just from
looking at the code you know that if I'm randomly creating hosts, destroying them
and stuff over the whole 4 billion int32 IDs, like it's just going to fall apart
and not find anything interesting. So I just constrained that for the URL. That is
also a custom message that I constrained to just return a few premade legit URLs so
that way I'm also not testing the URL parsing stuff. So then, how do I handle
the network? Well, I just read the source and looked at "what are all the types of
HTTPS response codes that affect control flow?". And I just enumerated them and
then for any given request, that comes in from the AppCache system, I just encode
anything interesting about the response that I thought of just by reviewing the
source. And it seems like the things that mattered were those HTTP codes whether or
not the headers asked AppCache to do caching or just download it once. And then
also the AppCache can request from the server this manifest file which has some
metadata about what files that it should be caching. And so essentially just all of
this is encoded in one message. And so how you go from this like high level
description to actually fuzzing is just this. So you can see how simple it is.
You're really just... you know I looked at the unit test code and saw how they set up
this AppCache service. And so they let you pass in this URLLoaderFactory, and what
this is, is just this kind of unit testable network request thing so this is how I'm
like, intercepting the network requests and feeding data. And so I do this little
setup and then here I just create the one render to browser host. This just kind of
simulating how you would do the Mojo stuff if it was the real render to browser
interaction, and then I just go through those commands that I mentioned and just
do these things. So I mean this is all it is. You just pull the HostID out of this
Protobuf message that we're getting at the top there - that session that I defined as
the top level TreeNode. And you just go through and you just call the APIs that
are there. And so, how to get the network stuff to work, as I mentioned they have
this like mock URLLoaderFactory - also C++-y! But essentially it's this... well okay
so this is when I basically handle one of my request messages that I came up with. I
just simulated a response. This is a built in like unit test function that they have
in their codebase and I just pass in the relevant bits that came from that message.
So, yes this is what it looks like. I have some kind of DoRequest helper function and
then I just pass my stuff through to it. And so it takes that URLFactory and then
serves responses to anything that's waiting and then what's interesting here
and what's necessary to find the bug is that - I mentioned that this is
asynchronous - so what will happen is when you do RegisterHost... and then if I go
back to.. Yeah, you like register host, select a cache, do some things - like the
AppCache will make a request to the server and then get this manifest, and then it
will start making requests to download things and then these things are pending like
responses that it's waiting for from the server. And so it actually mattered that
you mutate the state further before those responses come in. And so by doing this
like in between the IPC messages - not like preloading the network factory with a
bunch of responses - I'm actually serving things… Like I'm not encoding an assumption
about when I'm serving responses. And I know this is kind of tedious to go so into
detail, but essentially you run this thing that's maybe 150 lines or something, and
then trigger this bug with AddressSanitiser. And so essentially a Use-After-Free
happens and what what's going on here is you can see the scoped_refptr
pointer destructor. And it turns out that when... yes so when you go to unregister
the host - like it's an IPC message there at the bottom that I that I send - and
then it just accidently... This is kind of inaccurate, this stack trace, but
essentially some ref count goes from one to zero, and then it starts destroying
this AppCache object. And then in the destructor one of these requests was
waiting on a response from the server, and then it essentially gives a reference back
to that other object. And that's kind of eliding some details but essentially the
refcount went back up to 1 and then, now you're adding a bunch of references all
over the place to something while it's being destroyed. And so what happens is
now you have all these pointers to a freed object and then you can trigger access to
that freed thing again later. And so this kind of the recipe for an exploitable bug.
And so I just want to point out that all of this fuzzer is open source and it's
just in the Chrome codebase. So if you download it or go online to the code
search tool you can search for appcache fuzzer and it will come up. So then real
quickly, just to cover the exploitation. You know I guess I have more time and I
thought - I compressed this a lot! But essentially I did this part in a chain
with two other guys - Saelo and Niklas - and so Saelo provided the RCE bug. And so
from there we get code execution in the render, and then this lets us send
arbitrary IPC messages and so it's kind of annoying to send IPC with Mojo like
arbitrarily, so we kind of piggybacked on the renderer-sided glue code for sending
these AppCache messages. So we just like found the C++ object and called into it
and then all in all we end up with this primitive where we can decref and like
release a reference to this refcounted thing. Like after it's been freed multiple
times. So, there's two stages to exploiting this - because we're in the
render and we only have one bug, we need to turn this into a memory disclosure. And
so you know fortunately this bug can be triggered repeatedly. And so the idea here
is triggering it once gives you this decrement-by-n primitive. And so when
you're releasing - you know if you ever hit zero - you'll trigger the destructor
again. And so essentially what you want to do for the leak is to not trigger the
destructor because it will blow up, but rather find a string somewhere in memory
where it is a string pointing to the heap and then decrement the string pointer so
then it starts like sliding somewhere else into the heap. So that when you read that
string back you're actually leaking heap data. And so, we did that. So there's some
object that had a standard C++ string at the beginning. On Windows the first..
keyword[?] is like the, er, pointer to the string data. So you decrement this.
It is actually a cookie object, so we just read the cookie back from the browser and
then in the cookie value we see the leaked bytes. And then from there there was a
vtable access that we can control in the destructor. So we make another fake object
that looks like it has one reference left. Make it hit zero so the destructor is
triggered and then this AppCache thing gets confused and essentially calls a
control vtable pointer. And then from there, those are the primitives you need
to write an exploit and then it was just a matter of putting it together. And if
you're curious about that again you should look forward to Niklas' talk. And so just
a summary: essentially starting all the way from the beginning you want to be
practicing deliberately. Keep working constantly, and keep identifying gaps and
actively working to improve. It sounds weird but you want to keep that in mind.
And use this new technique with LibFuzzer and protobuf-mutator - I can promise you
it's not going to be the last time you see someone using this. And I mentioned I've
started on XNU and we'll see some initial results pretty soon on that. It's working.
So yeah, and lastly never give up. It may take months but it's fine. So with that I
guess I'll open to questions. Yeah, thank you.
*Applause*
Herald: Okay thank you for that talk. If you have a question please line up at the
microphones in the room and try to limit your question to one single sentence. If
you would like to leave at this point please do that as quietly as possible so
that everyone else can still stay for the questions. And also if you're listening on
a stream you can ask a question online. Seems there is... no question? Ah there is
one. Microphone number 2, your question please.
Mic 2: Hello. I just want to ask why have you chosen Chrome for bug hunting? Was it
just like you picked one random browser and you started?
Ned: Yeah no. I mean it's basically just kind of the hardest thing I could think of
that I could plausibly do? You know just for the purpose of getting better. And
there's more to it. I think Chrome the way it's written is very amenable to research
and like I actually didn't know C++ before I worked on Chrome!
So like learning looking at a great example of the C++ code base and learning
from that was really helpful to me. And you know I glossed over kind of my path
but I was actually finding random obscure library bugs that weren't even reachable
at first. So, just the quality of Chrome makes it so that what your training is the
real talent not just being able to decipher bad code. So, I highly recommend
it. I can say that I definitely feel that the two years I invested on that one
project completely helped me get better. Herald: Thank you. Signal angel, question
from the internet. Signal Angel: Hello, there is one question
from the internet.... *inaudible* Ned: So the question is: "is it possible
to attack using Meltdown or SPECTRE?" I don't know. I guess it's possible. I was
essentially focusing only on application level bugs. So things that I could
trigger deterministically using only bugs in the Chrome code itself. And so... I
mean also those things came along way after I was doing my research. So you know
I can't comment on that but I'm sure someone knows.
Herald: Thanks. Ned: Yeah. Herald: I see no more people on the
microphones or questions on the internet. Yeah. OK. Thanks for your talk and thanks
for Q&A. Ned: Thank you.
*Applause*
*postroll music*
subtitles created by c3subtitles.de in the year 2020. Join, and help us!