0:00:00.000,0:00:19.520
36c3 preroll
0:00:19.520,0:00:25.990
Herald: To everybody here, please be[br]welcome to this fantastic kid here. I
0:00:25.990,0:00:31.249
learned a lot from him, even though he's[br]only since two years playing around with
0:00:31.249,0:00:37.380
iOS. I mentioned as well the first[br]untethering case that I had with my phone
0:00:37.380,0:00:44.010
was something about iOS 5.1 and with every[br]update, you had to do the whole shebang
0:00:44.010,0:00:50.910
again. Of course, that's what I remember.[br]So please give a warm welcome here too
0:00:50.910,0:00:54.470
littlelailo. Do I spell this correctly?[br]Offscreen: Yeah.
0:00:54.470,0:01:03.250
Herald: Yes, littlelailo is really a very[br]fascinating geek. He actually hacks any
0:01:03.250,0:01:08.610
kind of OS device, to my opinion. Any kind[br]of. Yeah. So throw it to him and it comes
0:01:08.610,0:01:14.390
back in pieces. He's as sharp as a knife.[br]Please give him a warm, well, welcoming
0:01:14.390,0:01:17.850
applause. We're gonna untether iOS. Yay!
0:01:17.850,0:01:23.760
applause
0:01:23.760,0:01:28.470
littlelailo: Okay, so yeah, I'm[br]littlelailo, as already introduced. I'm
0:01:28.470,0:01:34.120
interested in I.T. security in general.[br]And I started to look into iOS about two
0:01:34.120,0:01:40.670
years ago and then I met three awesome[br]guys: @s1guza, @stek29 and @iBSparkes. And
0:01:40.670,0:01:46.790
we basically started to chat a lot and in[br]the end also released the "jailbreak me"
0:01:46.790,0:01:50.950
for iOS 10 on https://totally-[br]not.spyware.lol/. For everybody who isn't
0:01:50.950,0:01:55.430
familiar with the jailbreak scene, it's[br]like rooting on Android. So it basically
0:01:55.430,0:02:02.420
is a tool that gives the user the[br]capability to tweak the device, and that's
0:02:02.420,0:02:06.170
mostly done by basically installing a[br]jailbreak in this case. You just go to a
0:02:06.170,0:02:07.920
website on the slide there[br][https://totally-not.spyware.lol/] and it
0:02:07.920,0:02:12.490
will install a package manager on your[br]phone and then the end user can install
0:02:12.490,0:02:17.980
tweaks. So just a little dynamic libraries[br]and they get injected into all the other
0:02:17.980,0:02:21.500
system processes and then they can[br]obviously modify their memory. And that,
0:02:21.500,0:02:25.620
for example, allows customization of the[br]home screen or something like that. And
0:02:25.620,0:02:31.770
basically with the release, we also formed[br]the team @Jakeblair420 was created with a
0:02:31.770,0:02:35.070
Twitter account. There are also a few[br]demos there if you want to check it out
0:02:35.070,0:02:39.699
after the talk. And I basically had this[br]pipe dream that in my life. I really want
0:02:39.699,0:02:44.740
to achieve an untethered jailbreak using[br]only some sort of plist or other save file
0:02:44.740,0:02:49.890
corruption. So basically there are[br]different kinds of jailbreaks. In modern
0:02:49.890,0:02:54.480
jailbreak the most common kind are semi-[br]untethered, which means that the users has
0:02:54.480,0:02:59.580
to connect their device to a PC when they[br]first install a jailbreak and then they
0:02:59.580,0:03:04.250
sideload an app onto the device or it just[br]installs itself there. And then on each
0:03:04.250,0:03:09.160
reboot, the user has to go into the app[br]and press a button to jailbreak the phone.
0:03:09.160,0:03:14.370
And basically, after they reboot, they[br]lose the jailbreak and have to go through
0:03:14.370,0:03:19.510
this process again. And with an untethered[br]jailbreak, the jailbreak gains persistent
0:03:19.510,0:03:24.250
installation and then they will[br]automatically jailbreak the device on each
0:03:24.250,0:03:31.900
boot. So the device automatically boots[br]jailbroke. The untether can be divided
0:03:31.900,0:03:37.390
into four stages. The first stage is when the[br]config file or database stage. I would go
0:03:37.390,0:03:42.150
over how we gain execution of the boot.[br]Then the config parser bug that gives us
0:03:42.150,0:03:47.970
the right opey primitive. And then I will[br]talk about the main bug, the ASLR bypass,
0:03:47.970,0:03:53.090
which allows us to get into ROP. Then[br]stage two is basically a kernel exploit
0:03:53.090,0:03:58.360
and completely written in ROP and there I[br]will go over the two main kernel bugs: the
0:03:58.360,0:04:03.570
kASLR leak and then the racy double free[br]we used to get the kernel read/write, and
0:04:03.570,0:04:09.690
I will also talk about the kASLR weakness[br]and RootDomain user client memory leak
0:04:09.690,0:04:14.440
which aided us in exploitation. And after[br]we get kernel read/write, we can remove a
0:04:14.440,0:04:19.810
few restrictions from Apple. Mainly we can[br]bypass code signing and that gives us the
0:04:19.810,0:04:24.380
ability to load an unsigned dynamic[br]library into our process. So now in stage
0:04:24.380,0:04:30.210
3, we are basically C, we are dynamic[br]library and there we are stashing the
0:04:30.210,0:04:36.830
kernel task port to host special port 4.[br]So iOS has this concept of ports and task
0:04:36.830,0:04:41.770
ports. And basically, if you have a send[br]right to a task port of a process, you can
0:04:41.770,0:04:48.219
read and write its memory. They're most[br]used for inter process communication. And
0:04:48.219,0:04:53.409
where modern jailbreaks used to stash the[br]kernel task port into a special port 4 so
0:04:53.409,0:04:57.830
that other user mode applications can then[br]retrieve it and with that retrieved and
0:04:57.830,0:05:01.810
with send right they can just read and[br]write process memory. And after that I
0:05:01.810,0:05:07.740
just fix up everything and then spawn[br]stage four and that's basically running in
0:05:07.740,0:05:12.599
a separate process. That has a few[br]advantages I will go into later. It's
0:05:12.599,0:05:15.699
basically performing the whole post[br]exploitation process, including launching
0:05:15.699,0:05:19.689
substrate - the framework that's used for[br]tweek injection and then performing
0:05:19.689,0:05:24.099
ldrestart to restart or launch demons on[br]the system and inject the tweaks into
0:05:24.099,0:05:30.931
them. In stage 1, we first need to get[br]execution after boot, and when iOS boots
0:05:30.931,0:05:35.830
launchd is the first process that's[br]running on the system and it then loads a
0:05:35.830,0:05:41.871
dynamic library with a list of executables[br]- that are LaunchDaemons - and there are a
0:05:41.871,0:05:47.219
few flags associated with them. If the[br]"run and load" flag is set, launchd will
0:05:47.219,0:05:52.759
then spawn all of those LaunchDaemons[br]afterwards. And we basically just take one
0:05:52.759,0:05:58.591
of those launch demons, namely Racoon. So[br]what is Racoon? Basically Racoon is part
0:05:58.591,0:06:07.030
of the ipsec package. And it's a VPN[br]client used to interact with an IPSec VPN
0:06:07.030,0:06:13.919
server. And the problem is though, that[br]IPsec-Tools project has been abandoned
0:06:13.919,0:06:18.649
since 2014. So they officially here state[br]on their website that the development of
0:06:18.649,0:06:22.789
the project has been abandoned and that[br]IPsec-Tools has security issues. You
0:06:22.789,0:06:29.059
should not use it. Apple still decided it[br]was a good idea to ship it after 2014 and
0:06:29.059,0:06:34.680
they now maintain their own fork on[br]https://opensource.apple.com . And yeah,
0:06:34.680,0:06:39.099
basically the only thing that's important[br]for the talk is that raccoon has a
0:06:39.099,0:06:43.560
configuration parser. So on startup it[br]will just look for a file on disk and
0:06:43.560,0:06:49.080
start to parse that. And that's written in[br]yacc so completely written in C. So memory
0:06:49.080,0:06:53.789
corruption might become a problem. And it[br]actually is a problem as we can see if the
0:06:53.789,0:07:00.409
config parser bug and for that when we[br]have to go back a bit. Basically in 2011
0:07:00.409,0:07:08.619
Pod2g released Corona for iOS 5, which was[br]also an untethered. He used a bug in
0:07:08.619,0:07:15.699
Racoon in the configuration parser and[br]Pod2g found this one on an old bug tracker
0:07:15.699,0:07:20.789
and basically the IPsec website had this[br]bug tracker there. There users could just
0:07:20.789,0:07:27.460
report problems with their programs and[br]one user in 2009 I think reported a bug
0:07:27.460,0:07:31.509
that if he would use the specific config[br]Racoon would just segfault, but nobody
0:07:31.509,0:07:36.930
looked at this for two years. So in 2011[br]Pod2g realized that this was actually
0:07:36.930,0:07:40.539
exploitable and then used in a jailbreak.[br]And after that, obviously Apple got aware
0:07:40.539,0:07:45.610
of it. And then they also said that they[br]fixed it. And it's got a CV, assigned
0:07:45.610,0:07:53.910
CVS-2012-2737. And yeah, they say that[br]this was improved through improved bounds
0:07:53.910,0:07:58.869
checking. So let's look at the patch.[br]That's the function before the patch and
0:07:58.869,0:08:03.249
that's the function after the patch. And[br]if this was too fast for you, here's the
0:08:03.249,0:08:07.559
diff. So basically there is no difference.[br]The bug is still there and Apple didn't
0:08:07.559,0:08:13.029
patch it. What really happened here was[br]that basically this is obviously a
0:08:13.029,0:08:17.529
configuration parser. So there are[br]different statements that nearly do the
0:08:17.529,0:08:22.610
same thing. There are these nbns4[br]statements to parse an IP address and
0:08:22.610,0:08:27.180
there are wins4 statement followed by an[br]IP address and both of them have to parse
0:08:27.180,0:08:33.940
the IP address. So the function for[br]parsing that was just copy pasted from
0:08:33.940,0:08:41.130
another one. And yeah, basically Apple[br]fixed the bug in the other function. Which
0:08:41.130,0:08:47.520
I don't get because basically the engineer[br]there had to look at PoC Pod2g provided
0:08:47.520,0:08:51.250
then they had to realize okay, the bug is[br]in there, fix it, then they had to
0:08:51.250,0:08:55.050
recompile and never tested against the[br]original PoC because otherwise Racoon
0:08:55.050,0:08:59.620
would have crashed again. Yeah, it's[br]basically an off by one which allows you
0:08:59.620,0:09:05.640
to overwrite the index of an array and[br]Pod2g gave a talk about this and we can
0:09:05.640,0:09:11.390
basically reuse his strategy from back in[br]2011 to get a write primitive.
0:09:11.390,0:09:16.730
Again, the one function here at the top.[br]You can see the yacc syntax so an addrwins
0:09:16.730,0:09:21.240
list has to consist of either an addrwins[br]statement or addrwins statement followed
0:09:21.240,0:09:25.600
by a comma, followed by an addrwins list.[br]So that's a recursion. There can be
0:09:25.600,0:09:31.020
multiple addrwins statements in a row. And[br]then below that, an addrwins statement is
0:09:31.020,0:09:35.390
defined as containing an address string.[br]And that's just a regex to match an IP
0:09:35.390,0:09:38.950
version 4 address. And if the parcel finds[br]that it would run the code between those
0:09:38.950,0:09:44.480
two brackets. So they get the pointer to a[br]global struct and then they check if this
0:09:44.480,0:09:50.500
nbns4_index is bigger than MAXWINS and if[br]so, they return an error. The problem here
0:09:50.500,0:09:54.620
is that this is off by 1. It shouldn't be[br]checking if it's greater than so, but it
0:09:54.620,0:09:58.440
should be a check for greater or equal[br]than MAXWINS. And after that, they use
0:09:58.440,0:10:03.090
thisinet_pton function to basically parse[br]the address, which is in this variable
0:10:03.090,0:10:10.130
into the nbns4 array at the nbns4 index[br]and then post-increment the index. And
0:10:10.130,0:10:13.990
that will also be coming in handy when[br]exploiting this bug. So yeah. How is
0:10:13.990,0:10:18.740
exploitation done? Basically there are[br]here the globals, there is this Lcconfig
0:10:18.740,0:10:23.610
pointer in the globals pointing to a heap[br]structure and that the parser also uses.
0:10:23.610,0:10:29.670
And then there is this dns4 array followed[br]by the dns_area_index and then the nbns4
0:10:29.670,0:10:35.341
array followed by the nbns_4_array_index[br]so we can just use mode_cfg statement
0:10:35.341,0:10:39.270
followed by this wins4 statement with the[br]IP address to parse the IP address in the
0:10:39.270,0:10:44.630
first array element and we can repeat this[br]process four times to then move the index
0:10:44.630,0:10:49.680
out of bounds. So now it's pointing to[br]itself and the cool thing is about that is
0:10:49.680,0:10:53.910
that therefore there would be a good idea[br]to use integers for those area indexes
0:10:53.910,0:10:59.110
instead of unsigned integers so we can[br]just point them... you just use negative
0:10:59.110,0:11:04.260
numbers to bypass the bounds check. So[br]we'll overwrite it with -1 to point it to
0:11:04.260,0:11:09.110
an dns_array_index and then we use another[br]wins statement to override the
0:11:09.110,0:11:13.130
dns_array_index with a negative number.[br]And the cool thing about this is that
0:11:13.130,0:11:16.400
because of the post-incrementation, it[br]will move it back to zero so we can repeat
0:11:16.400,0:11:20.760
this process as often times as we want,[br]and we will use a negative number to
0:11:20.760,0:11:24.740
basically point it to the Lcconfig[br]pointer. And then we can use two dns4
0:11:24.740,0:11:28.940
statements to override the Lcconfig[br]pointer and point it anywhere in memory.
0:11:28.940,0:11:34.660
And after that we can use a timer[br]statement with a counter. Then the
0:11:34.660,0:11:39.510
parsible just tried to write it to the[br]Lcconfig structure so it will de-reference
0:11:39.510,0:11:44.200
our pointer and write it there and the[br]interval statement will basically write to
0:11:44.200,0:11:49.740
an uint32, which is followed by the uint[br]where the counter statement is written so
0:11:49.740,0:11:53.500
we can turn this into a 64 bit write[br]anywhere primitive and write anywhere in
0:11:53.500,0:11:59.560
process memory and that gives us an[br]ability to basically exploit the ASLR
0:11:59.560,0:12:05.030
bypass. So this is the main bug of the[br]whole untethered and allows us to
0:12:05.030,0:12:10.350
basically have this primitive. And it's[br]also the reason why I decided to give this
0:12:10.350,0:12:14.530
talk this year and not last year, because[br]there the bug was pretty fresh and only
0:12:14.530,0:12:20.550
patched in iOS 12 and it gives to an[br]attacker the ability to exploit zero click
0:12:20.550,0:12:23.680
more easily because they just need the[br]write anywhere primitive. And that's why I
0:12:23.680,0:12:29.740
held back on it. Siguza found it after[br]looking into Apple's patches of the Corona
0:12:29.740,0:12:35.410
ASLR bypass and basically from Pod2g's[br]presentation, we as a team learned that
0:12:35.410,0:12:40.160
ASLR is always bypassed when a writable[br]region is larger than the maximum slide,
0:12:40.160,0:12:45.100
because then there's always a writable[br]address in process memory and you can use
0:12:45.100,0:12:51.230
this to brute force the slide and Siguza[br]found out that there was what was also the
0:12:51.230,0:12:56.680
same problem with the cache in iOS 11 and[br]a few versions of iOS 10 actually. So
0:12:56.680,0:13:02.200
what's the cache? Basically, the cache is[br]a pre compiled blob containing the dynamic
0:13:02.200,0:13:07.550
link libraries from Apple. So on each[br]released, you just move all of those into
0:13:07.550,0:13:13.430
one big file to optimize load times of[br]apps and keep memory pressure low. So it's
0:13:13.430,0:13:19.230
a pretty big file. And for that on boot[br]the kernel needs to calculate a slide and
0:13:19.230,0:13:23.940
slide it in memory. But they only slide it[br]once on boot because it's used in every
0:13:23.940,0:13:30.070
process. And so Apple defines an area of[br]memory for the cache with a base address
0:13:30.070,0:13:35.890
and the size and that's hardcoded on iOS[br]11. The cache got so big that a maximum
0:13:35.890,0:13:42.630
slide is now smaller than its data[br]segment. So the conditioning is satisfied
0:13:42.630,0:13:47.910
and ASLR can be bypassed, basically. And[br]the weird thing about this is that
0:13:47.910,0:13:54.040
actually the same thing also happened in[br]iOS 7. Back then, the size was defined as
0:13:54.040,0:13:58.220
500 megabytes and the cache got bigger[br]than 500 megabytes. But before that, it
0:13:58.220,0:14:02.650
was also in a few versions possible to[br]have the same condition that the data
0:14:02.650,0:14:06.910
segment again was bigger than the maximum[br]slide. And Apple was actually also aware
0:14:06.910,0:14:11.150
of this because Acer talked about it[br]in one of his talks, but they just
0:14:11.150,0:14:15.950
increased the maximum slide to 1 gigabyte[br]and didn't edit any asserts. And now in
0:14:15.950,0:14:22.240
iOS 11, it again got so big that the same[br]thing happened. And we believe that they
0:14:22.240,0:14:27.380
didn't even really notice that up until[br]iOS 12 where the cache was now bigger than
0:14:27.380,0:14:31.150
1 gigabyte so the kernel just couldn't fit[br]it in the memory region and paniced. And
0:14:31.150,0:14:35.250
because of that they just thought well,[br]then we will increase it to 4 gigabyte
0:14:35.250,0:14:41.140
while developing so we might see this[br]reoccurring in I don't know, iOS 15 or
0:14:41.140,0:14:47.300
something like that. Exploitation is also[br]pretty simple. We can just brute force the
0:14:47.300,0:14:52.210
slide now and there are a lot of function[br]pointers in the data segment. So we
0:14:52.210,0:14:58.550
decided to go with __platform_mem_move[br]because we can reach it via strlcpy from
0:14:58.550,0:15:02.730
the conflict parser. And the basic[br]strategy behind this is we write a slid
0:15:02.730,0:15:08.640
rop chain into an address we can always[br]write to for a specific slide abc and then
0:15:08.640,0:15:13.220
we overwrite the __platform_mem_move[br]pointer at its unslid address plus the
0:15:13.220,0:15:19.590
slide we just chose with a gadget that[br]would pivot to our slid rop chain and then
0:15:19.590,0:15:25.550
we call strlcpy. And now there are[br]basically two possibilities. Either we
0:15:25.550,0:15:30.220
guessed the right slide and then we jump[br]to our gadget and pivot the stack or we
0:15:30.220,0:15:33.500
guessed the wrong slide. But obviously[br]nothing happened because we can just write
0:15:33.500,0:15:38.540
there so we can go back to step one and[br]try again. And then with that we can just
0:15:38.540,0:15:41.650
brute force the slide till we get the[br]right one. The catch with this, however,
0:15:41.650,0:15:45.590
is that the write what were is pretty slow[br]and it also needs a lot of storage. So for
0:15:45.590,0:15:53.461
a 64 bit write I need about 120 bytes in[br]the config file. And because there are so
0:15:53.461,0:16:01.720
many possible slide values, the chain I[br]have currently is around... it's only to
0:16:01.720,0:16:07.010
rop gadgets, but the config file is[br]already 12 megabytes, I think. And because
0:16:07.010,0:16:11.490
of that, it takes around 10 seconds to run[br]if it's a really bad slide. So the rop
0:16:11.490,0:16:16.050
chain has to be as short as possible. And[br]the other problem is that if we have a bad
0:16:16.050,0:16:19.570
slide, we will basically smash the whole[br]data segment and we can't really recover
0:16:19.570,0:16:23.520
from that. So we had crashes in malloc and[br]stuff like that while developing,
0:16:23.520,0:16:27.650
basically. There are some solutions to[br]that, we can have a really short rop chain
0:16:27.650,0:16:34.100
in stage one and we can achieve this by[br]basically just opening the big cache file
0:16:34.100,0:16:39.440
to get a file descriptor to it and then we[br]can map it at a static address and then
0:16:39.440,0:16:43.300
get many gadgets there because the file is[br]obviously code signed by Apple so we can
0:16:43.300,0:16:47.900
just jump there after mapping it. But the[br]problem with that is that nothing is set
0:16:47.900,0:16:53.110
up. So malloc and other functions aren't[br]working. But as I said earlier, the
0:16:53.110,0:16:57.830
current cache has a smashed data segment[br]so we don't really lose anything. And
0:16:57.830,0:17:01.530
after having the cache at a static address[br]we can use the open and mmap functions
0:17:01.530,0:17:05.569
from that cache to basically map stage two[br]into the process memory and stage two is
0:17:05.569,0:17:16.480
just a really big rop stack. So, yeah. And[br]then we are basically in ROP, but we can
0:17:16.480,0:17:21.200
only use raw syscalls and not much more[br]because everything else will either use a
0:17:21.200,0:17:26.449
function that uses malloc or will use[br]malloc on it's own. And the other problem
0:17:26.449,0:17:30.360
is that basically errno is also broken. So[br]every syscall which fails will crush the
0:17:30.360,0:17:35.110
binary. So the first thing I do is[br]basically map a few pages so that errno
0:17:35.110,0:17:40.610
works again because we have a few syscalls[br]that might fail. And then the other big
0:17:40.610,0:17:45.150
problem is that we now need to implement[br]the whole kernel exploit in rop. So I
0:17:45.150,0:17:50.480
start to write a framework around this[br]awesome gadget which is present in all the
0:17:50.480,0:17:54.059
cache versions I looked at. Basically at the[br]top you can see that all the high
0:17:54.059,0:18:00.559
registers are moved into x0 through x7. So[br]all the registers used for the calling
0:18:00.559,0:18:07.610
convention are and then we branch with[br]link to register based on x27. So also
0:18:07.610,0:18:11.950
high register. And after that we load all[br]the register values back from the stack so
0:18:11.950,0:18:16.749
we can just chain those gadgets together[br]to call any functions. You basically rop
0:18:16.749,0:18:22.309
into the lower half and then chain another[br]one of those afterwards and then they can
0:18:22.309,0:18:26.889
load all the values, call the function and[br]then load all the values from the stack
0:18:26.889,0:18:32.399
again. And that's how the whole rop chain[br]works. So yeah, I also used another gadget
0:18:32.399,0:18:37.480
to basically add two registers together[br]and another one which stores x0 so that I
0:18:37.480,0:18:42.799
can store the return value on the stack[br]and later reuse it. And for loops I use a
0:18:42.799,0:18:46.950
gadget which just returns if x0 is zero.[br]So it's basically just a read instruction
0:18:46.950,0:18:51.300
then and otherwise it will tail call a[br]function using function pointer from the
0:18:51.300,0:18:54.890
data segment. And because I can control[br]the whole data segment, I can just put a
0:18:54.890,0:19:00.230
function pointer there that that will then[br]jump to an epilog and misasligning the stack
0:19:00.230,0:19:05.119
with that. And then I can call longjmp[br]with two different register values and
0:19:05.119,0:19:10.399
because of that pivot the stack to another[br]location. And when we basically didn't
0:19:10.399,0:19:16.399
jump outside of the loop, I just mmap the[br]part of stage 2 which has the loop in it
0:19:16.399,0:19:23.050
back over old stack again. And then I can[br]just reuse to stack every time. And then
0:19:23.050,0:19:27.529
pivot up using longjup. The problem wih[br]this, however, is obviously that it's
0:19:27.529,0:19:33.590
pretty slow because I use mmap as a[br]syscall. But this can be solved by just
0:19:33.590,0:19:38.980
unrolling your loops like for 10[br]iterations and then mmapping the file so
0:19:38.980,0:19:43.390
that I get more loop iterations basically[br]per second, which is important for the
0:19:43.390,0:19:52.310
race. So now we will go over the kernel[br]bug so the kASLR leak is CVS-2018-4413 by
0:19:52.310,0:19:58.130
panicall. It was fixed in iOS 12.1 and[br]it's luckily reachable from the Racoon
0:19:58.130,0:20:03.289
sandbox because Apple is sandboxing most[br]userland processes and the Racoon sandbox
0:20:03.289,0:20:08.390
is really tight. We didn't have that much,[br]many bugs that would work in Racoon, but
0:20:08.390,0:20:14.510
luckily this one does because Racoon has[br]access to the sysctl functions and this
0:20:14.510,0:20:20.240
one is in the sysctl_progargsx function.[br]The progargsx function basically allocates
0:20:20.240,0:20:25.500
a page using kmem_alloc. But it doesn't[br]zero it so it might contain old heap data
0:20:25.500,0:20:30.889
and then they copy the process arguments[br]to the front of the page. And now if you
0:20:30.889,0:20:35.139
supply a wrong size from user space and[br]there are a few other conditions that have
0:20:35.139,0:20:40.059
to be met for some weird reason it copies[br]from the end of the page into user space
0:20:40.059,0:20:44.559
which I don't get why does this even get[br]by code review. But here's a graphical
0:20:44.559,0:20:49.909
illustration: there is basically this page[br]is full of uninitialized data and then
0:20:49.909,0:20:53.240
they put the process arguments in the[br]front and copy out uniinitialized heap
0:20:53.240,0:20:58.909
data from the back into user land and we[br]can obviously just spray specific heap
0:20:58.909,0:21:04.429
objects with kernel pointers in them and[br]then free them again and use this
0:21:04.429,0:21:08.191
primitive to maybe leak them. And if we[br]find a kernel pointer in there, we can
0:21:08.191,0:21:15.379
just calculate this kernel slide. So yeah.[br]And then we come to the racy double free.
0:21:15.379,0:21:19.679
As I said, the main problem of the[br]untether is the racoon sandbox, so many of
0:21:19.679,0:21:24.720
the kernel bugs that would work in iOS 11[br]from an app, didn't work from racoon. But
0:21:24.720,0:21:30.080
luckily on Halloween Sparky told me about[br]Lightspeed bug from Synacktiv, which is
0:21:30.080,0:21:35.960
reachable from the sandbox. It's a double[br]free in kalloc.16. The kernel allocator's
0:21:35.960,0:21:42.879
based on zones and with different sizes.[br]So there's, for example, kalloc.16 and
0:21:42.879,0:21:47.270
then all objects in that zone have a size[br]of 16 or less bytes and they are obviously
0:21:47.270,0:21:55.460
also other zones for bigger objects. And[br]basically there is a structure in there
0:21:55.460,0:22:00.690
that then gets doubled. And there is the[br]syscall that's handling async file events.
0:22:00.690,0:22:04.370
So a user mode application can just call[br]it and tell the kernel, hey, please write
0:22:04.370,0:22:09.720
a buffer to a file and then move on with[br]execution and it uses a kernel thread to
0:22:09.720,0:22:13.639
perform those. And for that, that[br]basically allocates a structure to contain
0:22:13.639,0:22:18.330
some metadata like where the buffer is and[br]to which file it should write, and the
0:22:18.330,0:22:23.590
kernel thread obviously has to free the[br]structure after it's done because it just
0:22:23.590,0:22:27.850
gets into the query for[br]the kernel thread, that then wakes up and
0:22:27.850,0:22:33.000
maybe sees it has a new job, then performs[br]the operation and then it has to free it,
0:22:33.000,0:22:36.450
otherwise it will leak. But the problem[br]here is that if an error occurred while
0:22:36.450,0:22:41.419
setting the structure up, the second field[br]in the structure will be zero. And then
0:22:41.419,0:22:46.440
the structure also isn't included into the[br]jobs list. So the kernel thread will never
0:22:46.440,0:22:51.460
wake up and look at it. So the syscall has[br]to free it because otherwise it will leak.
0:22:51.460,0:22:55.100
And the problem here is that we can[br]basically reallocate the structure before
0:22:55.100,0:22:58.929
the syscall checks. So what happens here[br]is that the syscall allocates the
0:22:58.929,0:23:04.400
structure, fills it up, and then it gets[br]added to the list and then the kernel
0:23:04.400,0:23:09.999
thread is so fast that it runs while the[br]syscall isn't finished yet and basically
0:23:09.999,0:23:17.780
gets the gets the job done and frees it.[br]And then we can spray heap objects pretty
0:23:17.780,0:23:22.980
fast to overlay with that structure. And[br]then the syscall finishes and checks the
0:23:22.980,0:23:27.619
field and sees that it's zero because we[br]just replaced it with an object that has
0:23:27.619,0:23:32.749
zero at that location so it frees it again[br]leading to a double free. And yeah, we can
0:23:32.749,0:23:38.909
obviously exploit it. So for exploitation[br]I just spam the syscall on one thread
0:23:38.909,0:23:42.869
which is pretty hard to do in ROP, but I[br]just call threadCreate. We've appointed
0:23:42.869,0:23:46.970
two long jump and then pivot the stack to[br]the application and then in the other
0:23:46.970,0:23:52.930
thread I spray Mach messages with[br]MACH_PORT_NULL in it. And the thing with
0:23:52.930,0:23:58.590
Mach messages is they can be used to do[br]inter-process communication and you can
0:23:58.590,0:24:03.350
also transfer port rights from one process[br]to another. So in this case we just send
0:24:03.350,0:24:07.709
an empty port, but you could also place[br]something else there and that will create
0:24:07.709,0:24:14.289
a structure and kalloc.16 containing zero[br]at that location. And and then if the Mach
0:24:14.289,0:24:18.690
message gets freed, we can replace it with[br]something and basically point it to
0:24:18.690,0:24:22.859
somewhere in kernel where fake port[br]structure lives. And when we receive the
0:24:22.859,0:24:26.809
Mach message again, it will basically[br]think that this is a real port and treat
0:24:26.809,0:24:32.600
it as such. And with that we can create a[br]fake kernel task port. But for that we
0:24:32.600,0:24:38.409
obviously need to replace it and we need[br]to heap spray. And most commonly iOS
0:24:38.409,0:24:43.519
surface is used for that as a kernel[br]extension, but because of our sandbox we
0:24:43.519,0:24:48.419
are so limited that we don't have iOS[br]surface access. So the question is how we
0:24:48.419,0:24:54.310
actually spray and the[br]rootDomainUserClient comes to rescue with
0:24:54.310,0:25:01.179
a memory leak. So actually this function[br]secureSleepSystemOptions is reachable from
0:25:01.179,0:25:07.020
the raccoon sandbox and Apple has a way of[br]basically passing data to the kernel via
0:25:07.020,0:25:12.850
XML. So a userland application can just[br]pass the XML to the kernel and then they
0:25:12.850,0:25:18.039
will use this OSUnserializeXML function to[br]turn the XML back into C++ objects which
0:25:18.039,0:25:21.789
the kernel can then use. And if this[br]sounds dangerous to you, it actually is.
0:25:21.789,0:25:27.549
There were a few bugs in that. But in this[br]case we basically this just makes sure
0:25:27.549,0:25:33.309
with the OSDynamicCast that the data the[br]user mode application supplied isn't
0:25:33.309,0:25:38.499
always dictionaries so that it can use it[br]afterwards. And the problem here is that
0:25:38.499,0:25:44.130
we can basically just OSDataObject or an[br]always OSArray. So this OSDynamicCast
0:25:44.130,0:25:48.899
will fail and serialized options will[br]become null. But the original point of
0:25:48.899,0:25:55.570
return from OS under the XML will get lost[br]and so we will leak that memory and we
0:25:55.570,0:26:02.010
can just use this for spraying. So then[br]about those two primitives, I will use to
0:26:02.010,0:26:06.860
basically exploitation. The case law[br]weakness, Darrell Justice CCL buffers and
0:26:06.860,0:26:10.980
they are located in the kernel data region[br]and because of that they are stacked with
0:26:10.980,0:26:17.080
the same slide as the kernel text region.[br]And this means that as long as we know the
0:26:17.080,0:26:21.159
kernel slide we already do, that from the[br]case of largely and we can control the
0:26:21.159,0:26:27.659
contents of a CCL buffer and we can get[br]data to a known address and we can easily
0:26:27.659,0:26:33.010
do that with racoon because it runs US[br]route. And so we can just switch all of
0:26:33.010,0:26:37.320
this of CCL buffer, for example, place the[br]same point structure there will be later.
0:26:37.320,0:26:44.369
Also, place a fake trust constructively,[br]but I will get into that. So yeah, now we
0:26:44.369,0:26:49.869
can use that primitive to basically spray[br]tan or state objects pointing to the CCL
0:26:49.869,0:26:53.739
buffer. And then we just received the[br]message again and check if the polls are
0:26:53.739,0:27:00.120
now. And if that's the case, we best place[br]the structure. And then for the non SMP
0:27:00.120,0:27:04.830
version we can even get the case load by[br]traversing a few pointers. But that's not
0:27:04.830,0:27:10.710
needed for SMP Version because there we[br]already got it with the case logic but
0:27:10.710,0:27:15.039
young, Non CCL IP devices, we also don't[br]need a CCL buffers because we can just
0:27:15.039,0:27:20.110
place the fake port structure and use the[br]land and then we get the kernel slide this
0:27:20.110,0:27:24.090
way. And with the kernel slide and this[br]fake port, we can create a fake user
0:27:24.090,0:27:28.789
client and from there we can create a[br]called primitive and then we can use this
0:27:28.789,0:27:33.539
to override that pierce's trusses pointer[br]and pointed to a buffer with two hashes
0:27:33.539,0:27:38.179
and four stage 3 and stage 4. So basically[br]Apple has two ways of doing code signing.
0:27:38.179,0:27:42.749
Either it has a used land daemon that[br]verifies third party applications or a
0:27:42.749,0:27:48.110
test, so-called trust cache, which is a[br]list of hashes from all of their system
0:27:48.110,0:27:54.279
applications. And as soon as the process[br]spawned or dynamic link library is loaded
0:27:54.279,0:27:59.309
and they will basically first verify if[br]the hash of that file is inside of the
0:27:59.309,0:28:02.800
trust cache. And if so, they will just[br]trust a binary blindly because it comes
0:28:02.800,0:28:07.309
from Apple, basically. And now when we[br]override this trust cache point and
0:28:07.309,0:28:13.769
pointed to our buffer, we can basically[br]place the hash of stage 3 and 4 there. And
0:28:13.769,0:28:20.000
then the system will think those are apple[br]binaries and we can just load them. So.
0:28:20.000,0:28:24.470
Yeah. And for that we need to use a geter[br]open. We can't use the real deal open
0:28:24.470,0:28:29.779
because that uses malock. So we just[br]open stage 3 to get a file descriptor.
0:28:29.779,0:28:34.440
Then we attached a signature which now[br]succeeds as the caches and trust cache and
0:28:34.440,0:28:39.559
then we can map it as read executed and[br]jump there. And then we are after two
0:28:39.559,0:28:46.099
months of writing options. We are finally[br]in C and we can write code more easily.
0:28:46.099,0:28:51.229
And the problem there is that we still[br]don't have a working cache. So we are
0:28:51.229,0:28:57.220
still limited to the basic functionality[br]and because of the ghetto dlopen link is
0:28:57.220,0:29:02.599
obviously not working. So we just rely on[br]raw as somebody follows syscalls. And I
0:29:02.599,0:29:07.910
also pass some function pointers which I[br]already use for stage 2. So for example,
0:29:07.910,0:29:13.700
open and a map to stage 3. And from there[br]we remove the con task and session into
0:29:13.700,0:29:20.759
our special port 4 so that other user mode[br]applications can use it. And then we can
0:29:20.759,0:29:24.510
basically escape the sandbox by removing[br]the sandbox label in the process
0:29:24.510,0:29:30.700
structure, so that we can launch a new[br]binary, because otherwise the raccoons and
0:29:30.700,0:29:35.350
bugs doesn't allow it. But in the kernel,[br]those process structures basically have
0:29:35.350,0:29:39.009
this label, which tells the kernel of[br]which sandbox to use. And as you're doing
0:29:39.009,0:29:45.539
it, you can just tell it to not use any[br]samples. And and then we can launch stage
0:29:45.539,0:29:49.799
3, 4 and with that, get a working[br]cashback. And that's the big advantage
0:29:49.799,0:29:55.359
from like having a separate file. We now[br]have the full cache functions working and
0:29:55.359,0:30:01.480
can do work more easily. And then I it's[br]just called two opposing spawn and then a
0:30:01.480,0:30:06.449
raw exit. This to exit the daemon without[br]crashing because of launch. You would see
0:30:06.449,0:30:10.610
that one of the launch demons crashed and[br]the specifics flag inside it would try to
0:30:10.610,0:30:14.340
restart it. And then our option would run[br]again. We obviously want to prevent that.
0:30:14.340,0:30:21.449
So we use the exits as call to exit it.[br]And then we are in stage 4. And from my
0:30:21.449,0:30:25.780
side, that was just basically to block or[br]signal. So we don't get killed by launchd.
0:30:25.780,0:30:31.110
Because when launchd the launch and[br]exits, it will send the kill to all this
0:30:31.110,0:30:35.520
child process. And I need to catch that.[br]Otherwise Stage 4 would get killed. And
0:30:35.520,0:30:40.250
then I just called the Post Exploitation[br]Framework, which was written by Sparky.
0:30:40.250,0:30:45.200
And basically that does the following. It[br]first elevates the process to root with
0:30:45.200,0:30:50.220
current credentials, then it performs a[br]remount of the root file system because on
0:30:50.220,0:30:56.249
stock IOS, a file system was mounted as[br]read only and we obviously need to
0:30:56.249,0:31:01.470
mount it as read/write to modify some files[br]on there and then set non it sets the
0:31:01.470,0:31:06.589
nonce so that the user might be able to[br]downgrade to an older version if they are
0:31:06.589,0:31:12.099
flops. Verifies that the bootstrap was in[br]place from the installation. And then
0:31:12.099,0:31:17.450
checked substrates or the framework that's[br]used for them to perform tweak injection
0:31:17.450,0:31:23.599
and it's plugging into trust us and starts[br]them so that they can start to inject into
0:31:23.599,0:31:27.870
newly spawn processes. Then it's Ponce or[br]the launch demons associate with the
0:31:27.870,0:31:33.520
jailbreak and unloads or own demons so[br]that we don't respawned by extended
0:31:33.520,0:31:38.809
run. They kernel exploit again and then[br]performs an LUV start to basically restart
0:31:38.809,0:31:43.649
all of the launch teams off the system so[br]that subset can inject 3 STEM. And with
0:31:43.649,0:31:49.009
that the system is faulty, jail broken and[br]we can perform a few cleanup steps. But
0:31:49.009,0:31:52.750
yeah, basically the end user has[br]jailebroken system. Then as a little
0:31:52.750,0:31:58.240
side note while we are testing all the[br]demons, we got killed by jetsam a lot. So
0:31:58.240,0:32:02.769
basically jetsam is the kernel[br]extension from Apple. That is therefore
0:32:02.769,0:32:08.349
memory management. And they basically want[br]to make sure the user mode application
0:32:08.349,0:32:12.909
doesn't use too much memory because they[br]don't have that much on iPhone. And on all
0:32:12.909,0:32:19.019
the iPhones, actually. So there is this[br]list and jetsam, it jetsam seems easier to
0:32:19.019,0:32:24.909
use than process uses more than read and[br]should use. It would just kill it. So we
0:32:24.909,0:32:29.500
changed the values in the plist under[br]LaunchDaemons to actually allow the
0:32:29.500,0:32:33.820
LaunchDaemons to use more memory. But [br]the weird thing about this is that this
0:32:33.820,0:32:39.479
actually got us accepted by jetsam and we[br]had normal crashes while Apple actually
0:32:39.479,0:32:46.719
tried to mitigate that beforehand. So[br]because jailbreak is always modify those
0:32:46.719,0:32:50.359
configuration files on the LaunchDaemons,[br]they start to move all of them into a
0:32:50.359,0:32:54.489
dynamic library to guard them under code[br]signing. So the jailbreak just couldn't
0:32:54.489,0:32:59.330
change them anymore. But when you tried to[br]figure out the Launchdemon at the Launch
0:32:59.330,0:33:06.031
Demons, we dumped the dylib and ahm[br]there was also plist embedded for
0:33:06.031,0:33:12.429
Jetsam, but Apple was still using those[br]files on disk. So I really want to look
0:33:12.429,0:33:16.299
further into this because it seems like[br]Apple isn't always ignoring those
0:33:16.299,0:33:22.409
configurations files on disk. And then[br]thanks to the whole team. Siguza, Sparkey,
0:33:22.409,0:33:27.739
and Stek for bouncing ideas back and[br]forth and writing the many part of the
0:33:27.739,0:33:35.299
jailbreak. Then for Pod2g, Synacktiv for[br]the kernel bugs. And basically also a big
0:33:35.299,0:33:39.519
thanks to Saurik for substrate and the[br]whole jailbreaking framework and for
0:33:39.519,0:33:43.240
Swaggo, parrorgeek and Samg_is_a_Ninja[br]for testing a few things and keeping
0:33:43.240,0:33:49.359
motivated. And for Jonathan Levin for his[br]books basically because he bought a few
0:33:49.359,0:33:54.720
awesome books about IOS and that got me[br]into it two years ago. And yeah. And in
0:33:54.720,0:33:59.919
the future, I think exploiting kernel[br]vulnerability with other cache functions
0:33:59.919,0:34:05.400
and owning ROP really is a pain and that[br]probably won't do it again. Because he's
0:34:05.400,0:34:12.770
spent most of that. But yeah, the other[br]big problem now is that with a 12 so the
0:34:12.770,0:34:18.850
new iPhones pack. So point authentication[br]kills most of these types of exploits
0:34:18.850,0:34:23.419
because the problem there is that you[br]would now need an ASLA bypass and the pack
0:34:23.419,0:34:30.130
bypass to get into return oriented[br]programing. And it's pretty unlikely to
0:34:30.130,0:34:36.690
basically have both. And because Pegg[br]bypasses are really rare and yet I only
0:34:36.690,0:34:43.380
know about this one is a LA bypass. So you[br]would have to get pretty lucky. Also un-
0:34:43.380,0:34:48.460
tethering gets progressively harder. Apple[br]just fixed another good idea ahead in iOS
0:34:48.460,0:34:55.640
13.1. Basically the idea was to use printf[br]with the format string format modify
0:34:55.640,0:35:01.170
'%n' to get a Turing complete[br]machine because printf, this modifier
0:35:01.170,0:35:08.170
is basic Turing complete and then you[br]start to develop a pack bypass basically
0:35:08.170,0:35:14.130
and get them to ROP. But now we're in IS[br]13.1. I think Apple actually removed the
0:35:14.130,0:35:19.830
'%n' modifier, so you can no[br]longer do this. And yeah. So this idea is
0:35:19.830,0:35:24.730
also gone. And yeah. In the end, I was[br]able to complete my pipe dream, so I guess
0:35:24.730,0:35:31.160
I will need a new one. So watch out, Apple[br]and that spice. Are there any questions?
0:35:31.160,0:35:41.090
Applause
0:35:41.090,0:35:45.400
Herald: Thank you, littlelailo for is[br]fantastic work. I suppose we're going to
0:35:45.400,0:35:49.790
hear more from you in the future.[br]Littlelailo: Maybe.
0:35:49.790,0:35:54.940
Herald: Are there questions here in[br]this audience. No one who wants to hire
0:35:54.940,0:36:01.650
this guy now right away. No one. No one.[br]Can you describe to me what change
0:36:01.650,0:36:09.190
actually do times in these, you know, all[br]the ASICs? Oh, yeah. Oh, yes. versions.
0:36:09.190,0:36:11.500
Littlelailo: Well what they change to[br]make.
0:36:11.500,0:36:17.030
Herald: Yeah. What. Plus, you know, I told[br]you like I started the tethering challenge
0:36:17.030,0:36:20.360
actually at 5.1.[br]Littlelailo: Well, they added a lot new
0:36:20.360,0:36:25.930
mitigations and also obviously pitched a[br]few bugs like for example, those ASLR
0:36:25.930,0:36:31.620
bypasses that posterity used in Corona[br]got patched. And this one also now got
0:36:31.620,0:36:37.660
patched by accident. But yeah, I mean like[br]some bugs are still there. For example,
0:36:37.660,0:36:42.120
the Bug in racoon and the conflict pass.[br]The bug is still an all day. But yeah, I
0:36:42.120,0:36:47.840
don't really care about it. And the kernel[br]bugs got patched by Apple. But for
0:36:47.840,0:36:52.590
example, this synthetic one, they also[br]also patched wrong by accident. And now it
0:36:52.590,0:36:55.800
always leaked the strike. But I think they[br]also fixed that now.
0:36:55.800,0:36:59.740
Herald: Your team, you you're mentioning[br]your team. You're working not on
0:36:59.740,0:37:01.740
your own, of course.[br]Littlelailo: No.
0:37:01.740,0:37:05.820
Herald: And how would you restructured?[br]How are the roles divided? How was...
0:37:05.820,0:37:10.740
Littlelailo: Well, we are just like four[br]people. So and we have this group chat and
0:37:10.740,0:37:14.860
then we are just hanging out there and[br]bouncing ideas back and forth and maybe
0:37:14.860,0:37:17.640
working on some stuff.[br]Herald: A close contact with the Apple
0:37:17.640,0:37:22.280
developers.[br]Littlelailo: No, not at all.
0:37:22.280,0:37:25.870
Herald: More.[br]Littlelailo: I mean, I reported one bug to
0:37:25.870,0:37:31.890
their bounty once or like actually says to[br]them that their bounty and that one got
0:37:31.890,0:37:40.770
fixed and it was all fine. But yeah, for[br]now, I don't report bugs at the moment.
0:37:40.770,0:37:44.020
Herald: If you have time, you've time left[br]now actually, you're looking for a new
0:37:44.020,0:37:46.560
project doesn't it?[br]Littlelailo: Yeah. Yeah. And I might
0:37:46.560,0:37:50.290
report some of those bugs in the meantime[br]but I mean with the presentation they know
0:37:50.290,0:37:54.430
about them though so they might fix them.[br]Herald: They will be listening now and at
0:37:54.430,0:37:57.220
least probably I hope.[br]Littlelailo: Yes.
0:37:57.220,0:38:02.460
Herald: Is there anyone who has really,[br]you where sitting on a question here. None
0:38:02.460,0:38:10.360
of you? It's already noon. You know, noon[br]passed, so could be that none of you. You
0:38:10.360,0:38:15.260
can ask them something, maybe someone[br]asked them something, maybe they can help
0:38:15.260,0:38:18.490
you out with certain challenges that are[br]there.
0:38:18.490,0:38:23.660
Littlelailo: Well, I don't really have a[br]question either. Laughter I have my own
0:38:23.660,0:38:29.760
research project now. Well, like, I do[br]stuff at the moment and look at other
0:38:29.760,0:38:34.880
things. For example, to the bootrom[br]exploit came out now. And so I started
0:38:34.880,0:38:40.600
developing on the chick team with them.[br]And that's what I currently do, basically.
0:38:40.600,0:38:45.450
Herald: You're great, man. Littlelailo,[br]thank you. Giving him a warm applause!
0:38:45.450,0:38:48.380
Applause
0:38:48.380,0:38:51.936
36c3 Postroll music
0:38:51.936,0:39:15.000
Subtitles created by c3subtitles.de[br]in the year 2020. Join, and help us!