34c3 preroll music
Herald Angel: And now I want to introduce
our first speaker and the topic he's
talking about. iOS kernel exploitation
archaeology. A kernel exploit from late
2013 early 2014 will be digged out and
analyzed proper archaeology all the
digging... digging and analysis is done by
argp here to my left on the stage and give
him a big round of applause. And the stage
is yours, thanks.
Applause
argp: Thanks for the introduction. First
of all, thank you all for being here. As
the person that did the introduction told
you this is going to be an archeology talk
so I apologize in advance if it's not that
interesting for you. So we'll talk about a
bit older stuff rather than new things.
Okay so a bit a few things about myself.
Actually, I think from all these things,
the most important are the the Phrack
papers, right? So, yeah. Let's ignore all
the other stuff okay? So, what I'm going
to talk about. I'm going to talk about the
evasi0n7 kernel exploit. Now evasi0n7 was
a jailbreak it was released by the evad3rs
on the 22nd of December 2013. It supported
iOS7 to iOS7.1 beta 3. That's not the
7.1 stable release, right? So that's a
beta. And this supported all devices at
that time including the iPhone 5s which
was the first 64-bit device except the
Apple TV. So, I decided to reverse
engineer the kernel exploit of the
jailbreak focused just on that. Because I
was really interested, not so much in the
bug itself, which was as we will see not
very complicated. But I was really
interested to understand the exploitation
techniques that the evad3rs used. So, I
started reversing it, and I understanding
it, and at some point I just said I'm just
gonna do a reimplementation of the kernel
exploit. So, this talk is basically my
notes on this whole process. And, of
course, it's not a jailbreak walkthrough,
right? And I'm going to specifically focus
on the various problems I encountered
during this task and how I overcame them.
And hopefully it's going to give you some
helpful takeways for if you do iOS kernel
research nowadays. Okay, so, the general
outline is I'm going to say a few things
about the version 7 to setup the stage.
And then I'm going to explain the kernel
bug itself. And, then I'm going to talk in
length about my debugging setup. And, I
think that's a very important step that
usually phone or embedded talks
exploitation talks don't analyze that
much, and I think it's a really important
part. Because usually having a working the
debugging set up is, basically, maybe half
the job of doing a reliable exploit. Then
I'm going to do to talk about my
reimplementation of the exploit, and
hopefully, at the end, we're gonna have
some things to take away or maybe not. We
will see. Okay so it was the evasi0n7
jailbreak was released about 4 years ago.
And that's the archaeology in the title.
That's ancient history right? And if you
were following the jailbreak community,
you might remember this huge drama around
this jailbreak, initially, with geohot and
if he was planning or not to release it
before the evad3rs. And who he was
planning to sell it to, and some leaked
discussion that he had with some of that
he was offering money to buy. And geohot,
his jailbreak supposedly using
some of the bugs the evad3rs were using so
this is a huge drama. And then after the
evasi0n7 jailbreak released, like maybe a
few hours ago, people realize that if your
phone had a Chinese locale then the
jailbreak was installing a piracy app. So,
that was basically a third-party app that
was taking you to an
app store not operated
by Apple but by TaiG that had some pirated
versions of the real applications on the
App Store. And, of course, that also
create like a huge drama, this practice.
Okay, so a lot of things were said about
the jailbreak at that time and about the
TaiG pirate App Store. But what I really
set apart was this tweet. And the really
important thing that I like about this
tweet is, that it doesn't really make
sense. So he says that we have to decide
to remotely disable the default
installation of TaiG in China for further
investigations of the piracy. So that
whole thing doesn't make sense. So you
mean, you didn't know what was happening?
You didn't bundle it with a jailbreak? Are
you going to disable it for new
installations? And then, what does
remotely then mean exactly? So what about
the people that already had the apps, the
piracy app. How are you going to disable
that? Is that what remotely refers to? So
that's an excellent tweet I think. Okay,
so some point after the evasi0n7 jailbreak
was released geohot did a writeup on the
userland part of it. So, he analyzed how
the userland part worked and he stopped at
the point of gaining root and basically,
he mentioned in his writeup that the
evasi0n7 untethered binary, which
basically what was good doing the kernel
exploit, was obfuscated. And as we will
see this was indeed the case and as far as
I know that's the first jailbreak that
used the deliberate obfuscation. I don't
know the reason, I assume it's
partly to hide the the piracy
app store that was bundled with it and
maybe partly to hide the bug, the kernel
bug, but I'm not sure about the reason.
Now p0sixninja who found as far as I know
the bug, the kernel bug did a writeup on
the kernel bug, it's on the iPhone wiki,
and he basically describes the bug and he
stops at the point where he gets a crash
log from gdb. So he doesn't say anything
about how to exploit it. Okay, so after
all these things happened then I decided
to reverse engineer the untethered binary
and understand the exploitation techniques
and I was really interested to reverse
engineer the obfuscation that the evad3rs
were using, it seemed like an interesting
challenge, and... but as I also mentioned
earlier I was really interested to
understand the exploitation techniques
that they were using that was more
important for me at that time. And,
okay, so the jailbreak was released
December 2013 and I started doing that
around February 2014, and I did that while
having an actual day job, right, so I was
spending at most two days per week on
that. So what was my setup? I had an
iPhone 4, and if you know about iPhone 4s
they have a boot ROM bug called limera1n
which basically allows you to load
arbitrary kernels, unsigned kernels, on
the device and run them and that basically
means that you can very easily
set up kernel debugging. So initially I
had an iPhone 4 device with iOS 7.0.6. I
want to remind you that iPhone 4 is ARM32,
right. I also had an iPhone 5s with the
same version of iOS and I had that in
order to verify all my findings and all my
tests on - to redo my tests on an ARM64
device and as I told you - the iPhone 5s
at that time was the only ARM64 device.
Actually, I think on the market, I don't
think there was another consumer device
with ARM64 at that time. So that's the
exact version of version 7 I was analyzing
and of course IDA, gdb, lldb. Now the lols
in this slide they don't actually refer to
something funny they actually mean
something very painful and that caused a
lot of like sleepless nights, but I'll get
onto that. Okay, a few things about the
obfuscation. So, not all the functions of
the entire binary were obfuscated, but
some of the important ones were, and those
were the ones that were triggering the bug
and they were actually doing heap
manipulation and all the other important
things. Now I have been told, I haven't
checked that, but I have been told that
later versions remove the obfuscation but
I'm not sure about that, I haven't
verified it and I already had my
implementation done at that point so I
wasn't that interested to look at that. So
as I mentioned the kernel bug that the
evasi0n7 untethered binary was based on
was found by p0sixninja, and basically as
far as he says on that iPhone wiki page he
used that six line bash script fuzzer to
find it. So as you can see he basically
creates device nodes and, with
controlled arguments here like minor and
major numbers. Now in order to get to the
point to create device nodes you basically
need to be outside of the application
sandbox that exists on iOS and you also
need root privileges and that's what I
refer to as the userland part of the
evasi0n7 binary and I'm not going to cover
that at all. So I'm gonna start my
analysis from the point on that we have
escaped the sandbox, we have obtained
root and now we go to exploit the kernel
bug. Now that's code from that version of
the XNU kernel that had the bug. Now this
ptsd_open function is called everytime
userland code opens a /dev/ptmx device and
then this ptmx_get_ioctl function is
called. Now the important thing here
is that dev here is completely
user controlled and then
it's passed to this ptmx_get_ioctl
function with no checks at all, right, and
then this ptmx_get_ioctl function uses
this to index an array without any checks.
So basically the bug's an invalid indexing
bug, right, so since we can control that
you can put here whatever. I have here the
ptmx_ioctl struct that, okay, this array
here is, so this state struct here is
global to the kernel and this
pis_ioctl_list array here is on the kernel
heap and it is an array of ptmx_ioctl
structs and that's the PTMX ioctl struct
and the important thing here is, that
I'm going to refer to again and again
during the talk, is that it has a pointer
to a tty struct as the first element of
the structure. Okay, so we control the
index to the array, so what can we do with
that? So here as you can see it return
the ptmx_get_ioctl function returns
whatever it indexes, right. So, as you can
see here is it assigns this pti variable
and then does all kinds of interesting
things, so pti is controllable, tp is
controllable here as well after this
dereference here to some controllable
value and, I mean in other code parts of
the kernel this is called again and so
there are there are a lot of things to
consider when you know the bug and
then you try to think how to exploit it.
Okay, one important thing here that I
wanted to mention is that this ptmx,
this function here ptmx_get_ioctl also
does the allocation of this struct here,
of this tty struct here and that's
important because I'm going to use further
on. Okay, another important thing is that
you basically, this bug what allows you
to do is you can control the size of this
array here, so by, can you see that?
Okay, so by repeatedly open the ptmx
device you can grow this array and you can
grow it as you see here by this grow
vector that's 16, but it doesn't matter.
What matters is that the size of this
array in bytes is controllable by you, the
person who are trying to exploit this bug.
Now, for example these are notes from my
exploit so if I did one allocation, if I
did one open of this ptmx device then this
array was going into kalloc_64. If I was
doing 17 it was going to kalloc_128, if I
was doing 33 opens here it was going to
kalloc_192 and so on and so forth. So I
could decide in which kalloc zone I could
place the array. If you don't know kalloc
zones, they are basically, you can think
them as container, you can think kalloc
zones as containers for heap objects on
the kernel heap. All of them can be of
different type but they're, all of them
are of the same size, right, so kalloc_64
can have different structures of size 64
bytes, but all of them are our size 64
bytes. Okay so I started debugging the
untethered binary in userland, that's how
I started. So initially I was using gdb
and I found out that nothing worked with
gdb. It was at that point Apple was
starting to move from gdb to lldb, so I
don't, maybe that was the reason gdb
wasn't tested at all. So when I
say nothing worked I mean that I was
placing break points and they weren't
hitting and I was trying like stepping and
it was continuing execution and stuff like
that. Sometimes I couldn't even attach the
binary. So then I moved to lldb, on lldb
set up with debugserver and things were
much better. Now, while I was
experimenting stealing from, just with
userland debugging my iPhone 4 device went
to into a recovery loop and I wasn't able
to get out of it, so I was forced to do a
clean restore of the device. The problem
was that at that time only iOS 7.1 was
signed by Apple, so I couldn't install a
version of files that hit the kernel that
hit the bug that I was interested to look
at, but on the other hand I couldn't not
restore my device because that was the
only device I had at the point I could do
kernel debugging with. So I updated my
device to 7.1. As I said just told you 7.1
didn't have a vulnerable kernel
to this bug, so what I wanted to do was
basically to boot an iOS 7.1 device with a
7.0.6 kernel, and in order to do that I
could use the limera1n bug that allowed me
to boot arbitrary kernels and the utility
to do that was redsn0w, right. The problem
was that redsn0w only supported up to iOS
6 and it wasn't, it didn't have support
for iOS 7 so I left all the other things I
was doing and I started reversing redsn0w
to understand how it worked. Redsn0w, if
you don't know it's, it was back then
and still is closed source, right, so I
started reversing that to understand how
it worked in order to support, for me to
hot patch it, to binary
patch it to add support for iOS 7 and I
spent like I don't know maybe a month on
that and then I realized that it was, it
wasn't leading me anywhere, I couldn't
understand a lot of things about how
redsn0w was implemented, so I I stopped
doing that, and at that point I found
opensn0w which was an effort by winocm to
implement redsn0w as open source. So, it
seemed to have support for iOS 7 and that
was good, I tested that and it was
working. Now my problem was that I
couldn't have an arbitrary
length of boot-args. Boot-args are the
arguments that you pass to the kernel when
it boots and they are really important in
iOS because by passing certain boot-args
to the kernel you can disable sign checks,
you can enable kernel debugging, so it's
really important to be able to pass
arbitrary length boot-args. And iOS 7.1
was using absurd 9 character so that was
the reason opensn0w couldn't support more
So what I ended up doing was I patched
iBEC, which is basically the loader
of the kernel, right, that passes boot-
args to the kernel when it boots and,
basically I changed the pointer to the
boot-args to some other place that had
much more space. So at that point I was
able to pass arbitrary-length boot-args to
my kernel. So where we are at last? So I
had an iPhone 4 device with iOS 7.1 and I
was using opensn0w to boot the 7.0.6
kernel that had the bug that I was
interested to exploit. Now, one side note
here is that as I was doing that and I was
trying to add to open snow all the patches
to the kernel to enable kernel debugging,
I was reversing the evasi0n7 binary as
well. Now, the evasi0n7 binary was trying
also to, after it exploited the kernel
it was patching it to enable kernel
debugging, but, so I was just copying
their patches, right, and adding them to
opensn0w, but I realized at some point
that they missed some check for the debug-
enabled variable and KDP wasn't really
working, so the session was established
and it seemed like it was working, but if
you tried to actually use the kernel,
the KDP, the kernel debugging setup for
to do actual, like to attach debugger to
the kernel and do whatever, like place a
breakpoint or step then KDP just froze. So
I added another part that was required on
that. Ok, so kernel debugging at last, but
that's not really what happened, because
you know breakpoints didn't always work so
you were placing a breakpoint and it
wasn't hitting when execution was reaching
there and you were trying to step
instructions and the execution just
continues, so you were stepping one
instruction it was just like you would
type in continue and if you were taking
too long to type an lldb command then KDP
froze and then you had to restart your
device, re-establish the kernel debugging
session and start from zero. And if you
issue commands too fast then KDB froze
again, so you have to reboot again. It was
amazing, it was great time. And now I did
similar stuff with iOS 6 and I distinctly
remember that was much easier and kernel
debugging worked much better. And... I
mean the issue that comes to everyone's
mind that does that is: do Apple engineers
really use KDP for debugging the iOS
kernel or do they use something else?
Okay, so now I could debug the evasi0n7
untethered binary both from the userland
side and from the kernel side, and that
was good because I was analyzing at run
time and at the same time I was reversing
it in IDA, so the obfuscation who... I
could do it much faster since I was taking
hints from runtime. So I... at that point
things started moving fast and I quickly
found that it was abusing the data by
structure to obtain read/write access to
physical memory. I mean that was
interesting to me, but I was expecting
something else. I was expecting something
like what they did in iOS in the evasi0n6
jailbreak, that they did like a lot of
heap manipulation and that's my interest
actually, heap exploitation. So at that
point I decided to stop reversing it and
reimplement the exploit the way that I
wanted to do it. So obviously that wasn't
work from scratch, it was from everything
that I understood up to that point, and
what I really wanted to use was the
vm_map_copy structures technique by Dowd
and Mandt and I'm going to explain that in
the following slides, how it works.
Okay, so at that point I had the clear
understanding of the bug, what it was and
I had the general idea like about how to
exploit it and I mean if you've done
that you know then it takes a lot of pen
and paper like ideas you develop on paper,
then you go test them and they don't work
and then you design them again and then
again and you fail and you despair and
then you suddenly have an idea and you
spend like I don't know like two nights
stay up until 5:00 in the morning testing
things and they don't work and then you
despair again and ad nauseam. But
eventually you get somewhere so let's talk
about exploitation now. Now, a few things
to refresh your memory about the bug. So
as I said it was an invalid indexing bug.
This pis_ioctl_list array was on the heap
and I could control in which kalloc zone
it can go. I can grow it, but once I grow
it I cannot shrink it back. Now, that's
code from that ptmx_get_ioctl function, so
what... basically what it does it
allocates a new ptmx_ioctl structure and
then it uses the index that you provide...
that you control to store the address on
the array. Now, this allocation here...
this struct here goes into kalloc.88 and
that's useful for the next parts. Okay, a
few things about the technique I wanted to
use... about the exploitation technique I
wanted to use. So it's the vm_map_copy
technique, it was proposed by Dowd and
Mandt and basically they were spraying the
heap with these structs here, the
vm_map_copy structs, and assuming you have
like some way to corrupt this struct that
you've sprayed on the heap if you can
overwrite this kdata element here, then
basically what you have is a leak of
kernel memory other adjacent like next to
the kdata, whatever is below or above the
kdata pointer or arbitrary if you put
whatever address you want in there. By
overwriting the kalloc_size element here
and then freeing the struct on the heap,
you put it on a wrong zone and basically
when you allocate it back, since you put
it on on a different size zone, you can
have a heap overflow. So that's a general
overview of this technique. So but you
corrupt this struct and you get primitive
exploitation primitives. Okay, so what was
the idea I had at that point? The idea
was to use the... this pis_ioctl_list
index bug to corrupt this kdata pointer
here and to have arbitrarily... Sorry, we
have a relative leak of kernel heap
memory, and that would be my first step
towards exploiting the bug. Of course the
end goal is to have arbitrary read/write,
right, and of course it was just a fuzzy
idea at that point and you know that's
always the goal, but when you study the
bug and you see the different code paths
and how the things you affect are used,
then you have some maybe not completely
concrete things in your mind, but you know
that interesting things can happen, so
that's what I had at that point.
Okay, so let's talk about the exploitation
strategies now. So at stage one I sprayed
the kernel heap with vm_map_copy structs
and I decided to work on the kalloc.256
zone, and the reason for that was
completely arbitrary... was because of all
the kernel debugging I have done up to
this point of this entire binary I saw
that this kalloc zone was not really used
that much, either by the kernel or by
whatever the exploit was doing. So...
that's good because it means that you
can... you as an exploiter can have much
better control over the kernel heap if
there aren't other things placing
allocations on the zone you work. So I
decided to use the kalloc.256 zone and I
avoided of course kalloc.384 because the
tty structs were going there and that
would really mess up my heap arrangements.
So the first... let me actually... ok. So
what I wanted to do was to do this.
So initially you spray the heap with
vm_map_copy structs and you control both
their size and their contents, the content
don't matter at this point. So it... just
the size matters. So I spray with 256
bytes vm_map_copy structs and then I free
every other second one and I create this
kind of pattern like a vm_map_copy and a
free slot and a vm_map_copy and a free
slot and then I grow the pis_ioctl_list
array to 256 bytes and then it goes into
one of these free slots here. Now, the
code for doing that looks something like
that, so what this basically does it
sends... it creates this... so if you see
here the out of line mach messages as
basically these vm_map_copy structs and...
Their size is 256, their buffer doesn't
matter at this point and you just send
them like machs and methods. And then
after you've sprayed with them then you
free every second one here... with this
loop here. So in order to make this free
slot you just receive this mach out of
line messages that correspond to the
vm_map_copy structs. And after you've
created the holes you basically grow the
array to 256 bytes. How do you do that? As
I mentioned earlier you open the dev ptmx
device a number of times. How many times
doesn't matter, like a specific number of
times that I mentioned earlier, that I
have noticed grows it 256 bytes. So that's
the arrangement you have at that first
stage. Okay, so the second stage is done
on the kalloc.88 zone. So I spray again
with vm_map_copy structs and this
time I make them 88 bytes to go to
the kalloc.88 zone and then I create again
holes. And then I trigger the bug with an
invalid index value and remember that when
you trigger the bug a ptmx_ioctl struct is
allocated and this goes to kalloc.88. But
because on kalloc.88 I have created this
pattern of used free used free it goes
into one of the free slots. So now I have
a ptmx_ioctl struct in one of my free
slots. I don't know where that is but I
know that it falls into the pattern,
right, so I trigger the bug and remember
that basically you control this index,
right, so since I control the index I
point it to the vm_map... to the kdata
element of the vm_map_copy struct that I
know is below the free slot that the array
went into. I don't know the address,
right, I can't put like an address there,
but I can... I know the relatives... the
relative distance in bytes because I
created the pattern... the heap pattern.
So let's go to... okay. So it looks like
that. So that's my first stage, right,
free, vm_map_copy, ... and this is the
same pattern on the kalloc.88 zone.
When you trigger the bug, this ptmx_ioctl
structure is allocated. It goes into one
of the free slots, right, and then the bug
itself, which is what we see here is...
remember you control the index, so this is
the new allocation that went here, and
then it goes and stores the address where
the index tells it to store it. But
remember that this is controlled, we
control that, so what I do I point this
here relatively to the neighboring
vm_map_copy struct at the kdata field,
right. So in this kdata field here of the
vm_map_copy struct I have now this
address, right. So that's how the heap
looks like. I have here the code, it's
very similar to the first stage that you
spray with vm_map_copy structs of size 88,
machs and methods, right, and then you
receive every second one, you create the
holes on the 88 zone and then you trigger
the bug here, right. This invalid pis
index number here is basically what points
relatively here, right. So I have now the
address of this ptmx_ioctl struct which is
an address on the kalloc.88 zone. I have
it on the kdata field of this vm_map_copy
struct here. So what I do... I can simply
receive these methods and in its content I
can see the address of that slot on the
kalloc.88 zone. So that's the code to do
that, I simply receive all the messages
and that's my address. Okay, so at this
point I only... what I only have is this
address here, right? I have the address of
this heap slot. So, at that point I
started looking at other code paths that
this invalid index... what other
variables this invalid index was
influencing and I found the code path that
was actually giving... was giving me a
write and... But in order to reach that I
needed to survive several dereferences,
and what I only knew was just the
kalloc.88 address, right? Nothing else. So
I will now walk you through everything
that gave me this write. So I clean up the
kalloc.256 zone and I spray it again with
vm_map_copy structs and create holes
exactly like the previous step...
the first stage. Again, next to the
pis_ioctl_list array I have a vm_map_copy
struct, but at this time I... in all the
the vm_map_copy structs I put a payload of
the... of this fake ptmx_ioctl address I
have. And remember that the first element
of the ptmx_ioctl struct is a pointer to
tty struct and I can use the leaked
address I have for this pointer that I
don't know... I didn't know where to point
it to. So, the next step was to clean up
the kalloc.88 zone and spray it again, and
again I sprayed with vm_map_copy structs,
but at this time at their payload I can
put now the fake tty struct that the
ptmx_ioctl struct is pointing to. The
problem at that point was that the tty
struct with 256 bytes and kalloc.88 has...
the slots are only 88 bytes, so I couldn't
just with the elements of the... just with
the first 88 byte elements, I couldn't get
to the path that was giving the write, so
I needed to find some other way to host my
fake tty struct. So remember that I
couldn't work on any other kalloc zone or
anywhere else because what I only knew was
the address of that kalloc.88 zone, I had
nothing else to build on. So at that point
I started doing a much more complicated
heap arrangement. So instead of spraying
just one thing I was spraying... I was
trying to create a pattern of two
controlled things. Now, I couldn't use
vm_map_copy structs for both these slots
because the vm_map_copy structs has
a header, right? So it would mess up my
fake tty struct. So by reading i0n1c's
kernel heap exploitation slides, I
realized that I could spray the heap with
XML properties of length 88 from that
AppleJPEGDriver and I could place as a
second controlled object after the
vm_map_copy struct these XML properties
which are completely controlled in
content, and I could host the second part
of the tty struct there. I mean, it's
still not 256 bytes, but what it gives me
is the ability to survive all dereferences
to reach the write that I was interested
in. Okay. So, a few things about the
tty struct. So that's what I got... I
wanted to create on the kalloc.88 zone,
right, so that's the tty struct that the
ptmx_ioctl struct is pointing to. Now,
what basically I wanted to do here is I
wanted to point... the final thing was
to use this clist struct to control this
element here, c_cs, as a start of the ring
buffer for the tty, to give me an
arbitrary write... Sorry, to give me a
controlled write. I started playing a bit
with to use it to do arbitrary write, but
I found that I wasn't able to do it
because at later stage some other parts of
the tty struct were needed that I wasn't
able to control, so I only had two
88-slots to host my fake tty struct. So
that wasn't stable. So I was only
using that to do a relative write. So
we'll see the code later on, let's go to
the heap layout. So that's the third
stage. Again, remember I sprayed the
kalloc.256 zone with vm_map_copy structs/
frees, just place my pis_ioctl_list array
next to vm_map_copy struct. Remember that
I control the contents of vm_map_copy,
right, so I placed in the buffer of
vm_map_copy this ptmx_ioctl address that I
know and I point the invalid index that I
control to this ptmx_ioctl... this address
that I put here. And what is this address?
It's that leaked address that I got in the
previous stage which points to the
kalloc.88 zone. And what's the arrangement
of that kalloc.88 zone? It's as I told you a
vm_map_copy followed by an XML properties.
vm_map_copy, XML properties... And all
this hosts this fake tty struct, right?
All these are the same, I just explained
here how it looks like. So this points to
the kdata element here and the rest of
it holds the rest of the... all this is
basically the fake tty struct, like the
buffer of the vm_map_copy and then
following the XML contents of this heap
allocation. And where do I... this c_cs
pointer that I told you that I wanted to
control, where do I point it? I point it
relatively again, I don't know any
addresses but I can put it relatively
since I know the... since I created this
heap arrangement, I can put it relatively
to the size of the kalloc size of the
neighboring vm_map_copy struct. And why do
I need this? Because I want to use the
vm_map_copy technique by Mandt and Dowd
that I mentioned earlier, so that's the
end goal. So what's the code looks like?
Okay, that's the spray of 256, we've seen
that a lot of times, then we have the
freeze... wait, no, that's not the freeze.
So that's the allocations of the 256...
that's... yeah, I don't have the freeze
here because they don't matter, because we
have seen them before. So what I have here
is the spray of the kalloc.88 zone and the
important thing here is that... what I
wanted to show you that is that at every
step I took two allocations. One is the
vm_map_copy struct here with the machs and
methods, and the second part is the XML
properties, which are sprayed on the heap
when you open the device driver, the
AppleJPEGDriver. And what are the contents
of that XML properties? They're basically
that fake... the second part of the fake
tty struct that you have the controlled
c_cs pointer that will give me the
relative write. So if you see here, I have
this function setup_fake_tty that
basically creates the structs so I don't
have to type all the time, and we are at
second stage here, and basically what you
can see here is the creation of the
fake tty struct, right? So that's the
different elements of the fake tty as we
saw it from the code. And that's the write
offset I wanted to... that I pointed to
the kdata field of the neighboring
vm_map_copy struct. So, again, that's how
it looks like in the heap. Okay, so after
that, after we have arranged the... we
have arranged it this way, we trigger
again the invalid index array bug, but at
this time on the slave ptmx device. I was
only doing that on a master ptmx device,
but in order to reach that write code path
that I mentioned, you need to get on a
slave ptmx device. So that's what happens
here. And then you simply write to the
corresponding descriptor and it just
dereferences this c_cs that you controlled
and your end... and it writes with
whatever you want to write. And what do I
want to write? I want to write a new size
for the vm_map_copy struct... for the
kalloc size field of the vm... of the
neighboring vm_map_copy struct, so I can
use the Dowd and Mandt technique. So,
putting everything together. So at that
point I have a controlled corruption of a
vm_map_copy struct and I can use the
primitives to get arbitrary... an
arbitrary leak, so I can leak for example
the KSLR-slide and I can do a heap
overflow. Again these are how you can use
the primitives that Mandt and Dowd gave
us. Now I also know my location on the
kernel heap, and remember that... that's
basically... we found that on the stage...
on the first... on the first of stages and
we only... we use only that, like where
that ptmx_ioctl struct was stored on the
kernel heap, that's the only thing we
knew, that address, in order to
successively build on it, in order to
reach like a much more useful primitive.
And the important... the interesting thing
here is that everything up to this point
is data only, right? So you haven't
injected any code, you haven't done
anything at all that you could be caught
somehow by a kernel self-protection
mechanism or these kind of things,
everything's data only. So once you reach
that point, how do you get PC control? So
since you can use Dowd's and and Mandt's
technique, you can basically do a heap
overflow, so you can again do a heap
arrangement, you can place IOKit objects
next to vm_map_copy structs where you can
overflow from, and you can corrupt IOKit
objects and from there you can have...
also you can do an arbitrary write...
read/write, so you can... by the arbitrary
read you can read the vtables of the IOKit
objects so you know the KSLR-slide
and you can corrupt it in order to get PC
control. Of course getting to a whole
jailbreak from that point is out of the
scope of this talk, and... but is not that
hard actually from that point on. And
okay, so after doing all that how close
was that exploit to the evasi0n... to the
real evasi0n7 kernel exploiter? I'd say it
was pretty far off, but I mean it wasn't
my point to recreate it like completely,
but it was my point to play with the heap
and to try to do complex heap arrangements
and to see how much I understand the iOS
kernel heap, that was the point of this
whole exercise for me. Okay, so some
lessons learned. So the real surprising
thing for me at that point was that I
couldn't believe that Apple does kernel
debugging by KDB. It was very flaky, it
was very unstable as I told you. If you
type commands too fast it froze, if you
type commands very slowly it had like a
go-stop timer and froze, I think.
there was a claim of something like that
and it's unbeliev... I couldn't believe
that the Apple engineers were using this
interface to do kernel debugging. So it
was really hard to do anything on the
kernel side of idevices. But of course I
don't really mean that you shouldn't mess
with these things, right? I mean, these
devices are really interesting and it's
really becoming harder to hack them, but I
think it's much more fun and I think the
only takeaway may be that you shouldn't
report bugs to Apple at all and if you
need street cred you should just report
white elephant bugs now. I mean that's
always good. And I mean this very... this
is getting very esoteric, right, there are
not a lot of information and Apple keeps
changing stuff and everything is closed
source, I mean, all the important parts
are closed source... and I mean, I really
think people that work on that things
should share notes as much as possible.
Okay, so these are some of the people I
was talking to while doing all this and I
want to mention them, and basically that's
all of the material I have and I'm open to
any questions you might have.
applause
Herald: Thank you, argp, for the talk. So
we have prepared microphones 1, 2, 3 and 4
in the room and we have a Signal Angel, I
think. You... when you have questions, you
can give me a hand sign, but I
think we start with microphone 2 here in
the front. And please ask questions and no
comments, there's time after the talk.
Okay, go ahead.
Q: Thanks for the awesome talk.
argp: Thanks.
Q: I have a question about heap spraying.
Was your heap spraying really stable? If
it is not successful, did it crash the
device?
argp: Yeah. So I haven't mentioned it
here, but it was pretty stable I think.
It was something like... because I did a
lot of tests for that because it was
really interesting for me to know. It was
maybe something like 90%, so 9 out of 10
times it worked, but if it didn't work -
yeah, then... yes it crashed the kernel
and crashed the device, yeah.
Q: And did you try to return heap into
some kind of initial state to start your
exploit from scratch?
argp: Yeah, that's true I haven't included
that, but you're right. So the initial
step on every spray that I mentioned here
was to spray a lot of objects of the
specific size you were targeting in order
to get basically a new page of the kalloc
zone, right? So you... so even if as I
told you the kalloc 256 zone wasn't
that busy, it's still... there were still
allocations going on it, right? So if you
did a lot of initial spraying, you were
making sure that when you're... the
allocations that mattered to you we're
made, were on a new page that weren't...
wasn't too much noise from other
allocations from the kernel. So yeah,
you're right I haven't included that, but
yeah, that happened.
Q: Thanks, great.
argp: Thanks.
Herald: Then microphone 1, please.
Q: Also thank you for your awesome talk
again.
argp: Thanks.
Q: My question was nowadays it's way
harder to use vm_copy I think Apple truly
deprecated it, it's not possible anymore
that due to security. Do you see hope in
reconstructing some function that does the
same or is it totally dead now?
argp: Oh, you mean the vm_map_copy
technique?
Q: Yes.
argp: No, I think it's completely dead
now.
Q: All right. And I recently saw on the
iOS logs vulnerabilities that again a
vulnerability in AppleJPEGDriver was
found. Do you think... have you looked
into it or...
argp: Well, Apple... the AppleJPEGDriver
is one of the 4, I think, IOkit drivers
that you can reach from the container
sandbox, right? So that means it's very
fast by everyone, Apple included, and
very audited. So I'm not saying that there
aren't many... there aren't things there,
like interesting findings, but if there
are they're not going to live much longer,
I think.
Q: Okay, thank you.
Herald: Thanks for your question and now
from the Signal Angel a question from the
Internet.
Signal Angel: Yes, I have a question from
the internet. How long did this research
take you? You said two weeks in the
beginning, but from begin to end, how many
hours about? Because you also said it was
during work?
argp: No, it didn't it didn't take two
weeks, no. It took like maybe close to
three months or two months and something
like that. So I spent... as I mentioned I
spent like a complete month, I think, like
- maybe three weeks, maybe not a complete
month just on reversing redsn0w and trying
to get redsn0w to play with iOS7. So I
wouldn't count this month in the exploit
part of it, right? So if you're interested
just in the kernel exploit part I would
say something like maybe seven weeks,
something like that. But just with 2 maybe
3 days per week right, not complete weeks.
Herald: Okay, then microphone 1,
please.
Q: Congratulations on your talk which was
really interesting, I liked it a lot and
my question is if the technique you used
to exploit the bug was in FreeBSD or any
other BSD as well?
argp: Oh no, no. I mean, the vm_map_copy
struct doesn't exist anywhere else except
the XNU kernel. But I think the
interesting takeaway is that you can do
complex heap arrangements if you
understand the kernel heep allocator,
right? So this process I described by
creating holes and maybe controlling 2
allocations in order to host fake
structures that you are able then to use
to get exploitation primitives then that's
applicable everywhere, right?
Herald: Okay, then we go to microphone 2
again, please.
Q: So I saw one sentence, just not report
or... just don't report the bugs. I would
like to understand your thinking behind,
because I think this is really important
for companies to know the bugs that they
made and yeah, make the products better
and this is really beneficial for
researcher because for example Apple they
pay a lot of money for the bugs. What...
argp: Okay, yeah, I don't have much to say
on that. I mean, apart from: if all the
bugs are fixed then you won't be able to
do this kind of work and it's no fun.
Sorry, I don't have anything else to say
on that. Sorry, I don't have anything
else, no comment.
Herald: Okay. Signal Angel, do we have
another question from the internet? Okay,
then please a big round of applause for
our speaker!
argp: Thanks.
applause
postroll music
subtitles created by c3subtitles.de
in the year 2020. Join, and help us!