WEBVTT
00:00:00.000 --> 00:00:19.960
36c3 preroll music
00:00:19.960 --> 00:00:25.420
Herald: So, Samuel is working at Google
Project Zero on especially vulnerabilities
00:00:25.420 --> 00:00:30.200
in Web browsers and mobile devices. He was
part of the team that discovered some of
00:00:30.200 --> 00:00:33.960
the vulnerabilities that he will be
presenting in this talk today in detail
00:00:33.960 --> 00:00:41.370
about the no user interaction
vulnerability that will be able to
00:00:41.370 --> 00:00:48.660
remotely exploit and compromise iPhones
through iMessage. Please give Samuel a
00:00:48.660 --> 00:00:55.890
warm round of applause.
Applause
00:00:55.890 --> 00:01:00.880
Samuel: OK. Thanks, everyone. Welcome to
my talk. One note before I start,
00:01:00.880 --> 00:01:05.900
unfortunately, I only have one hour. So I
had to omit quite a lot of details. But
00:01:05.900 --> 00:01:09.760
there will be a blog post coming out
hopefully very soon that has a lot more
00:01:09.760 --> 00:01:14.729
details. But for this talk, I wanted to
get everything in there and leave out some
00:01:14.729 --> 00:01:21.840
details. OK. So this is about iMessage in
theory some of it applies, or quite a lot
00:01:21.840 --> 00:01:26.050
actually applies to other messengers, but
we'll focus on iMessage. So what is
00:01:26.050 --> 00:01:31.290
iMessage? Yeah, it's a messaging service
by Apple. We've heard about it in the
00:01:31.290 --> 00:01:37.700
previous talk a bit. As far as I know, it
is enabled by default. As soon as someone
00:01:37.700 --> 00:01:42.759
signs into an iPhone with their account,
which I guess most people do, because
00:01:42.759 --> 00:01:48.540
otherwise you can't download apps.
Interestingly, anyone can send messages to
00:01:48.540 --> 00:01:55.211
anyone else. So it's like SMS or phone
calling. And then if you do this, then it
00:01:55.211 --> 00:02:01.390
pops up some notifications, which you can
see that here on the right screenshot,
00:02:01.390 --> 00:02:06.879
which means that there must be some kind
of processing happening. And so, yeah,
00:02:06.879 --> 00:02:11.080
this is like default enabled, zero click
attack surface without the user doing
00:02:11.080 --> 00:02:15.780
anything, there's stuff happening. And
then on the very right screenshot, you can
00:02:15.780 --> 00:02:23.700
see that you can receive messages from
unknown senders. It just like says there.
00:02:23.700 --> 00:02:29.829
This sender is not in your contact list,
but all the processing still happens. In
00:02:29.829 --> 00:02:36.920
terms of architecture, this is roughly how
iMessage is structured, not very, yeah,
00:02:36.920 --> 00:02:42.760
anything too interesting, I guess. You
have Apple cloud servers and then sender
00:02:42.760 --> 00:02:48.240
and receiver are connected to these
servers. That's pretty much it. Content is
00:02:48.240 --> 00:02:53.830
end to end encrypted, which is very good.
We heard this before, also. Interestingly,
00:02:53.830 --> 00:02:58.320
this also means that Apple can hardly
detect or block these exploits though,
00:02:58.320 --> 00:03:06.130
because, well, they are encrypted, right?
So that's an interesting thing to note. So
00:03:06.130 --> 00:03:11.920
what does an iMessage exploit look like?
So in terms of prerequisites, really the
00:03:11.920 --> 00:03:16.120
attacker only needs to know the phone
number or the email address, which is the
00:03:16.120 --> 00:03:22.160
Apple account. The iPhone has to be in
default configuration so you can disable
00:03:22.160 --> 00:03:26.910
iMessage. But that's not done by default.
And the iPhone has to be connected to the
00:03:26.910 --> 00:03:31.980
Internet. And in terms of prerequisites,
that's pretty much all you need for this
00:03:31.980 --> 00:03:38.169
exploit to work. So that's quite a lot of
iPhones. The outcome is the attacker has
00:03:38.169 --> 00:03:43.410
full control over the iPhone. After a few
minutes, I think it takes like five to
00:03:43.410 --> 00:03:48.970
six, seven minutes maybe. And it is also
possible without any visual indicator. So
00:03:48.970 --> 00:03:53.880
there's no... you can make it so there are
no notifications during this entire
00:03:53.880 --> 00:03:59.981
exploit. OK. But before we get to
exploiting, of course, we need a
00:03:59.981 --> 00:04:04.460
vulnerability and for that we need to do
some reverse engineering. So I want to
00:04:04.460 --> 00:04:09.060
highlight a bit how we started this or how
we approached this. And I guess the first
00:04:09.060 --> 00:04:14.450
question, you might be interested in, is
what daemon or what service is handling
00:04:14.450 --> 00:04:20.989
iMessages. And one easy way to figure
this out is you can just make a guess. You
00:04:20.989 --> 00:04:26.429
look at your process list on your Mac, the
Mac can also receive iMessages. You, like,
00:04:26.429 --> 00:04:30.890
stop one of these processes and then you
see if iMessages are still delivered. And
00:04:30.890 --> 00:04:36.140
if not, then probably you found a
process that's somewhat related to
00:04:36.140 --> 00:04:43.680
iMessages. If you do this, you'll find
"imagent", already sounds kind of related.
00:04:43.680 --> 00:04:47.510
If you look at it, it also has an iMessage
library that it's loading. Ok, so this
00:04:47.510 --> 00:04:55.170
seems very relevant. And then you can load
this library in IDA. You see a screenshot
00:04:55.170 --> 00:05:00.010
top right. And you find a lot of handlers.
So for example, this
00:05:00.010 --> 00:05:03.840
"MessageServiceSession handler:
incomingMessage:", and then you can set a
00:05:03.840 --> 00:05:07.170
breakpoint there. And then at that point
you can see these messages as they come
00:05:07.170 --> 00:05:13.230
in. You can dump them, display them, look
at them, change them. And so this is a
00:05:13.230 --> 00:05:17.040
good way to get started. Of course, from
there, you want to figure out how these
00:05:17.040 --> 00:05:22.460
messages look like. So, yeah, you can dump
them in there when they come in in the
00:05:22.460 --> 00:05:30.000
handler, on the right side you see how
these iMessages look like more or less on
00:05:30.000 --> 00:05:37.460
the wire. They are encoded as a PList,
which is an Apple proprietary format.
00:05:37.460 --> 00:05:45.140
Yeah, think of it like JSON or XML. And I
guess some fields are self-explanatory.
00:05:45.140 --> 00:05:50.160
So, "p", that's the participants in this
case this is me sending a message to
00:05:50.160 --> 00:05:57.270
another account I own. You have "T" which
is the text content of the message. So
00:05:57.270 --> 00:06:04.530
"Hello 36C3!". You have a version, for
some reason you also have an XML or HTML-
00:06:04.530 --> 00:06:11.190
ish field, which is probably some legacy
stuff. It's being parsed, this XML. But
00:06:11.190 --> 00:06:14.170
yeah, the whole thing looks kind of
complex already. I mean maybe you would
00:06:14.170 --> 00:06:20.200
expect a simple string message to just be
a string. In reality, it's sending this
00:06:20.200 --> 00:06:29.340
dictionary over the wire. So let's do some
more attack service enumeration. If you
00:06:29.340 --> 00:06:34.980
then do more reverse engineering, read the
code of the handler, you find two
00:06:34.980 --> 00:06:41.250
interesting keys that can be present,
which is ATI and BP, and they can contain
00:06:41.250 --> 00:06:49.270
NSKeyedUnarchiver data, which is another
Apple proprietary serialization format.
00:06:49.270 --> 00:06:55.340
It's quite complex, it has had quite a few
bugs in the past. On the left side you see
00:06:55.340 --> 00:07:01.930
an example for such an archive. It's yeah,
it's being encoded in a plist and then
00:07:01.930 --> 00:07:08.360
it's pretty much one big array that has,
like, every object has an index in this
00:07:08.360 --> 00:07:15.360
array. And here you can see, for example,
number 7 is some object, is the class
00:07:15.360 --> 00:07:24.500
NSSharedKeyDictionary. And I think key one
is an instance of that class and so on. So
00:07:24.500 --> 00:07:30.590
it's quite powerful. But really what this
means is that this serializer is now zero
00:07:30.590 --> 00:07:35.430
click attack surface because it's being
passed on this path without any user
00:07:35.430 --> 00:07:42.850
interaction. So I said it's quite complex.
It even supports things like cyclic
00:07:42.850 --> 00:07:47.889
references. So you can send an object
graph where A points to B and B points
00:07:47.889 --> 00:07:55.540
back to A for whatever reason you might
want that. Natalie wrote a great blog post
00:07:55.540 --> 00:08:00.040
where she describes this in more detail.
What I have here is just an example for
00:08:00.040 --> 00:08:06.200
the API, how you use it. This is Objective
C at the bottom. If you're not familiar
00:08:06.200 --> 00:08:12.190
with Objective C, you can think of these
brackets as just being method calls. So
00:08:12.190 --> 00:08:17.360
this is doing, in the last line, it's
calling the unarchivedObjectOfClasses
00:08:17.360 --> 00:08:25.170
method for this NSKeyedUnarchiver. You can
see you can pass a whitelist of classes.
00:08:25.170 --> 00:08:31.880
So in this case, it will only decode
dictionary, strings, data, etc. So looks
00:08:31.880 --> 00:08:38.750
quite okay. Interestingly, if you dig
deeper into this, this is not quite true
00:08:38.750 --> 00:08:44.470
because it also allows all the subclasses
to be decoded. So if you have an NS-
00:08:44.470 --> 00:08:49.089
something-something dictionary that
inherits from NSDictionary, then that can
00:08:49.089 --> 00:08:55.280
also be decoded here, which is quite
unintuitive I think. And this really blows
00:08:55.280 --> 00:09:01.760
up the attack surface because now you have
not only these 7 or so classes, but you
00:09:01.760 --> 00:09:09.650
have like 50. Okay. So this is what we
focused on when me and Natalie were
00:09:09.650 --> 00:09:17.230
looking for vulnerabilities. It seemed
like the most complex thing we found. We
00:09:17.230 --> 00:09:22.390
reported quite a few vulnerabilities here,
you can see it maybe a bit on the left.
00:09:22.390 --> 00:09:31.420
The one I decided to write an exploit for
is this 1917, reported on July 29th and
00:09:31.420 --> 00:09:38.400
then exploits sent on August 9th. Yeah,
mostly I decided to use this one because
00:09:38.400 --> 00:09:42.900
it seemed the most convenient. I do think
many of the other ones could be exploited
00:09:42.900 --> 00:09:47.610
in a similar way, but not quite as nice,
so would maybe take some more heap
00:09:47.610 --> 00:09:55.700
manipulation, etc. So then Apple first
pushed the mitigation quite quickly, which
00:09:55.700 --> 00:10:01.180
basically blocks this code from being
reached over iMessage. In particular, what
00:10:01.180 --> 00:10:08.430
they did is, they exactly no longer allow
subclasses to be decoded in iMessage. So
00:10:08.430 --> 00:10:12.760
that's quite a good mitigation, it blocks
off maybe 90 percent of the attack surface
00:10:12.760 --> 00:10:22.589
here. Yeah. So then they fully fixed it in
iOS 13.2. But again, after August 26th
00:10:22.589 --> 00:10:32.840
this was only just local attack surface.
OK, so what is the bug? It's some
00:10:32.840 --> 00:10:37.390
initialization problem during decoding,
the vulnerable class is
00:10:37.390 --> 00:10:42.000
SharedKeyDictionary, which again, it's a
subclass of NSDictionary, so it's allowed
00:10:42.000 --> 00:10:50.260
to be decoded. So let's take a look at
that. So, yeah. SharedKeyDictionary.
00:10:50.260 --> 00:10:56.000
Here's some pseudocode in Python. It's a
dictionary. So its purpose is to, well,
00:10:56.000 --> 00:11:01.880
look up keys to values or map keys to
values. The lookup method is really
00:11:01.880 --> 00:11:08.120
simple. It just looks up an index in a key
set. So every key dictionary has a shared
00:11:08.120 --> 00:11:13.760
key set and then that index is used to
index into some area. OK, so that's quite
00:11:13.760 --> 00:11:21.060
simple so most of the magic happens in the
SharedKeySet. And so what that does is
00:11:21.060 --> 00:11:27.690
something like compute a hash of the key.
Use that hash to index into something
00:11:27.690 --> 00:11:35.350
called a rankTable, which is an array of
indices. And then if that index is valid,
00:11:35.350 --> 00:11:41.530
so it's being bounced, checked against the
number of keys. Then it has found the the
00:11:41.530 --> 00:11:47.260
correct index and if not, it can recurse
to another SharedKeySet. So every
00:11:47.260 --> 00:11:53.260
SharedKeySet, can have a sub-SharedKeySet,
and then it repeats the same procedure. So
00:11:53.260 --> 00:11:57.980
it already looks kind of complex. Why does
it have... why does it need this
00:11:57.980 --> 00:12:03.160
recursion? I'm not quite sure, but it's
there. And so now we look at how this goes
00:12:03.160 --> 00:12:11.060
wrong. So this is the initWithCoder, which
is the SharedKeySet constructor used
00:12:11.060 --> 00:12:18.289
during decoding with the keyedUnarchiver.
And it looks pretty solid at first, it's
00:12:18.289 --> 00:12:26.070
really just taking the values out of the
archive and then storing them as the
00:12:26.070 --> 00:12:32.370
fields of this SharedKeySet. I have a, I'm
gonna go through the code here in, like,
00:12:32.370 --> 00:12:36.650
single step to highlight where it goes
wrong or what goes wrong here, what's
00:12:36.650 --> 00:12:43.339
wrong with this code. So we start with
SharedKeySet1 which implies there's gonna
00:12:43.339 --> 00:12:48.800
be another one. And at the start it's all
zero initialized. It's basically being
00:12:48.800 --> 00:12:54.220
allocated through ?calloc?. So everything
is zero. Then we execute the first line.
00:12:54.220 --> 00:13:02.020
Okay. So numKey, you see some interesting
values coming. So far this is all fine.
00:13:02.020 --> 00:13:06.600
Note, that you can set numKey, at this
point numkey can be anything because it's
00:13:06.600 --> 00:13:12.550
only being validated three lines further
down, right? Where it's making sure that
00:13:12.550 --> 00:13:20.670
numKey matches the the real length of this
array. So this is fine, but here it's now
00:13:20.670 --> 00:13:25.260
recursing and it's decoding another
SharedKeySet. So we start again. We have
00:13:25.260 --> 00:13:32.680
another SharedKeySet, all filled with
zeros and we start from the top. Again,
00:13:32.680 --> 00:13:39.740
numKey is one, so this is this is a
legitimate SharedKeySet, decoding a
00:13:39.740 --> 00:13:47.620
rankTable. And here we are making a
circle. So for SharedKeySet2 we pretend
00:13:47.620 --> 00:13:53.979
that its sub-KeySet is SharedKeySet1. And
this actually works. So the
00:13:53.979 --> 00:13:59.730
NSKeyedUnarchiver has special handling to
handle this correctly. So it does not
00:13:59.730 --> 00:14:06.410
create a third object and it makes the
cycle. And we're good to go. Okay. Next to
00:14:06.410 --> 00:14:13.950
decode the keys area. So this is fine.
SharedKeySet2 seems legitimate so far. And
00:14:13.950 --> 00:14:19.490
now it's doing some sanity checking. Where
it's trying, where it's making sure that
00:14:19.490 --> 00:14:25.941
this SharedKeySet can look up every key.
And so it does this for the only key it
00:14:25.941 --> 00:14:32.910
has, key one. Now, at this point, it's
again, remember, it's hashing the key
00:14:32.910 --> 00:14:39.120
going into rank table, takes out 42, which
is bigger than numKey. So in this case,
00:14:39.120 --> 00:14:44.819
this look up here has failed. And now it's
recursing to SharedKeySet1. Right? This
00:14:44.819 --> 00:14:53.530
was the logic. And at this point it's
taking out this hex41414141 as index,
00:14:53.530 --> 00:15:00.380
compares it against hexffffffff and that's
fine, and now it's accessing, null
00:15:00.380 --> 00:15:07.560
pointer, which is.. the keys area is still
null pointer plus, well, 41 41 41 41 times
00:15:07.560 --> 00:15:13.910
8. So at this point it's crashing. It's
accessing invalid memory, precisely
00:15:13.910 --> 00:15:21.140
because in this situation the
SharedKeySet1 hasn't been validated yet.
00:15:21.140 --> 00:15:30.500
OK, so that's the bug we're going to
exploit. I have these checkpoints just to
00:15:30.500 --> 00:15:35.490
think where we are, so we now have a
vulnerability in this NSUnarchiver API. We
00:15:35.490 --> 00:15:41.990
can trigger it through iMessage. So what
Exploit Primitive do we have? Let's take a
00:15:41.990 --> 00:15:50.240
look again at the lookup function, which
we saw before. So here where it's bold,
00:15:50.240 --> 00:15:54.770
this is where we crash. keys is null
pointer, index is fully controlled. So we
00:15:54.770 --> 00:16:00.959
can access null pointer plus offset. And
then what happens is the result of this
00:16:00.959 --> 00:16:06.220
memory access is going to be used as some
Object-C Object. So this is all
00:16:06.220 --> 00:16:09.660
Objective-C in reality, it's doing some
comparison, which means it does something
00:16:09.660 --> 00:16:16.940
like it called some method called
isNSString, for example. And then also
00:16:16.940 --> 00:16:24.529
eventually it calls dealloc, which is the
destructor. So yeah, we have... the thing
00:16:24.529 --> 00:16:29.209
it reads from whatever, it will treat it
as an objectif C-Object calls a
00:16:29.209 --> 00:16:36.410
message on it. And that's our Exploitation
Primitive. Okay, so here we are. How do we
00:16:36.410 --> 00:16:44.030
exploit this? So the rough idea for
exploiting such vulnerabilities looks like
00:16:44.030 --> 00:16:49.850
this. You want to have some fake
Objective-C object somewhere in memory
00:16:49.850 --> 00:16:55.079
that you're referencing. So again, we have
an we can access an arbitrary absolute
00:16:55.079 --> 00:17:00.150
address. We want some fake Objective-C
object there. Every Objective-C object has
00:17:00.150 --> 00:17:06.509
a pointer to its class. And then the class
has something called a method table, which
00:17:06.509 --> 00:17:10.940
basically has function pointers to these
methods. Right. And so if we fake this
00:17:10.940 --> 00:17:17.309
entire data structure thing, the fake
object and the fake class, then as soon as
00:17:17.309 --> 00:17:22.379
the process calls some method on our fake
thing, we get code execution. So we get
00:17:22.379 --> 00:17:28.459
control over the instruction pointer and
then it's game over. So that's going to be
00:17:28.459 --> 00:17:37.770
our goal for this exploit. So here we have
two different types of addresses: On the
00:17:37.770 --> 00:17:43.159
left side we have heap addresses or data,
really. And on the right side, in this
00:17:43.159 --> 00:17:49.929
NSString-thing we need library addresses
or code addresses, simply because on iOS
00:17:49.929 --> 00:17:57.200
you can't have writeable code regions. So
we have to necessarily reuse existing
00:17:57.200 --> 00:18:00.919
code, do so to something like ROP also.
So we need to know where libraries are
00:18:00.919 --> 00:18:07.290
mapped for this. And this is exactly the
problem we are gonna face now because
00:18:07.290 --> 00:18:12.310
there is something called ASLR, Address
Space Layout Randomization. And what it
00:18:12.310 --> 00:18:17.269
does is it will randomize this entire
address space. So on the left side, you
00:18:17.269 --> 00:18:23.419
can see how a process looks like, how the
virtual memory of a process looks like
00:18:23.419 --> 00:18:28.529
before ASLR. And there everything is
always mapped at the same address. So if
00:18:28.529 --> 00:18:33.069
you start the same address twice on
different phones, maybe even without ASLR
00:18:33.069 --> 00:18:37.200
the same library is at the same address,
the heap is always at the same address
00:18:37.200 --> 00:18:41.559
stack. Everything is the same. And so this
would be really simple to exploit now
00:18:41.559 --> 00:18:47.539
because, well, everything is the same.
With ASLR everything is shifted and now
00:18:47.539 --> 00:18:53.000
all the addresses are randomized and we
don't really know where anything is. And
00:18:53.000 --> 00:19:00.220
so that makes it harder to exploit this.
So we need an ASLR bypass is what this
00:19:00.220 --> 00:19:07.309
means. We're gonna divide it into two
parts. So the heap addresses we get them
00:19:07.309 --> 00:19:11.519
from in a different way than the library
addresses. So let's see how we get heap
00:19:11.519 --> 00:19:18.100
addresses. It's really simple honestly,
what you can do is heap spraying, which is
00:19:18.100 --> 00:19:24.749
an old technique. I think 15 years old
maybe. And it does still work today. The
00:19:24.749 --> 00:19:29.789
idea is that you simply allocate lots of
memory. So if you look at this code there
00:19:29.789 --> 00:19:35.069
put on the right, which you can use to
test that, what it does is that it allocates
00:19:35.069 --> 00:19:40.999
256 megabytes of memory on the heap with
malloc. And then afterwards there's one
00:19:40.999 --> 00:19:47.629
address or there's many addresses. But in
this case, I'm using this hex110000000
00:19:47.629 --> 00:19:54.059
where you will find your data at. Okay. So
just spraying 256 megabytes lets you put
00:19:54.059 --> 00:19:59.659
controlled data at a controlled address,
which is enough for this first part of the
00:19:59.659 --> 00:20:05.330
exploit. The remaining question is how can
you heap spray over a iMessage. That's a
00:20:05.330 --> 00:20:10.830
bit more complicated. But it is possible
because NSKeyedUnarchiver is great and it
00:20:10.830 --> 00:20:18.260
lets you do all sorts of weird stuff which
you can abuse for heap spraying. So, yeah.
00:20:18.260 --> 00:20:24.299
Blog posts will have more details. Okay.
So we have these, the heap addresses. We
00:20:24.299 --> 00:20:32.340
have them. We need the library addresses.
Let's go back to the virtual memory space.
00:20:32.340 --> 00:20:38.149
On iOS and also on macOS the libraries -
so maybe in this case all three libraries,
00:20:38.149 --> 00:20:42.919
but in reality, it's like hundreds of
system libraries - they are all prelinked
00:20:42.919 --> 00:20:48.849
into one gigantic binary blob, which is
called a dyld_shared_cache. The idea is
00:20:48.849 --> 00:20:53.539
that this speeds up like loading times
because all the interdependencies between
00:20:53.539 --> 00:20:59.270
libraries are resolved pretty much at
compile time. But yeah, so we have this
00:20:59.270 --> 00:21:04.981
gigantic binary blob and it has everything
we need. So it has all the code, it has
00:21:04.981 --> 00:21:10.769
all the ROP gadgets and it has all the
Objective-C classes. So we have to know
00:21:10.769 --> 00:21:18.980
where this dyld_shared_cache is mapped. If
you dig into that a bit or if you look at
00:21:18.980 --> 00:21:25.499
the documentation or the the binaries, you
can find out that it is going to be mapped
00:21:25.499 --> 00:21:32.380
always between these two addresses. So
between 0x180000000 and 0x280000000, which
00:21:32.380 --> 00:21:37.429
leaves only a 4 gigabyte region, so it's
only being mapped in these 4 gigabytes.
00:21:37.429 --> 00:21:43.409
And then the randomization granularity is
also 0x4000 because iOS uses large pages
00:21:43.409 --> 00:21:49.759
so it can only randomize with page
granularity, and that page granularity is
00:21:49.759 --> 00:21:57.760
0x4000. But really what's most interesting
is that on the same device, the
00:21:57.760 --> 00:22:03.570
dyld_shared_cache is only randomized once
per boot. So if if you have two different
00:22:03.570 --> 00:22:08.270
processes on the same device, the shared
cache is at the same virtual address. And
00:22:08.270 --> 00:22:12.259
if you have one process, then it crashes
and you have another one. And so on, like
00:22:12.259 --> 00:22:17.590
the shared cache is always going to be at
the same address. And that makes it really
00:22:17.590 --> 00:22:24.249
interesting. And also, it's one gigabyte
in size. It's gigantic. So it's not too
00:22:24.249 --> 00:22:31.650
hard to find in this four gigabyte region.
Right. So this is what our our task has
00:22:31.650 --> 00:22:37.340
boiled down to at this point. We have this
address range, we have the shared cache.
00:22:37.340 --> 00:22:44.649
And all we need to know now is what is
this offset? So let's make a thought
00:22:44.649 --> 00:22:51.039
experiment. Let's say we had an oracle
which would tell us... which we could give
00:22:51.039 --> 00:22:55.729
an address. And it would tell us if this
address is mapped in the remote process.
00:22:55.729 --> 00:23:03.480
OK, if we have this, it suddenly becomes
really easy to solve this problem, because
00:23:03.480 --> 00:23:08.500
then all you have to do is you go in 1
gigabyte steps the the size of the shared
00:23:08.500 --> 00:23:16.250
cache between these two addresses and then
at some point you find a valid address. So
00:23:16.250 --> 00:23:20.750
maybe here after 3 steps, you find a valid
address, and then from there you just do a
00:23:20.750 --> 00:23:26.210
binary search. Right. Because you know
that somewhere between the green and the
00:23:26.210 --> 00:23:31.100
second red arrow, the shared cache starts.
So you can do a binary search and you find
00:23:31.100 --> 00:23:39.740
the the start address in logarithmic time
in a few seconds, minutes, whatever. So
00:23:39.740 --> 00:23:43.650
obviously the question is what? How? Where
would we get this oracle from? This seems
00:23:43.650 --> 00:23:51.399
kind of weird. So let's look at receipts,
message receipts. So iMessage like many
00:23:51.399 --> 00:23:57.859
other messengers - I think pretty much all
of them that I know - send receipts for
00:23:57.859 --> 00:24:04.779
different things. iMessage in particular
has delivery receipts and read receipts.
00:24:04.779 --> 00:24:11.230
Delivery received means the device
received the message, read receipt means
00:24:11.230 --> 00:24:15.929
the user actually looked - opened the app,
looked at the message. You can turn off
00:24:15.929 --> 00:24:21.469
read receipts, but as far as I know, you
cannot turn off delivery receipts. And so
00:24:21.469 --> 00:24:27.769
here on the left you see a screenshot.
Three different messages were sent and
00:24:27.769 --> 00:24:31.810
they have three different states. The
first message was marked as read, which
00:24:31.810 --> 00:24:37.020
means it got a delivery receipt and a read
receipt. The second message is marked as
00:24:37.020 --> 00:24:41.899
delivered. So it only got a delivery
receipt and the third message doesn't have
00:24:41.899 --> 00:24:51.049
anything. So it hasn't received any
receipt. OK. So why is it useful? Here on
00:24:51.049 --> 00:24:58.000
the left is some pseudocode of imagent's
handling of how it handles messages and
00:24:58.000 --> 00:25:04.369
when it sends these receipts. And so you
can see that it first parses the plist
00:25:04.369 --> 00:25:10.319
that's coming in and it's then doing this
nsUnarchive at some later time. And this
00:25:10.319 --> 00:25:14.769
is this is exactly why all but would
trigger during nsUnarchive. And only then
00:25:14.769 --> 00:25:22.970
does it send a delivery receipt. Right. So
what that means is if during our during
00:25:22.970 --> 00:25:27.580
our nsUnarchive, if we can trigger the bug
and cause a crash, then we have somewhat
00:25:27.580 --> 00:25:33.750
of a one bit sidechannel. Right. Because
if we cause a crash, then we won't see a
00:25:33.750 --> 00:25:38.690
delivery receipt. And if we don't cause a
crash, then we see a delivery receipt. So
00:25:38.690 --> 00:25:47.670
it's a one bit of information. And this is
going to be our oracle. All right. So
00:25:47.670 --> 00:25:53.080
ideally, you have a vulnerability that
gives you this perfect oracle of is an
00:25:53.080 --> 00:25:59.249
address mapped or not? So crash, if it is
not mapped, don't crash if it mapped. In
00:25:59.249 --> 00:26:04.110
reality, you probably will not get this
perfect oracle from your bug. On the left
00:26:04.110 --> 00:26:10.040
side, you see the real Oracle function for
this vulnerability, which is, well it has
00:26:10.040 --> 00:26:18.159
to be mapped. OK. But then it's also using
the value that it's reading. And so it
00:26:18.159 --> 00:26:24.039
will only not crash if the value is either
0 or if it has the most significant bit
00:26:24.039 --> 00:26:29.340
set, that is some like pointer taking
stuff or if it's a real legitimate pointer
00:26:29.340 --> 00:26:34.899
to an Objective-C object. So this Oracle
function is a bit more complex, but the
00:26:34.899 --> 00:26:41.059
similar idea still works. So you can still
do something like a binary search, and
00:26:41.059 --> 00:26:48.439
then infer the shared cache start address
in logarithmic time. Right. And so it only
00:26:48.439 --> 00:26:54.049
takes maybe five minutes or so to do this.
But for this for this part, again, I have
00:26:54.049 --> 00:27:01.519
to refer to the blog post which will cover
how this works. OK. So this is the summary
00:27:01.519 --> 00:27:07.710
of the remote ASLR bypass. Two phases,
there's linear scan where it's just
00:27:07.710 --> 00:27:13.289
scanning, sending these payloads and
checking if it gets the receipt back, and
00:27:13.289 --> 00:27:17.459
the first time it gets a receipt back, it
knows. OK. This address is valid. I now
00:27:17.459 --> 00:27:22.090
found an address that is within the shared
cache. And at that point it starts this
00:27:22.090 --> 00:27:28.429
searching phase, which in logarithmic time
figures out the exact, precise starting
00:27:28.429 --> 00:27:36.940
address. So there's a few common questions
about this that I want to briefly go into.
00:27:36.940 --> 00:27:42.169
The first maybe obvious question is, can
you really just crash this agent like 20
00:27:42.169 --> 00:27:49.799
plus times? And the answer is yes. There's
no indicator or anything that the user
00:27:49.799 --> 00:27:55.759
would would see that this demon crashes.
The only thing you can do is you can go
00:27:55.759 --> 00:27:59.899
into like settings, privacy, something
something, crash log something, and then
00:27:59.899 --> 00:28:07.129
you can see these crash logs. Second
question is you can I think by default,
00:28:07.129 --> 00:28:11.820
the iPhone is configured to send crash
logs to the vendor, to Apple. So isn't
00:28:11.820 --> 00:28:17.499
that a problem? So I think I looked at
this briefly. What I stumbled across was
00:28:17.499 --> 00:28:25.499
that it seems that iOS collects at most 25
crash logs per service. This is not
00:28:25.499 --> 00:28:30.659
designed to be like a security feature.
Right. So this makes sense. But what that
00:28:30.659 --> 00:28:37.610
means is that an attacker can use some
kind of, well, resource exhaustion bug to
00:28:37.610 --> 00:28:44.599
crash this daemon maybe 25 times first,
and then only start to exploit and then no
00:28:44.599 --> 00:28:52.129
trace of the exploit will be sent over.
Third question is whether this can be
00:28:52.129 --> 00:28:56.879
fixed by simply sending the delivery
receipt very early on. I think this is...
00:28:56.879 --> 00:29:01.909
this was my first suggestion to Apple to
just send this delivery receipt right at
00:29:01.909 --> 00:29:06.659
the start. Eventually I figured out it
doesn't really work because you can still
00:29:06.659 --> 00:29:12.459
make some kind of timing side channel,
because when when a demon crashes multiple
00:29:12.459 --> 00:29:17.789
times, it's subject to some penalty and it
will only restart like a few seconds or
00:29:17.789 --> 00:29:24.369
even minutes later. So from the timing of
getting a delivery receipt, you can then
00:29:24.369 --> 00:29:30.269
still basically get this oracle. Right. So
it doesn't really work by just sending it
00:29:30.269 --> 00:29:38.289
earlier. I'll go into some other ideas
that might work later. Okay. So at this
00:29:38.289 --> 00:29:47.929
point I'm starting the demo. The demo is
two parts. Let's see where it is. Right.
00:29:47.929 --> 00:29:54.059
So I have this iPhone here and you can
with QuickTime... the screen is mirrored
00:29:54.059 --> 00:30:05.799
to the projector. So this iPhone is it's a
10S, so it's from last year. It's on 12.4,
00:30:05.799 --> 00:30:10.879
which is the last vulnerable version. So
that's like half a year old at this point.
00:30:10.879 --> 00:30:23.199
And what else? So there is no existing
chats open. Okay. And let's see. So I hope
00:30:23.199 --> 00:30:27.889
the Wi-Fi works. What you can see here is
the way the exploit works that it's
00:30:27.889 --> 00:30:35.259
hooking with Frida into... Do we get
delivery receipt? Uh, do we? Yeah. Okay,
00:30:35.259 --> 00:30:41.179
cool. It works. So, yeah, it's popping up
these messages. The way the exploit works
00:30:41.179 --> 00:30:46.330
that it's hooking the messages app on
macOS with Frida and then it's sending
00:30:46.330 --> 00:30:54.289
these specific marker messages like
INJECT_ATI, and then the Frida hook
00:30:54.289 --> 00:30:58.259
replaces this message with like the
current payload. Right. And now it's
00:30:58.259 --> 00:31:09.039
testing these addresses. It's not too slow
I guess. Yeah. And it's popping up some
00:31:09.039 --> 00:31:13.110
nice messages. Okay. It already found.
Okay. So this is already the end of the
00:31:13.110 --> 00:31:18.619
first stage. So that was quite fast. It
found a valid address in this like first
00:31:18.619 --> 00:31:25.409
probing step and now it has 21,000
candidates for the shared cache base. I
00:31:25.409 --> 00:31:31.139
know it's doing this kind of binary search
thing to half that in every step. Okay.
00:31:31.139 --> 00:31:38.580
Now it only has 10,000 left and so it's
quite fast and quite efficient. Okay.
00:31:38.580 --> 00:31:49.869
While this runs, um, let's continue. So
this is where we are. We can now create
00:31:49.869 --> 00:31:55.831
fake objects. We have all the addresses we
need. It's like this 1170 is where we can
00:31:55.831 --> 00:32:02.379
place our stuff and then we will gain
control over the program counter. And from
00:32:02.379 --> 00:32:07.009
there it's standard stuff, right? It's
what you would do in all of these exploits
00:32:07.009 --> 00:32:11.820
you pivot maybe to the stack, you do
return oriented programing and then you
00:32:11.820 --> 00:32:17.070
can run your code and you've succeeded.
Now, at this point, there is another thing
00:32:17.070 --> 00:32:23.649
coming in. Pointer authentication is a new
security feature that Apple designed and
00:32:23.649 --> 00:32:32.279
implemented first in the 10S, so this
device from 2018. And the idea is that you
00:32:32.279 --> 00:32:37.190
can now - for this you need CPU support -
the idea that you can now store a
00:32:37.190 --> 00:32:43.269
cryptographic signature in the top bits of
a pointer. OK, so here on the very left
00:32:43.269 --> 00:32:47.879
side, you have a raw pointer. So the top
bits are zero because the way the address
00:32:47.879 --> 00:32:57.330
space works. Now there's a set of
instructions that sign a pointer and they
00:32:57.330 --> 00:33:02.299
will maybe take a context on it, but they
use some key that's not in memory - that's
00:33:02.299 --> 00:33:07.769
in a register, compute a signature of this
pointer and store the signature in the top
00:33:07.769 --> 00:33:12.319
bits. And that's what you see on the right
side. The green things. That's the
00:33:12.319 --> 00:33:20.499
signature. And now before using this
pointer, the code will now authenticate by
00:33:20.499 --> 00:33:24.999
running another instruction. And this
instruction, if the verification fails, it
00:33:24.999 --> 00:33:28.899
will basically clobber this pointer,
make it invalid. And then the following
00:33:28.899 --> 00:33:34.360
instructions will just crash. Right. So
here this is the function called the BL,
00:33:34.360 --> 00:33:38.879
branch and link instruction. This is doing
a function call to a function pointer. But
00:33:38.879 --> 00:33:43.259
first it's authenticating this pointer.
And if this authentication step fails,
00:33:43.259 --> 00:33:49.820
then the process will crash right there.
What this means for an attacker is that
00:33:49.820 --> 00:33:55.509
more or less, ROP is dead, because ROP
involves faking a bunch of function
00:33:55.509 --> 00:33:59.839
point... or like, well, code pointers
really, that point in the middle of
00:33:59.839 --> 00:34:05.210
existing code. So this is no longer
possible because an attacker cannot
00:34:05.210 --> 00:34:13.109
generate these signatures. So this is
where our exploit breaks, right, the red
00:34:13.109 --> 00:34:20.240
thing. Well, we have a fake objective C
class with our own function pointer. This
00:34:20.240 --> 00:34:25.550
does no longer work because we cannot
compute these signatures. So what do we
00:34:25.550 --> 00:34:32.510
do? One thing that's still possible and
it's even documented in the documentation
00:34:32.510 --> 00:34:37.870
is that this class pointer in the object -
what's also called the ISA pointer -
00:34:37.870 --> 00:34:44.970
it's not protected by PAC in any way.
Which means we can fake instances of
00:34:44.970 --> 00:34:51.510
legitimate existing classes. Right. So in
this case here we can have a fake object
00:34:51.510 --> 00:34:58.770
that points to a real class that has real,
legitimately signed method pointers. So
00:34:58.770 --> 00:35:05.640
this tool works. And with this, we can now
get existing methods called, out of place
00:35:05.640 --> 00:35:10.070
and kind of manipulate the control flow.
And these existing methods are basically
00:35:10.070 --> 00:35:20.120
now gadgets. So if you want to think about
it that way. So what can we do with this?
00:35:20.120 --> 00:35:24.860
One very interesting method we can get
called is dealloc, the destructor. So I
00:35:24.860 --> 00:35:30.110
think in quite a few, maybe most of the
Objective-C exploitation scenarios, you
00:35:30.110 --> 00:35:36.220
can probably get a dealloc method called.
Now what you do is you just enumerate all
00:35:36.220 --> 00:35:41.390
the destructors in the shared cache.
There's tons of them, I think 50,000, and
00:35:41.390 --> 00:35:46.610
you can get any of those called. And then
one of them or a few of them are really
00:35:46.610 --> 00:35:52.500
interesting because they call this invoke
method, which is part of the NSInvocation
00:35:52.500 --> 00:35:59.200
object, or class. And an NCInvocation is
basically a bound function. So it has a
00:35:59.200 --> 00:36:05.240
target object, the method to be called and
all the arguments. And as soon as you call
00:36:05.240 --> 00:36:09.660
invoke on this NCInvocation, it does this
method call with fully control arguments.
00:36:09.660 --> 00:36:15.310
Right. So what that means is with this
destructor, we can now make a fake object
00:36:15.310 --> 00:36:20.840
with a fake NSInvocation that has any
method call we would like to perform, and
00:36:20.840 --> 00:36:28.060
then it's going to do that because it's
running this invoke here. Again, you see
00:36:28.060 --> 00:36:33.510
this shield here, which I put in place for
things that Apple has hardened since we
00:36:33.510 --> 00:36:39.210
sent them the exploit. So what they did so
far is they hardened NSInvocation and it's
00:36:39.210 --> 00:36:46.730
now no longer easily possible to abuse it
in this way. But yeah. So for us, we can
00:36:46.730 --> 00:36:52.910
now run arbitrary Objective-C methods with
controlled arguments. What about
00:36:52.910 --> 00:36:58.860
sandboxing? If you do some more reverse
engineering and figure out what services
00:36:58.860 --> 00:37:03.970
play into iMessage, this is what you end
up with. On the right side. So you have a
00:37:03.970 --> 00:37:08.750
number of services. Most of them are
sandboxed. If it has the red border, it
00:37:08.750 --> 00:37:15.320
means there's a sandbox. Interestingly,
Springboard also does some NSUnarchiver
00:37:15.320 --> 00:37:22.510
stuff. So it's decoding the BP key. So it
could also trigger our vulnerability and
00:37:22.510 --> 00:37:26.520
Springboard is not sandboxed. So it's the
main UI process. It's basically what's
00:37:26.520 --> 00:37:34.900
handling showing the the welcome screen.
And so on. And so what that means is,
00:37:34.900 --> 00:37:38.690
well, we can just target Springboard and
then we get code execution outside of the
00:37:38.690 --> 00:37:43.740
sandbox so we don't actually need to worry
too much about the sandbox. As of iOS 13,
00:37:43.740 --> 00:37:51.310
this is fixed and this key is now decoded
in the sandbox. Cool, so we can execute
00:37:51.310 --> 00:37:56.370
Objective-C methods outside of the
sandbox. We can with that access user
00:37:56.370 --> 00:38:00.980
data, activate camera, microphone, etc.
This is all possible through Objective-C
00:38:00.980 --> 00:38:06.250
quite easily. But of course we don't care
about that. What we want is a calculator
00:38:06.250 --> 00:38:10.960
and this is also quite easy, with one
Objective-C call - UIApplication
00:38:10.960 --> 00:38:17.180
launchApplication blah blah blah. And so
let's see if this works. Go back to the
00:38:17.180 --> 00:38:26.260
demo. So where are we at? So the, uh, the
ASLR bypass ran through. You can nicely
00:38:26.260 --> 00:38:30.680
see that it roughly halved the candidates
in every round, or with every message
00:38:30.680 --> 00:38:35.900
it had to send. It ended up with just
one message. Yeah, well with just one
00:38:35.900 --> 00:38:40.920
candidate at the end. And that is the
shared cache base in this case
00:38:40.920 --> 00:38:50.900
0x18a608000. Now it's preparing the heap
spray. This is all kind of hacked
00:38:50.900 --> 00:38:59.160
together. I think if you wanted to do this
properly, for one, you can send the whole
00:38:59.160 --> 00:39:06.680
heap spray in one message. I'm just lazy.
It's also probably way too big. Another
00:39:06.680 --> 00:39:11.760
thing is, I think you would probably not
target springboard in reality just because
00:39:11.760 --> 00:39:15.450
spring board is very sensitive. So if you
crash, did you get this re-spring and the
00:39:15.450 --> 00:39:20.360
UI restarts. So I think in reality you
would probably target IM agent and then
00:39:20.360 --> 00:39:26.250
chain the sandbox escape. Because while
this bug would also get you out of the
00:39:26.250 --> 00:39:32.390
sandbox. So looks should be doable. Okay.
So I think the last message arrived. It's
00:39:32.390 --> 00:39:35.850
freezing here for a couple of seconds. I
don't actually know why I never bothered,
00:39:35.850 --> 00:39:44.760
but it does work.
Applause
00:39:44.760 --> 00:39:52.460
Thank you. Yeah. So that was a demo. It's
it's kind of naturally reliable, this
00:39:52.460 --> 00:39:59.270
exploit, because there is not much of heap
manipulation involved except this one
00:39:59.270 --> 00:40:09.490
heaps spray, which is controllable. Okay.
Um, so what's left? I think one more thing
00:40:09.490 --> 00:40:14.740
you can do is you can attack the kernel if
you want that. You have to deal with two
00:40:14.740 --> 00:40:19.970
problems here. One is code signing. You
cannot execute unsigned code on iOS. And
00:40:19.970 --> 00:40:24.960
then the standard workaround for that is
you abuse JIT pages in safari. But we are
00:40:24.960 --> 00:40:29.770
not in safari or we are not in web
content, so we don't have JIT pages. What
00:40:29.770 --> 00:40:36.130
I did here is I basically pivoted into
JavaScript core, which is the the JS
00:40:36.130 --> 00:40:42.300
library. You can use it from from any app
also. And then I'm just bridging syscalls
00:40:42.300 --> 00:40:48.120
into JavaScript and then implementing the
kernel exploit in JavaScript. This does
00:40:48.120 --> 00:40:53.170
not require any more vulnerabilities. So
you do not need a JavaScript core bug to
00:40:53.170 --> 00:40:58.870
do this. And the idea is very similar to
pwn.js. Maybe some of you know about that.
00:40:58.870 --> 00:41:03.300
It's a library. I think initially
developed for Edge because they did
00:41:03.300 --> 00:41:10.580
something similar was like JIT page
hardnings. So what I decided to do is take
00:41:10.580 --> 00:41:18.750
SockPuppet from Ned or CVE-2019-8605,
which works on this version, it works on
00:41:18.750 --> 00:41:26.870
12.4. This is the trigger for it. And I
only ported the trigger. I didn't bother
00:41:26.870 --> 00:41:31.100
re -implementing the entire exploit. So
yeah, this is the trigger. It will cause a
00:41:31.100 --> 00:41:36.560
kernel panic. It's quite short. Which is
nice. So if you want to run this from
00:41:36.560 --> 00:41:41.750
JavaScript, really, there's only three
things you care about, right? So the first
00:41:41.750 --> 00:41:48.020
one is you need the syscalls. So
highlighted here, there is like four or so
00:41:48.020 --> 00:41:52.360
different syscalls here. Not a lot. And
you just have to be able to call them from
00:41:52.360 --> 00:41:58.360
JavaScript. The other thing is you need
constants, right? So I have AF_INET6,
00:41:58.360 --> 00:42:01.730
SOCK_STREAM. These are all integer
constants. So this is really easy, right?
00:42:01.730 --> 00:42:07.020
You just need to look up what these values
end up being. And then the last thing is
00:42:07.020 --> 00:42:13.520
you need some data structures. So in this
case, I need this so_np_extension thing.
00:42:13.520 --> 00:42:21.500
It needs some integer value to pass
pointers to and so on. Yeah. And then this
00:42:21.500 --> 00:42:28.370
is kind of the the magic that happens. You
take sock_puppet.c extract the syscalls
00:42:28.370 --> 00:42:34.161
etc. There is one Objective C message you
can call which is very convenient, which
00:42:34.161 --> 00:42:42.030
gives you a dlsym. What this lets you do
is, it lets you get native C function
00:42:42.030 --> 00:42:46.680
pointers that are signed, right. Because
so far we can only call Objective C
00:42:46.680 --> 00:42:51.270
methods, but we need to be able to call
syscalls or at least the C wrapper
00:42:51.270 --> 00:42:59.720
functions. So with this dlsym method thing
we can get signed pointers to C functions.
00:42:59.720 --> 00:43:03.190
Then we need to be able to pivot into
JavaScript code, which is also really easy
00:43:03.190 --> 00:43:08.830
with one method call, the JSContext
evaluateScript. We need to mess around
00:43:08.830 --> 00:43:12.840
with memory a bit like corrupt some
objects from outside, corrupt some area
00:43:12.840 --> 00:43:18.810
buffers in javascript, get read, write.
Kind of standard browser exploitation
00:43:18.810 --> 00:43:23.220
tricks I guess. But yeah. So if you do
this what you end up with is
00:43:23.220 --> 00:43:30.920
sock_puppet.js. It looks very similar. You
can see a bit of my javascript API that
00:43:30.920 --> 00:43:36.920
lets you allocate memory buffers. I read
and write memory, have some integer
00:43:36.920 --> 00:43:42.050
constants and yeah, apart from that, it
doesn't really look much different from
00:43:42.050 --> 00:43:49.370
the initial trigger. And so this can now
be served over, well, staged onto the
00:43:49.370 --> 00:43:55.350
iMessage exploit building on top of this
object a C method called primitive. And I
00:43:55.350 --> 00:43:59.740
guess at least in theory I didn't fully
implement it. This should be able to just
00:43:59.740 --> 00:44:08.390
run a kernel exploit and fully compromise
the device without any interaction in
00:44:08.390 --> 00:44:13.990
probably less than 10 minutes. Okay, so
this was the first part. How does how does
00:44:13.990 --> 00:44:19.260
this exploit work. What I have now is a
number of suggestions how to make this
00:44:19.260 --> 00:44:25.910
harder and how to improve things. So one
of the first things that is really
00:44:25.910 --> 00:44:30.420
critical for this exploit is the ASLR
bypass, which relies on a couple of
00:44:30.420 --> 00:44:36.620
things. And I think a lot of this ALSR
bypass also works on other platforms. So
00:44:36.620 --> 00:44:41.960
Android has a very similar problem with
like mappings being at the same address
00:44:41.960 --> 00:44:47.350
across processes. And other messengers
have these like receipts and so on. So I
00:44:47.350 --> 00:44:51.640
think a lot of this applies not just to
Apple but to Android and to other
00:44:51.640 --> 00:44:57.490
messengers. But okay. What is the first
point? So weak ALSR, this is basically the
00:44:57.490 --> 00:45:03.530
heap spraying, which is just too easy.
This shouldn't be so easy. In terms of
00:45:03.530 --> 00:45:07.890
theoretical ASLR, you can see it maybe
sketched here on the right. In theory,
00:45:07.890 --> 00:45:12.660
ASLR could be much stronger, much more
randomized. In reality, it's just like the
00:45:12.660 --> 00:45:18.800
small red bar. So it really it should just
have much more entropy to make heap
00:45:18.800 --> 00:45:30.180
springing not viable anymore. The next
problem with ASLR is per-boot stuff. At
00:45:30.180 --> 00:45:33.240
the bottom you can see it, right? So you
have three different processes, the shared
00:45:33.240 --> 00:45:37.330
cache is always at the same address,
similar problems on other platforms, I
00:45:37.330 --> 00:45:44.270
mentioned that. This is probably hard to
fix because by this point a lot of, quite
00:45:44.270 --> 00:45:50.040
a lot relies on this. And it would be a
big performance hit to change this. But
00:45:50.040 --> 00:45:55.960
maybe some clever engineers can figure out
how to do it better. The third part here
00:45:55.960 --> 00:46:01.280
is the delivery receipts, which,
interestingly, they can give this side
00:46:01.280 --> 00:46:06.370
channel, this one bit information side
channel and this can be enough to break
00:46:06.370 --> 00:46:11.250
ASLR. And as I've mentioned before, I
think a lot of other messengers have this
00:46:11.250 --> 00:46:21.270
same problem. What might work is to
either, well, remove these receipts. Sure.
00:46:21.270 --> 00:46:25.270
Or maybe send them from a different
process so you can't do this timing thing
00:46:25.270 --> 00:46:29.520
or even from the server. I think if you
send them, if the server already sends the
00:46:29.520 --> 00:46:37.760
delivery receipt, it's a bit of cheating.
But at least this attack doesn't work.
00:46:37.760 --> 00:46:43.020
Sandboxing, another thing, it's probably
obvious, right? So the everything that's
00:46:43.020 --> 00:46:50.100
on zero click attack surface should be
sandboxed as much as possible. Of course,
00:46:50.100 --> 00:46:55.770
to, you know, to require the attacker to
do another full exploit after getting code
00:46:55.770 --> 00:47:02.270
execution. But Sandboxing can also
complicate information leaks. So not only
00:47:02.270 --> 00:47:07.660
had this other iMessage bug
CVE-2019-8646, there's a blog post about
00:47:07.660 --> 00:47:15.160
this one. It basically lets you. She was
able to send to cause a Springboard to
00:47:15.160 --> 00:47:20.480
send HTTP requests to some server and
those would contain pictures, data,
00:47:20.480 --> 00:47:27.320
whatever. If Springboard would've been
sandbox to not allow network activities,
00:47:27.320 --> 00:47:31.190
this would have been much harder. So
sandboxing is not necessarily just about
00:47:31.190 --> 00:47:36.550
this second breakout. What I do want to
say about sandboxing, ithat it shouldn't
00:47:36.550 --> 00:47:41.510
be relied on. So I think that this remote
attack surface is pretty hard. And it's
00:47:41.510 --> 00:47:46.480
not unlikely that it's actually harder
than the sandboxing attack surface. And
00:47:46.480 --> 00:47:50.770
also on top of that, this bug, the
NSKeyedUnarchiver bug, it would also get
00:47:50.770 --> 00:47:56.460
you out of the sandbox because the same
API is used locally for IPC. So there's
00:47:56.460 --> 00:48:03.160
that. Yeah. This would be nice if the zero
click attack surface code would be open
00:48:03.160 --> 00:48:08.530
source. Would have been nice for us. It
would have been easier to audit. Maybe
00:48:08.530 --> 00:48:17.160
someday. Another feature that I would like
to see or another theme is reduced zero
00:48:17.160 --> 00:48:21.780
attack surface. Make it one click at least
one click attack surface. Right. So before
00:48:21.780 --> 00:48:28.070
and here you could see that an unknown
sender can send any messages. It would be
00:48:28.070 --> 00:48:32.300
nice if there would be some pop up that's
like, well, do you actually want to accept
00:48:32.300 --> 00:48:37.390
messages? Threema lets you block unknown
senders. I think that's a cool feature. So
00:48:37.390 --> 00:48:44.791
yeah, there's more work to be done here.
Also, this restarting service problem, I
00:48:44.791 --> 00:48:51.740
think it could get bigger even. So, here
we have pretty much unlimited tries for
00:48:51.740 --> 00:48:56.840
the ASLR bypass. It's probably going to
become even more relevant with memory
00:48:56.840 --> 00:49:03.680
tagging, which we can also be defeated if
you have many tries. So yeah, I guess if
00:49:03.680 --> 00:49:08.330
there's some process or some critical
demon crashes ten times, maybe not restart
00:49:08.330 --> 00:49:15.280
it. I don't know. It's gonna need some
more thinking, right? You don't want to
00:49:15.280 --> 00:49:20.660
denial-of-service the user by just not
restarting this demon that crashed for
00:49:20.660 --> 00:49:26.080
some unrelated reason. But yeah, this
would be a very good idea to have some
00:49:26.080 --> 00:49:33.210
kind of limit here. Okay. Conclusion. So
yeah, zero click exploit, they are thing.
00:49:33.210 --> 00:49:39.310
They do exist. It is possible to exploit
single memory corruption bugs on this
00:49:39.310 --> 00:49:45.490
surface with, you know, without separate
info leaks. Despite all the mitigations we
00:49:45.490 --> 00:49:51.240
have. However, I do think by turning the
right knobs, this could be made much
00:49:51.240 --> 00:49:57.180
harder. So I gave some suggestions here.
And yeah, we need more atack surface
00:49:57.180 --> 00:50:01.650
reduction, especially on the zero click
surface. But I think there is progress
00:50:01.650 --> 00:50:06.500
being made. And with that thanks for your
attention. And I think we have time for
00:50:06.500 --> 00:50:08.910
questions. Thank you.
00:50:08.910 --> 00:50:16.270
applause
00:50:16.270 --> 00:50:21.080
Herald: We do have time for questions. And
if you're in the room, you should line up
00:50:21.080 --> 00:50:25.790
at the microphones and then we might also
have questions from the Internet. One
00:50:25.790 --> 00:50:32.960
quick reminder is that all fun things that
what they work with explicit consent that
00:50:32.960 --> 00:50:38.890
includes photos. So the photo policy of
the CCC is that if you take a photo, you
00:50:38.890 --> 00:50:43.490
need to have explicit consent by the
people in the frame. So remember, don't do
00:50:43.490 --> 00:50:47.880
any long shots into the crowd because you
want to have the consent of everybody
00:50:47.880 --> 00:50:53.300
there. Good. We have the first question
from the Internet.
00:50:53.300 --> 00:50:57.110
Question: The Internet wants to know. Did
Apple give you some kind of a reward? And
00:50:57.110 --> 00:51:01.750
was it in your iPhone?
Answer: No, we did not get any kind of
00:51:01.750 --> 00:51:11.520
reward. But we also didn't ask for it. No,
I didn't get a new iPhone, but I'm still
00:51:11.520 --> 00:51:20.590
using mine. Which is it? Yeah. I mean,
this is a Xs, right? Current hardware
00:51:20.590 --> 00:51:28.920
models can be defeated with this, if that
is the question.
00:51:28.920 --> 00:51:31.870
Herald: Good. We have a question for
microphone number 3.
00:51:31.870 --> 00:51:41.480
Q: Hello. Uh, just a question. I did not
truly understand how the fix with the
00:51:41.480 --> 00:51:47.620
server or having another process, uh,
sending that there every message will fix
00:51:47.620 --> 00:51:53.800
the problem because if it does work, if
you are in the right addresses, the thing
00:51:53.800 --> 00:52:02.100
just will work. Make the server or the
process, send the delivery message and if
00:52:02.100 --> 00:52:10.820
it crashes, it doesn't do anything so...
A: So the idea would be in this case, I'm
00:52:10.820 --> 00:52:15.020
like sending this one method that would
crash and then either I get a delivery
00:52:15.020 --> 00:52:20.030
received or I don't. If the server already
sends the delivery receipt before it
00:52:20.030 --> 00:52:26.260
actually gives the message to the client
or to the receiver, then I would always
00:52:26.260 --> 00:52:30.310
see a delivery receipt and I wouldn't be
able to figure out if my message caused
00:52:30.310 --> 00:52:36.150
the crash or not. So that's the idea
behind maybe sending it on the server
00:52:36.150 --> 00:52:38.890
side, if that makes sense.
Follow-up question: Yeah. But in this
00:52:38.890 --> 00:52:47.180
case, if legit people send a message and
it doesn't reach the people because...
00:52:47.180 --> 00:52:52.660
A: Yeah. Yeah. It's a hack. Right. So it's
not perfect. I mean the server could only
00:52:52.660 --> 00:52:58.960
send to find this delivery receipt once it
like send it out over TCP and maybe got a
00:52:58.960 --> 00:53:05.900
TCP ACK or whatever happens in the kernel.
But it's a hack in any case. Yeah. Like
00:53:05.900 --> 00:53:08.130
it's a tradeoff.
Herald: We have a question for microphone
00:53:08.130 --> 00:53:14.210
number two.
Q: Hello. Okay. Thanks for the talk. Two
00:53:14.210 --> 00:53:20.530
questions. First: Is OS X also a
potential candidate for this bug. And
00:53:20.530 --> 00:53:26.270
second: Can you distinguish multiple
devices with your address based
00:53:26.270 --> 00:53:31.440
randomization detection?
A: Mm hmm. So yes: OS X or MacOS is
00:53:31.440 --> 00:53:36.940
affected just the same. I think this
specific exploit wouldn't directly work
00:53:36.940 --> 00:53:40.290
because address space looks a bit
different, but I think you could make it
00:53:40.290 --> 00:53:45.280
work and it's affected. In terms of
multiple devices, so I haven't played
00:53:45.280 --> 00:53:51.270
around with that. I could imagine that it
is possible to somehow figure out that
00:53:51.270 --> 00:53:56.920
there are multiple devices or that you
know which device just crashed. But I
00:53:56.920 --> 00:54:01.010
haven't investigated. That's the answer.
Follow-up: Thanks.
00:54:01.010 --> 00:54:03.770
Herald: We still have time for more
questions. There was a question from
00:54:03.770 --> 00:54:08.490
microphone number 1.
Q: Hi. Thanks for the talk. Quick
00:54:08.490 --> 00:54:15.520
question. You said that exploitation could
be made without having any notification.
00:54:15.520 --> 00:54:21.040
How would that be made?
A: Yeah, I briefly looked into how it
00:54:21.040 --> 00:54:29.120
could work. Well. So for one, you can take
out parts of the message so that it fails
00:54:29.120 --> 00:54:34.480
parsing later on in the processing and
then it will just be like thrown away
00:54:34.480 --> 00:54:39.490
because it says, well, this is garbage.
The other thing is, of course, once you
00:54:39.490 --> 00:54:45.160
get with the like very last message where
you get code execution, you cannot prevent
00:54:45.160 --> 00:54:48.620
it from showing a message like a
notification, because that happens
00:54:48.620 --> 00:54:52.720
afterwards.
Follow-up Q: But until you get the code
00:54:52.720 --> 00:54:56.690
execution, you can't remove it. So you
see the first message?
00:54:56.690 --> 00:55:01.140
A: So but you can do the other. The other
thing, like make it make the message look
00:55:01.140 --> 00:55:05.690
bad - bad enough that like later parsing
stages will throw them away.
00:55:05.690 --> 00:55:08.510
Follow-up: Thanks.
Herald: Good. We have a couple of more
00:55:08.510 --> 00:55:11.130
questions. Remember, if you don't feel
comfortable lining up behind the
00:55:11.130 --> 00:55:15.180
microphones, you can ask through the
signal angel through the Internet.
00:55:15.180 --> 00:55:19.720
Microphone number 4, please.
Q: Yes. Hi. Hi Samuel. Um, I was curious
00:55:19.720 --> 00:55:24.340
you have some suggestions about reducing
the attack surface. Are there any
00:55:24.340 --> 00:55:28.400
suggestions that you'd make to save, like
Apple or Google? You know, in terms of
00:55:28.400 --> 00:55:31.830
what they can see. You mentioned logging a
little bit earlier.
00:55:31.830 --> 00:55:39.590
A: Yeah. So I sent pretty much this list
with the exploit I sent to Apple. And I
00:55:39.590 --> 00:55:47.280
think the blog post will have a bit more.
But yeah, I told them the same thing.
00:55:47.280 --> 00:55:50.190
Yeah, if that's your question, did I get
it right?
00:55:50.190 --> 00:55:53.770
Follow-up Q: Yes. I mean, maybe I
misunderstood a little bit, but I suppose
00:55:53.770 --> 00:55:57.400
that some of these reductions in the attack
surface seem to be in terms of like what's
00:55:57.400 --> 00:56:01.710
happening on the device. Yeah. Whereas I'm
wondering in terms of monitoring. So being
00:56:01.710 --> 00:56:03.560
able to catch something like this in
progress.
00:56:03.560 --> 00:56:07.600
A: Right. Right. So this is gonna be
really hard because of end-to-end
00:56:07.600 --> 00:56:13.210
encryption. So the server just sees like
encrypted garbage and has no way of
00:56:13.210 --> 00:56:18.570
knowing is this an image? Is that the
text? This is an exploit? So on the
00:56:18.570 --> 00:56:25.619
server, I don't think you can do much
there. I think it's gonna have to be on
00:56:25.619 --> 00:56:29.050
the device.
Herald: We have a question from the
00:56:29.050 --> 00:56:33.500
Internet.
Q: How do you approach a attack surface
00:56:33.500 --> 00:56:41.430
mapping?
A: Um, well, reverse engineering, playing
00:56:41.430 --> 00:56:47.760
around, looking at this message format. In
this case, what was somewhat obvious what
00:56:47.760 --> 00:56:51.590
an attack surface was. Right. So figure
out which key is off this message are
00:56:51.590 --> 00:56:57.770
being processed in some way. Make a note.
Decide which one looks most complex. Go
00:56:57.770 --> 00:57:03.090
for that first. That's what we did.
Herald: We have a question from microphone
00:57:03.090 --> 00:57:07.020
number 2, please.
Q: Hi. How long did you and your colleague
00:57:07.020 --> 00:57:14.590
research to get the export running?
A: So the the vulnerability finding thing
00:57:14.590 --> 00:57:21.510
was not only I think we spend maybe three
months finding the exploit. So I had a
00:57:21.510 --> 00:57:26.710
rough idea how I wanted to how how I would
approach this exploit. So I think at the
00:57:26.710 --> 00:57:33.990
end it took me maybe a week to finish it.
But I had thought about doing that for
00:57:33.990 --> 00:57:38.790
like, while a looking while looking for
vulnerabilities and those two to three
00:57:38.790 --> 00:57:42.190
months.
Herad: We have another question from
00:57:42.190 --> 00:57:48.550
microphone number three.
Q: Um, is there the, uh, threat that the
00:57:48.550 --> 00:57:54.500
attacked iPhone would itself turn into a
tack up by the exploit?
00:57:54.500 --> 00:57:59.270
A: Sure. Yeah, you can do that. I mean,
you have full control, right? So you have
00:57:59.270 --> 00:58:05.000
access to the contacts list and you can
send out iMessages. The question is if
00:58:05.000 --> 00:58:09.260
it's necessary. Right. I mean, you can
also send messages from you don't really
00:58:09.260 --> 00:58:15.860
need them, the iPhone to send the
messages. But I think in theory: Yes,
00:58:15.860 --> 00:58:19.000
that's possible.
Herald: Do we have more questions from the
00:58:19.000 --> 00:58:25.440
Internet?
Q: Does the phone stay compromised after
00:58:25.440 --> 00:58:28.740
restart?
A: So there is no persistence exploit
00:58:28.740 --> 00:58:34.410
here. No. You will need another exploit a
littlelailo did a talk. I think just an
00:58:34.410 --> 00:58:40.130
hour ago about persistence. So you would
need to change this with what, for
00:58:40.130 --> 00:58:45.040
example, to exploit that he showed.
Herald: And if you have questions in the
00:58:45.040 --> 00:58:47.800
room, please line up behind the
microphones. Do we have more questions
00:58:47.800 --> 00:58:55.150
from the Internet.
Q: Yes. So you've achieved the most novel
00:58:55.150 --> 00:59:02.330
buck ever found to be fine in iOS. What's
the next big thing you'll be looking at?
00:59:02.330 --> 00:59:06.480
A: Good question. I don't really know
myself, but I'm going to stay probably
00:59:06.480 --> 00:59:12.520
around for zero click attack surface reduction
for a bit more.
00:59:12.520 --> 00:59:16.100
Herald: Looks like we don't have any brave
people asking questions in the room. Does
00:59:16.100 --> 00:59:21.080
the Internet have more courage?
Q: How long does discovery and
00:59:21.080 --> 00:59:26.750
exploitation and development take and how
much does the team work to improve the
00:59:26.750 --> 00:59:33.710
process and development time?
A: Okay, so how much how long does this
00:59:33.710 --> 00:59:39.530
exploitation process work? That's the
first question. Yes. Yeah. I mean, this is
00:59:39.530 --> 00:59:45.020
generally a hard thing to answer. Right.
There's like years of hacking around and
00:59:45.020 --> 00:59:50.670
learning how to do this stuff, etc. that
you have to take into account. But as I
00:59:50.670 --> 00:59:54.500
said, I had a rough idea how this exploit
would look like. So then really
00:59:54.500 --> 01:00:00.600
implementing it was like one or two weeks.
The initial part of reverse engineering
01:00:00.600 --> 01:00:04.811
iMessage reverse engineering this
NSUnarchiver thing. I kind of I think this
01:00:04.811 --> 01:00:10.650
took forever. This took many months and it
was also very necessary for exploit
01:00:10.650 --> 01:00:17.030
writing. Right. So a lot of the expert
primitives I use, they also abuse the
01:00:17.030 --> 01:00:21.760
NSKeyUnarchiver thing.
Herald: We have time for perhaps two quick
01:00:21.760 --> 01:00:27.480
questions. Mike number 4, please.
Q: Super. Uh, I'm not super familiar with
01:00:27.480 --> 01:00:32.980
iOS virtual memory address space but you
should two heap regions when you showed the
01:00:32.980 --> 01:00:37.360
picture of it. And I'm wondering why are
there two heap regions?
01:00:37.360 --> 01:00:42.510
A: OK, because there is only a minor
detail, but I think there is one region
01:00:42.510 --> 01:00:48.700
initially like below the shared cache and
one state is full. It just makes another
01:00:48.700 --> 01:00:56.150
one above it. So it's really just like if
the one gets used or gets gets used up, it
01:00:56.150 --> 01:00:59.340
makes another one. And that's going to be
like above the shared cache. I think
01:00:59.340 --> 01:01:03.300
that's the picture you're referring to.
Follow-up: Yeah, thank you.
01:01:03.300 --> 01:01:06.910
Heralds: And unfortunately, we are out of
time. So the person that might have some
01:01:06.910 --> 01:01:10.690
number one, please come up to the stage
afterwards and perhaps you can grab a talk.
01:01:10.690 --> 01:01:17.287
So please give a warm. I can't say this
exactly. applause Thanks.
01:01:17.287 --> 01:01:22.797
applause
01:01:22.797 --> 01:01:26.942
Postroll music
01:01:26.942 --> 01:01:50.000
Subtitles created by c3subtitles.de
in the year 2020. Join, and help us!