*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!