< Return to Video

36C3 - Tales of old: untethering iOS 11

  • 0:00 - 0:20
    36c3 preroll
  • 0:20 - 0:26
    Herald: To everybody here, please be
    welcome to this fantastic kid here. I
  • 0:26 - 0:31
    learned a lot from him, even though he's
    only since two years playing around with
  • 0:31 - 0:37
    iOS. I mentioned as well the first
    untethering case that I had with my phone
  • 0:37 - 0:44
    was something about iOS 5.1 and with every
    update, you had to do the whole shebang
  • 0:44 - 0:51
    again. Of course, that's what I remember.
    So please give a warm welcome here too
  • 0:51 - 0:54
    littlelailo. Do I spell this correctly?
    Offscreen: Yeah.
  • 0:54 - 1:03
    Herald: Yes, littlelailo is really a very
    fascinating geek. He actually hacks any
  • 1:03 - 1:09
    kind of OS device, to my opinion. Any kind
    of. Yeah. So throw it to him and it comes
  • 1:09 - 1:14
    back in pieces. He's as sharp as a knife.
    Please give him a warm, well, welcoming
  • 1:14 - 1:18
    applause. We're gonna untether iOS. Yay!
  • 1:18 - 1:24
    applause
  • 1:24 - 1:28
    littlelailo: Okay, so yeah, I'm
    littlelailo, as already introduced. I'm
  • 1:28 - 1:34
    interested in I.T. security in general.
    And I started to look into iOS about two
  • 1:34 - 1:41
    years ago and then I met three awesome
    guys: @s1guza, @stek29 and @iBSparkes. And
  • 1:41 - 1:47
    we basically started to chat a lot and in
    the end also released the "jailbreak me"
  • 1:47 - 1:51
    for iOS 10 on https://totally-
    not.spyware.lol/. For everybody who isn't
  • 1:51 - 1:55
    familiar with the jailbreak scene, it's
    like rooting on Android. So it basically
  • 1:55 - 2:02
    is a tool that gives the user the
    capability to tweak the device, and that's
  • 2:02 - 2:06
    mostly done by basically installing a
    jailbreak in this case. You just go to a
  • 2:06 - 2:08
    website on the slide there
    [https://totally-not.spyware.lol/] and it
  • 2:08 - 2:12
    will install a package manager on your
    phone and then the end user can install
  • 2:12 - 2:18
    tweaks. So just a little dynamic libraries
    and they get injected into all the other
  • 2:18 - 2:22
    system processes and then they can
    obviously modify their memory. And that,
  • 2:22 - 2:26
    for example, allows customization of the
    home screen or something like that. And
  • 2:26 - 2:32
    basically with the release, we also formed
    the team @Jakeblair420 was created with a
  • 2:32 - 2:35
    Twitter account. There are also a few
    demos there if you want to check it out
  • 2:35 - 2:40
    after the talk. And I basically had this
    pipe dream that in my life. I really want
  • 2:40 - 2:45
    to achieve an untethered jailbreak using
    only some sort of plist or other save file
  • 2:45 - 2:50
    corruption. So basically there are
    different kinds of jailbreaks. In modern
  • 2:50 - 2:54
    jailbreak the most common kind are semi-
    untethered, which means that the users has
  • 2:54 - 3:00
    to connect their device to a PC when they
    first install a jailbreak and then they
  • 3:00 - 3:04
    sideload an app onto the device or it just
    installs itself there. And then on each
  • 3:04 - 3:09
    reboot, the user has to go into the app
    and press a button to jailbreak the phone.
  • 3:09 - 3:14
    And basically, after they reboot, they
    lose the jailbreak and have to go through
  • 3:14 - 3:20
    this process again. And with an untethered
    jailbreak, the jailbreak gains persistent
  • 3:20 - 3:24
    installation and then they will
    automatically jailbreak the device on each
  • 3:24 - 3:32
    boot. So the device automatically boots
    jailbroke. The untether can be divided
  • 3:32 - 3:37
    into four stages. The first stage is when the
    config file or database stage. I would go
  • 3:37 - 3:42
    over how we gain execution of the boot.
    Then the config parser bug that gives us
  • 3:42 - 3:48
    the right opey primitive. And then I will
    talk about the main bug, the ASLR bypass,
  • 3:48 - 3:53
    which allows us to get into ROP. Then
    stage two is basically a kernel exploit
  • 3:53 - 3:58
    and completely written in ROP and there I
    will go over the two main kernel bugs: the
  • 3:58 - 4:04
    kASLR leak and then the racy double free
    we used to get the kernel read/write, and
  • 4:04 - 4:10
    I will also talk about the kASLR weakness
    and RootDomain user client memory leak
  • 4:10 - 4:14
    which aided us in exploitation. And after
    we get kernel read/write, we can remove a
  • 4:14 - 4:20
    few restrictions from Apple. Mainly we can
    bypass code signing and that gives us the
  • 4:20 - 4:24
    ability to load an unsigned dynamic
    library into our process. So now in stage
  • 4:24 - 4:30
    3, we are basically C, we are dynamic
    library and there we are stashing the
  • 4:30 - 4:37
    kernel task port to host special port 4.
    So iOS has this concept of ports and task
  • 4:37 - 4:42
    ports. And basically, if you have a send
    right to a task port of a process, you can
  • 4:42 - 4:48
    read and write its memory. They're most
    used for inter process communication. And
  • 4:48 - 4:53
    where modern jailbreaks used to stash the
    kernel task port into a special port 4 so
  • 4:53 - 4:58
    that other user mode applications can then
    retrieve it and with that retrieved and
  • 4:58 - 5:02
    with send right they can just read and
    write process memory. And after that I
  • 5:02 - 5:08
    just fix up everything and then spawn
    stage four and that's basically running in
  • 5:08 - 5:13
    a separate process. That has a few
    advantages I will go into later. It's
  • 5:13 - 5:16
    basically performing the whole post
    exploitation process, including launching
  • 5:16 - 5:20
    substrate - the framework that's used for
    tweek injection and then performing
  • 5:20 - 5:24
    ldrestart to restart or launch demons on
    the system and inject the tweaks into
  • 5:24 - 5:31
    them. In stage 1, we first need to get
    execution after boot, and when iOS boots
  • 5:31 - 5:36
    launchd is the first process that's
    running on the system and it then loads a
  • 5:36 - 5:42
    dynamic library with a list of executables
    - that are LaunchDaemons - and there are a
  • 5:42 - 5:47
    few flags associated with them. If the
    "run and load" flag is set, launchd will
  • 5:47 - 5:53
    then spawn all of those LaunchDaemons
    afterwards. And we basically just take one
  • 5:53 - 5:59
    of those launch demons, namely Racoon. So
    what is Racoon? Basically Racoon is part
  • 5:59 - 6:07
    of the ipsec package. And it's a VPN
    client used to interact with an IPSec VPN
  • 6:07 - 6:14
    server. And the problem is though, that
    IPsec-Tools project has been abandoned
  • 6:14 - 6:19
    since 2014. So they officially here state
    on their website that the development of
  • 6:19 - 6:23
    the project has been abandoned and that
    IPsec-Tools has security issues. You
  • 6:23 - 6:29
    should not use it. Apple still decided it
    was a good idea to ship it after 2014 and
  • 6:29 - 6:35
    they now maintain their own fork on
    https://opensource.apple.com . And yeah,
  • 6:35 - 6:39
    basically the only thing that's important
    for the talk is that raccoon has a
  • 6:39 - 6:44
    configuration parser. So on startup it
    will just look for a file on disk and
  • 6:44 - 6:49
    start to parse that. And that's written in
    yacc so completely written in C. So memory
  • 6:49 - 6:54
    corruption might become a problem. And it
    actually is a problem as we can see if the
  • 6:54 - 7:00
    config parser bug and for that when we
    have to go back a bit. Basically in 2011
  • 7:00 - 7:09
    Pod2g released Corona for iOS 5, which was
    also an untethered. He used a bug in
  • 7:09 - 7:16
    Racoon in the configuration parser and
    Pod2g found this one on an old bug tracker
  • 7:16 - 7:21
    and basically the IPsec website had this
    bug tracker there. There users could just
  • 7:21 - 7:27
    report problems with their programs and
    one user in 2009 I think reported a bug
  • 7:27 - 7:32
    that if he would use the specific config
    Racoon would just segfault, but nobody
  • 7:32 - 7:37
    looked at this for two years. So in 2011
    Pod2g realized that this was actually
  • 7:37 - 7:41
    exploitable and then used in a jailbreak.
    And after that, obviously Apple got aware
  • 7:41 - 7:46
    of it. And then they also said that they
    fixed it. And it's got a CV, assigned
  • 7:46 - 7:54
    CVS-2012-2737. And yeah, they say that
    this was improved through improved bounds
  • 7:54 - 7:59
    checking. So let's look at the patch.
    That's the function before the patch and
  • 7:59 - 8:03
    that's the function after the patch. And
    if this was too fast for you, here's the
  • 8:03 - 8:08
    diff. So basically there is no difference.
    The bug is still there and Apple didn't
  • 8:08 - 8:13
    patch it. What really happened here was
    that basically this is obviously a
  • 8:13 - 8:18
    configuration parser. So there are
    different statements that nearly do the
  • 8:18 - 8:23
    same thing. There are these nbns4
    statements to parse an IP address and
  • 8:23 - 8:27
    there are wins4 statement followed by an
    IP address and both of them have to parse
  • 8:27 - 8:34
    the IP address. So the function for
    parsing that was just copy pasted from
  • 8:34 - 8:41
    another one. And yeah, basically Apple
    fixed the bug in the other function. Which
  • 8:41 - 8:48
    I don't get because basically the engineer
    there had to look at PoC Pod2g provided
  • 8:48 - 8:51
    then they had to realize okay, the bug is
    in there, fix it, then they had to
  • 8:51 - 8:55
    recompile and never tested against the
    original PoC because otherwise Racoon
  • 8:55 - 9:00
    would have crashed again. Yeah, it's
    basically an off by one which allows you
  • 9:00 - 9:06
    to overwrite the index of an array and
    Pod2g gave a talk about this and we can
  • 9:06 - 9:11
    basically reuse his strategy from back in
    2011 to get a write primitive.
  • 9:11 - 9:17
    Again, the one function here at the top.
    You can see the yacc syntax so an addrwins
  • 9:17 - 9:21
    list has to consist of either an addrwins
    statement or addrwins statement followed
  • 9:21 - 9:26
    by a comma, followed by an addrwins list.
    So that's a recursion. There can be
  • 9:26 - 9:31
    multiple addrwins statements in a row. And
    then below that, an addrwins statement is
  • 9:31 - 9:35
    defined as containing an address string.
    And that's just a regex to match an IP
  • 9:35 - 9:39
    version 4 address. And if the parcel finds
    that it would run the code between those
  • 9:39 - 9:44
    two brackets. So they get the pointer to a
    global struct and then they check if this
  • 9:44 - 9:50
    nbns4_index is bigger than MAXWINS and if
    so, they return an error. The problem here
  • 9:50 - 9:55
    is that this is off by 1. It shouldn't be
    checking if it's greater than so, but it
  • 9:55 - 9:58
    should be a check for greater or equal
    than MAXWINS. And after that, they use
  • 9:58 - 10:03
    thisinet_pton function to basically parse
    the address, which is in this variable
  • 10:03 - 10:10
    into the nbns4 array at the nbns4 index
    and then post-increment the index. And
  • 10:10 - 10:14
    that will also be coming in handy when
    exploiting this bug. So yeah. How is
  • 10:14 - 10:19
    exploitation done? Basically there are
    here the globals, there is this Lcconfig
  • 10:19 - 10:24
    pointer in the globals pointing to a heap
    structure and that the parser also uses.
  • 10:24 - 10:30
    And then there is this dns4 array followed
    by the dns_area_index and then the nbns4
  • 10:30 - 10:35
    array followed by the nbns_4_array_index
    so we can just use mode_cfg statement
  • 10:35 - 10:39
    followed by this wins4 statement with the
    IP address to parse the IP address in the
  • 10:39 - 10:45
    first array element and we can repeat this
    process four times to then move the index
  • 10:45 - 10:50
    out of bounds. So now it's pointing to
    itself and the cool thing is about that is
  • 10:50 - 10:54
    that therefore there would be a good idea
    to use integers for those area indexes
  • 10:54 - 10:59
    instead of unsigned integers so we can
    just point them... you just use negative
  • 10:59 - 11:04
    numbers to bypass the bounds check. So
    we'll overwrite it with -1 to point it to
  • 11:04 - 11:09
    an dns_array_index and then we use another
    wins statement to override the
  • 11:09 - 11:13
    dns_array_index with a negative number.
    And the cool thing about this is that
  • 11:13 - 11:16
    because of the post-incrementation, it
    will move it back to zero so we can repeat
  • 11:16 - 11:21
    this process as often times as we want,
    and we will use a negative number to
  • 11:21 - 11:25
    basically point it to the Lcconfig
    pointer. And then we can use two dns4
  • 11:25 - 11:29
    statements to override the Lcconfig
    pointer and point it anywhere in memory.
  • 11:29 - 11:35
    And after that we can use a timer
    statement with a counter. Then the
  • 11:35 - 11:40
    parsible just tried to write it to the
    Lcconfig structure so it will de-reference
  • 11:40 - 11:44
    our pointer and write it there and the
    interval statement will basically write to
  • 11:44 - 11:50
    an uint32, which is followed by the uint
    where the counter statement is written so
  • 11:50 - 11:54
    we can turn this into a 64 bit write
    anywhere primitive and write anywhere in
  • 11:54 - 12:00
    process memory and that gives us an
    ability to basically exploit the ASLR
  • 12:00 - 12:05
    bypass. So this is the main bug of the
    whole untethered and allows us to
  • 12:05 - 12:10
    basically have this primitive. And it's
    also the reason why I decided to give this
  • 12:10 - 12:15
    talk this year and not last year, because
    there the bug was pretty fresh and only
  • 12:15 - 12:21
    patched in iOS 12 and it gives to an
    attacker the ability to exploit zero click
  • 12:21 - 12:24
    more easily because they just need the
    write anywhere primitive. And that's why I
  • 12:24 - 12:30
    held back on it. Siguza found it after
    looking into Apple's patches of the Corona
  • 12:30 - 12:35
    ASLR bypass and basically from Pod2g's
    presentation, we as a team learned that
  • 12:35 - 12:40
    ASLR is always bypassed when a writable
    region is larger than the maximum slide,
  • 12:40 - 12:45
    because then there's always a writable
    address in process memory and you can use
  • 12:45 - 12:51
    this to brute force the slide and Siguza
    found out that there was what was also the
  • 12:51 - 12:57
    same problem with the cache in iOS 11 and
    a few versions of iOS 10 actually. So
  • 12:57 - 13:02
    what's the cache? Basically, the cache is
    a pre compiled blob containing the dynamic
  • 13:02 - 13:08
    link libraries from Apple. So on each
    released, you just move all of those into
  • 13:08 - 13:13
    one big file to optimize load times of
    apps and keep memory pressure low. So it's
  • 13:13 - 13:19
    a pretty big file. And for that on boot
    the kernel needs to calculate a slide and
  • 13:19 - 13:24
    slide it in memory. But they only slide it
    once on boot because it's used in every
  • 13:24 - 13:30
    process. And so Apple defines an area of
    memory for the cache with a base address
  • 13:30 - 13:36
    and the size and that's hardcoded on iOS
    11. The cache got so big that a maximum
  • 13:36 - 13:43
    slide is now smaller than its data
    segment. So the conditioning is satisfied
  • 13:43 - 13:48
    and ASLR can be bypassed, basically. And
    the weird thing about this is that
  • 13:48 - 13:54
    actually the same thing also happened in
    iOS 7. Back then, the size was defined as
  • 13:54 - 13:58
    500 megabytes and the cache got bigger
    than 500 megabytes. But before that, it
  • 13:58 - 14:03
    was also in a few versions possible to
    have the same condition that the data
  • 14:03 - 14:07
    segment again was bigger than the maximum
    slide. And Apple was actually also aware
  • 14:07 - 14:11
    of this because Acer talked about it
    in one of his talks, but they just
  • 14:11 - 14:16
    increased the maximum slide to 1 gigabyte
    and didn't edit any asserts. And now in
  • 14:16 - 14:22
    iOS 11, it again got so big that the same
    thing happened. And we believe that they
  • 14:22 - 14:27
    didn't even really notice that up until
    iOS 12 where the cache was now bigger than
  • 14:27 - 14:31
    1 gigabyte so the kernel just couldn't fit
    it in the memory region and paniced. And
  • 14:31 - 14:35
    because of that they just thought well,
    then we will increase it to 4 gigabyte
  • 14:35 - 14:41
    while developing so we might see this
    reoccurring in I don't know, iOS 15 or
  • 14:41 - 14:47
    something like that. Exploitation is also
    pretty simple. We can just brute force the
  • 14:47 - 14:52
    slide now and there are a lot of function
    pointers in the data segment. So we
  • 14:52 - 14:59
    decided to go with __platform_mem_move
    because we can reach it via strlcpy from
  • 14:59 - 15:03
    the conflict parser. And the basic
    strategy behind this is we write a slid
  • 15:03 - 15:09
    rop chain into an address we can always
    write to for a specific slide abc and then
  • 15:09 - 15:13
    we overwrite the __platform_mem_move
    pointer at its unslid address plus the
  • 15:13 - 15:20
    slide we just chose with a gadget that
    would pivot to our slid rop chain and then
  • 15:20 - 15:26
    we call strlcpy. And now there are
    basically two possibilities. Either we
  • 15:26 - 15:30
    guessed the right slide and then we jump
    to our gadget and pivot the stack or we
  • 15:30 - 15:34
    guessed the wrong slide. But obviously
    nothing happened because we can just write
  • 15:34 - 15:39
    there so we can go back to step one and
    try again. And then with that we can just
  • 15:39 - 15:42
    brute force the slide till we get the
    right one. The catch with this, however,
  • 15:42 - 15:46
    is that the write what were is pretty slow
    and it also needs a lot of storage. So for
  • 15:46 - 15:53
    a 64 bit write I need about 120 bytes in
    the config file. And because there are so
  • 15:53 - 16:02
    many possible slide values, the chain I
    have currently is around... it's only to
  • 16:02 - 16:07
    rop gadgets, but the config file is
    already 12 megabytes, I think. And because
  • 16:07 - 16:11
    of that, it takes around 10 seconds to run
    if it's a really bad slide. So the rop
  • 16:11 - 16:16
    chain has to be as short as possible. And
    the other problem is that if we have a bad
  • 16:16 - 16:20
    slide, we will basically smash the whole
    data segment and we can't really recover
  • 16:20 - 16:24
    from that. So we had crashes in malloc and
    stuff like that while developing,
  • 16:24 - 16:28
    basically. There are some solutions to
    that, we can have a really short rop chain
  • 16:28 - 16:34
    in stage one and we can achieve this by
    basically just opening the big cache file
  • 16:34 - 16:39
    to get a file descriptor to it and then we
    can map it at a static address and then
  • 16:39 - 16:43
    get many gadgets there because the file is
    obviously code signed by Apple so we can
  • 16:43 - 16:48
    just jump there after mapping it. But the
    problem with that is that nothing is set
  • 16:48 - 16:53
    up. So malloc and other functions aren't
    working. But as I said earlier, the
  • 16:53 - 16:58
    current cache has a smashed data segment
    so we don't really lose anything. And
  • 16:58 - 17:02
    after having the cache at a static address
    we can use the open and mmap functions
  • 17:02 - 17:06
    from that cache to basically map stage two
    into the process memory and stage two is
  • 17:06 - 17:16
    just a really big rop stack. So, yeah. And
    then we are basically in ROP, but we can
  • 17:16 - 17:21
    only use raw syscalls and not much more
    because everything else will either use a
  • 17:21 - 17:26
    function that uses malloc or will use
    malloc on it's own. And the other problem
  • 17:26 - 17:30
    is that basically errno is also broken. So
    every syscall which fails will crush the
  • 17:30 - 17:35
    binary. So the first thing I do is
    basically map a few pages so that errno
  • 17:35 - 17:41
    works again because we have a few syscalls
    that might fail. And then the other big
  • 17:41 - 17:45
    problem is that we now need to implement
    the whole kernel exploit in rop. So I
  • 17:45 - 17:50
    start to write a framework around this
    awesome gadget which is present in all the
  • 17:50 - 17:54
    cache versions I looked at. Basically at the
    top you can see that all the high
  • 17:54 - 18:01
    registers are moved into x0 through x7. So
    all the registers used for the calling
  • 18:01 - 18:08
    convention are and then we branch with
    link to register based on x27. So also
  • 18:08 - 18:12
    high register. And after that we load all
    the register values back from the stack so
  • 18:12 - 18:17
    we can just chain those gadgets together
    to call any functions. You basically rop
  • 18:17 - 18:22
    into the lower half and then chain another
    one of those afterwards and then they can
  • 18:22 - 18:27
    load all the values, call the function and
    then load all the values from the stack
  • 18:27 - 18:32
    again. And that's how the whole rop chain
    works. So yeah, I also used another gadget
  • 18:32 - 18:37
    to basically add two registers together
    and another one which stores x0 so that I
  • 18:37 - 18:43
    can store the return value on the stack
    and later reuse it. And for loops I use a
  • 18:43 - 18:47
    gadget which just returns if x0 is zero.
    So it's basically just a read instruction
  • 18:47 - 18:51
    then and otherwise it will tail call a
    function using function pointer from the
  • 18:51 - 18:55
    data segment. And because I can control
    the whole data segment, I can just put a
  • 18:55 - 19:00
    function pointer there that that will then
    jump to an epilog and misasligning the stack
  • 19:00 - 19:05
    with that. And then I can call longjmp
    with two different register values and
  • 19:05 - 19:10
    because of that pivot the stack to another
    location. And when we basically didn't
  • 19:10 - 19:16
    jump outside of the loop, I just mmap the
    part of stage 2 which has the loop in it
  • 19:16 - 19:23
    back over old stack again. And then I can
    just reuse to stack every time. And then
  • 19:23 - 19:28
    pivot up using longjup. The problem wih
    this, however, is obviously that it's
  • 19:28 - 19:34
    pretty slow because I use mmap as a
    syscall. But this can be solved by just
  • 19:34 - 19:39
    unrolling your loops like for 10
    iterations and then mmapping the file so
  • 19:39 - 19:43
    that I get more loop iterations basically
    per second, which is important for the
  • 19:43 - 19:52
    race. So now we will go over the kernel
    bug so the kASLR leak is CVS-2018-4413 by
  • 19:52 - 19:58
    panicall. It was fixed in iOS 12.1 and
    it's luckily reachable from the Racoon
  • 19:58 - 20:03
    sandbox because Apple is sandboxing most
    userland processes and the Racoon sandbox
  • 20:03 - 20:08
    is really tight. We didn't have that much,
    many bugs that would work in Racoon, but
  • 20:08 - 20:15
    luckily this one does because Racoon has
    access to the sysctl functions and this
  • 20:15 - 20:20
    one is in the sysctl_progargsx function.
    The progargsx function basically allocates
  • 20:20 - 20:26
    a page using kmem_alloc. But it doesn't
    zero it so it might contain old heap data
  • 20:26 - 20:31
    and then they copy the process arguments
    to the front of the page. And now if you
  • 20:31 - 20:35
    supply a wrong size from user space and
    there are a few other conditions that have
  • 20:35 - 20:40
    to be met for some weird reason it copies
    from the end of the page into user space
  • 20:40 - 20:45
    which I don't get why does this even get
    by code review. But here's a graphical
  • 20:45 - 20:50
    illustration: there is basically this page
    is full of uninitialized data and then
  • 20:50 - 20:53
    they put the process arguments in the
    front and copy out uniinitialized heap
  • 20:53 - 20:59
    data from the back into user land and we
    can obviously just spray specific heap
  • 20:59 - 21:04
    objects with kernel pointers in them and
    then free them again and use this
  • 21:04 - 21:08
    primitive to maybe leak them. And if we
    find a kernel pointer in there, we can
  • 21:08 - 21:15
    just calculate this kernel slide. So yeah.
    And then we come to the racy double free.
  • 21:15 - 21:20
    As I said, the main problem of the
    untether is the racoon sandbox, so many of
  • 21:20 - 21:25
    the kernel bugs that would work in iOS 11
    from an app, didn't work from racoon. But
  • 21:25 - 21:30
    luckily on Halloween Sparky told me about
    Lightspeed bug from Synacktiv, which is
  • 21:30 - 21:36
    reachable from the sandbox. It's a double
    free in kalloc.16. The kernel allocator's
  • 21:36 - 21:43
    based on zones and with different sizes.
    So there's, for example, kalloc.16 and
  • 21:43 - 21:47
    then all objects in that zone have a size
    of 16 or less bytes and they are obviously
  • 21:47 - 21:55
    also other zones for bigger objects. And
    basically there is a structure in there
  • 21:55 - 22:01
    that then gets doubled. And there is the
    syscall that's handling async file events.
  • 22:01 - 22:04
    So a user mode application can just call
    it and tell the kernel, hey, please write
  • 22:04 - 22:10
    a buffer to a file and then move on with
    execution and it uses a kernel thread to
  • 22:10 - 22:14
    perform those. And for that, that
    basically allocates a structure to contain
  • 22:14 - 22:18
    some metadata like where the buffer is and
    to which file it should write, and the
  • 22:18 - 22:24
    kernel thread obviously has to free the
    structure after it's done because it just
  • 22:24 - 22:28
    gets into the query for
    the kernel thread, that then wakes up and
  • 22:28 - 22:33
    maybe sees it has a new job, then performs
    the operation and then it has to free it,
  • 22:33 - 22:36
    otherwise it will leak. But the problem
    here is that if an error occurred while
  • 22:36 - 22:41
    setting the structure up, the second field
    in the structure will be zero. And then
  • 22:41 - 22:46
    the structure also isn't included into the
    jobs list. So the kernel thread will never
  • 22:46 - 22:51
    wake up and look at it. So the syscall has
    to free it because otherwise it will leak.
  • 22:51 - 22:55
    And the problem here is that we can
    basically reallocate the structure before
  • 22:55 - 22:59
    the syscall checks. So what happens here
    is that the syscall allocates the
  • 22:59 - 23:04
    structure, fills it up, and then it gets
    added to the list and then the kernel
  • 23:04 - 23:10
    thread is so fast that it runs while the
    syscall isn't finished yet and basically
  • 23:10 - 23:18
    gets the gets the job done and frees it.
    And then we can spray heap objects pretty
  • 23:18 - 23:23
    fast to overlay with that structure. And
    then the syscall finishes and checks the
  • 23:23 - 23:28
    field and sees that it's zero because we
    just replaced it with an object that has
  • 23:28 - 23:33
    zero at that location so it frees it again
    leading to a double free. And yeah, we can
  • 23:33 - 23:39
    obviously exploit it. So for exploitation
    I just spam the syscall on one thread
  • 23:39 - 23:43
    which is pretty hard to do in ROP, but I
    just call threadCreate. We've appointed
  • 23:43 - 23:47
    two long jump and then pivot the stack to
    the application and then in the other
  • 23:47 - 23:53
    thread I spray Mach messages with
    MACH_PORT_NULL in it. And the thing with
  • 23:53 - 23:59
    Mach messages is they can be used to do
    inter-process communication and you can
  • 23:59 - 24:03
    also transfer port rights from one process
    to another. So in this case we just send
  • 24:03 - 24:08
    an empty port, but you could also place
    something else there and that will create
  • 24:08 - 24:14
    a structure and kalloc.16 containing zero
    at that location. And and then if the Mach
  • 24:14 - 24:19
    message gets freed, we can replace it with
    something and basically point it to
  • 24:19 - 24:23
    somewhere in kernel where fake port
    structure lives. And when we receive the
  • 24:23 - 24:27
    Mach message again, it will basically
    think that this is a real port and treat
  • 24:27 - 24:33
    it as such. And with that we can create a
    fake kernel task port. But for that we
  • 24:33 - 24:38
    obviously need to replace it and we need
    to heap spray. And most commonly iOS
  • 24:38 - 24:44
    surface is used for that as a kernel
    extension, but because of our sandbox we
  • 24:44 - 24:48
    are so limited that we don't have iOS
    surface access. So the question is how we
  • 24:48 - 24:54
    actually spray and the
    rootDomainUserClient comes to rescue with
  • 24:54 - 25:01
    a memory leak. So actually this function
    secureSleepSystemOptions is reachable from
  • 25:01 - 25:07
    the raccoon sandbox and Apple has a way of
    basically passing data to the kernel via
  • 25:07 - 25:13
    XML. So a userland application can just
    pass the XML to the kernel and then they
  • 25:13 - 25:18
    will use this OSUnserializeXML function to
    turn the XML back into C++ objects which
  • 25:18 - 25:22
    the kernel can then use. And if this
    sounds dangerous to you, it actually is.
  • 25:22 - 25:28
    There were a few bugs in that. But in this
    case we basically this just makes sure
  • 25:28 - 25:33
    with the OSDynamicCast that the data the
    user mode application supplied isn't
  • 25:33 - 25:38
    always dictionaries so that it can use it
    afterwards. And the problem here is that
  • 25:38 - 25:44
    we can basically just OSDataObject or an
    always OSArray. So this OSDynamicCast
  • 25:44 - 25:49
    will fail and serialized options will
    become null. But the original point of
  • 25:49 - 25:56
    return from OS under the XML will get lost
    and so we will leak that memory and we
  • 25:56 - 26:02
    can just use this for spraying. So then
    about those two primitives, I will use to
  • 26:02 - 26:07
    basically exploitation. The case law
    weakness, Darrell Justice CCL buffers and
  • 26:07 - 26:11
    they are located in the kernel data region
    and because of that they are stacked with
  • 26:11 - 26:17
    the same slide as the kernel text region.
    And this means that as long as we know the
  • 26:17 - 26:21
    kernel slide we already do, that from the
    case of largely and we can control the
  • 26:21 - 26:28
    contents of a CCL buffer and we can get
    data to a known address and we can easily
  • 26:28 - 26:33
    do that with racoon because it runs US
    route. And so we can just switch all of
  • 26:33 - 26:37
    this of CCL buffer, for example, place the
    same point structure there will be later.
  • 26:37 - 26:44
    Also, place a fake trust constructively,
    but I will get into that. So yeah, now we
  • 26:44 - 26:50
    can use that primitive to basically spray
    tan or state objects pointing to the CCL
  • 26:50 - 26:54
    buffer. And then we just received the
    message again and check if the polls are
  • 26:54 - 27:00
    now. And if that's the case, we best place
    the structure. And then for the non SMP
  • 27:00 - 27:05
    version we can even get the case load by
    traversing a few pointers. But that's not
  • 27:05 - 27:11
    needed for SMP Version because there we
    already got it with the case logic but
  • 27:11 - 27:15
    young, Non CCL IP devices, we also don't
    need a CCL buffers because we can just
  • 27:15 - 27:20
    place the fake port structure and use the
    land and then we get the kernel slide this
  • 27:20 - 27:24
    way. And with the kernel slide and this
    fake port, we can create a fake user
  • 27:24 - 27:29
    client and from there we can create a
    called primitive and then we can use this
  • 27:29 - 27:34
    to override that pierce's trusses pointer
    and pointed to a buffer with two hashes
  • 27:34 - 27:38
    and four stage 3 and stage 4. So basically
    Apple has two ways of doing code signing.
  • 27:38 - 27:43
    Either it has a used land daemon that
    verifies third party applications or a
  • 27:43 - 27:48
    test, so-called trust cache, which is a
    list of hashes from all of their system
  • 27:48 - 27:54
    applications. And as soon as the process
    spawned or dynamic link library is loaded
  • 27:54 - 27:59
    and they will basically first verify if
    the hash of that file is inside of the
  • 27:59 - 28:03
    trust cache. And if so, they will just
    trust a binary blindly because it comes
  • 28:03 - 28:07
    from Apple, basically. And now when we
    override this trust cache point and
  • 28:07 - 28:14
    pointed to our buffer, we can basically
    place the hash of stage 3 and 4 there. And
  • 28:14 - 28:20
    then the system will think those are apple
    binaries and we can just load them. So.
  • 28:20 - 28:24
    Yeah. And for that we need to use a geter
    open. We can't use the real deal open
  • 28:24 - 28:30
    because that uses malock. So we just
    open stage 3 to get a file descriptor.
  • 28:30 - 28:34
    Then we attached a signature which now
    succeeds as the caches and trust cache and
  • 28:34 - 28:40
    then we can map it as read executed and
    jump there. And then we are after two
  • 28:40 - 28:46
    months of writing options. We are finally
    in C and we can write code more easily.
  • 28:46 - 28:51
    And the problem there is that we still
    don't have a working cache. So we are
  • 28:51 - 28:57
    still limited to the basic functionality
    and because of the ghetto dlopen link is
  • 28:57 - 29:03
    obviously not working. So we just rely on
    raw as somebody follows syscalls. And I
  • 29:03 - 29:08
    also pass some function pointers which I
    already use for stage 2. So for example,
  • 29:08 - 29:14
    open and a map to stage 3. And from there
    we remove the con task and session into
  • 29:14 - 29:21
    our special port 4 so that other user mode
    applications can use it. And then we can
  • 29:21 - 29:25
    basically escape the sandbox by removing
    the sandbox label in the process
  • 29:25 - 29:31
    structure, so that we can launch a new
    binary, because otherwise the raccoons and
  • 29:31 - 29:35
    bugs doesn't allow it. But in the kernel,
    those process structures basically have
  • 29:35 - 29:39
    this label, which tells the kernel of
    which sandbox to use. And as you're doing
  • 29:39 - 29:46
    it, you can just tell it to not use any
    samples. And and then we can launch stage
  • 29:46 - 29:50
    3, 4 and with that, get a working
    cashback. And that's the big advantage
  • 29:50 - 29:55
    from like having a separate file. We now
    have the full cache functions working and
  • 29:55 - 30:01
    can do work more easily. And then I it's
    just called two opposing spawn and then a
  • 30:01 - 30:06
    raw exit. This to exit the daemon without
    crashing because of launch. You would see
  • 30:06 - 30:11
    that one of the launch demons crashed and
    the specifics flag inside it would try to
  • 30:11 - 30:14
    restart it. And then our option would run
    again. We obviously want to prevent that.
  • 30:14 - 30:21
    So we use the exits as call to exit it.
    And then we are in stage 4. And from my
  • 30:21 - 30:26
    side, that was just basically to block or
    signal. So we don't get killed by launchd.
  • 30:26 - 30:31
    Because when launchd the launch and
    exits, it will send the kill to all this
  • 30:31 - 30:36
    child process. And I need to catch that.
    Otherwise Stage 4 would get killed. And
  • 30:36 - 30:40
    then I just called the Post Exploitation
    Framework, which was written by Sparky.
  • 30:40 - 30:45
    And basically that does the following. It
    first elevates the process to root with
  • 30:45 - 30:50
    current credentials, then it performs a
    remount of the root file system because on
  • 30:50 - 30:56
    stock IOS, a file system was mounted as
    read only and we obviously need to
  • 30:56 - 31:01
    mount it as read/write to modify some files
    on there and then set non it sets the
  • 31:01 - 31:07
    nonce so that the user might be able to
    downgrade to an older version if they are
  • 31:07 - 31:12
    flops. Verifies that the bootstrap was in
    place from the installation. And then
  • 31:12 - 31:17
    checked substrates or the framework that's
    used for them to perform tweak injection
  • 31:17 - 31:24
    and it's plugging into trust us and starts
    them so that they can start to inject into
  • 31:24 - 31:28
    newly spawn processes. Then it's Ponce or
    the launch demons associate with the
  • 31:28 - 31:34
    jailbreak and unloads or own demons so
    that we don't respawned by extended
  • 31:34 - 31:39
    run. They kernel exploit again and then
    performs an LUV start to basically restart
  • 31:39 - 31:44
    all of the launch teams off the system so
    that subset can inject 3 STEM. And with
  • 31:44 - 31:49
    that the system is faulty, jail broken and
    we can perform a few cleanup steps. But
  • 31:49 - 31:53
    yeah, basically the end user has
    jailebroken system. Then as a little
  • 31:53 - 31:58
    side note while we are testing all the
    demons, we got killed by jetsam a lot. So
  • 31:58 - 32:03
    basically jetsam is the kernel
    extension from Apple. That is therefore
  • 32:03 - 32:08
    memory management. And they basically want
    to make sure the user mode application
  • 32:08 - 32:13
    doesn't use too much memory because they
    don't have that much on iPhone. And on all
  • 32:13 - 32:19
    the iPhones, actually. So there is this
    list and jetsam, it jetsam seems easier to
  • 32:19 - 32:25
    use than process uses more than read and
    should use. It would just kill it. So we
  • 32:25 - 32:30
    changed the values in the plist under
    LaunchDaemons to actually allow the
  • 32:30 - 32:34
    LaunchDaemons to use more memory. But
    the weird thing about this is that this
  • 32:34 - 32:39
    actually got us accepted by jetsam and we
    had normal crashes while Apple actually
  • 32:39 - 32:47
    tried to mitigate that beforehand. So
    because jailbreak is always modify those
  • 32:47 - 32:50
    configuration files on the LaunchDaemons,
    they start to move all of them into a
  • 32:50 - 32:54
    dynamic library to guard them under code
    signing. So the jailbreak just couldn't
  • 32:54 - 32:59
    change them anymore. But when you tried to
    figure out the Launchdemon at the Launch
  • 32:59 - 33:06
    Demons, we dumped the dylib and ahm
    there was also plist embedded for
  • 33:06 - 33:12
    Jetsam, but Apple was still using those
    files on disk. So I really want to look
  • 33:12 - 33:16
    further into this because it seems like
    Apple isn't always ignoring those
  • 33:16 - 33:22
    configurations files on disk. And then
    thanks to the whole team. Siguza, Sparkey,
  • 33:22 - 33:28
    and Stek for bouncing ideas back and
    forth and writing the many part of the
  • 33:28 - 33:35
    jailbreak. Then for Pod2g, Synacktiv for
    the kernel bugs. And basically also a big
  • 33:35 - 33:40
    thanks to Saurik for substrate and the
    whole jailbreaking framework and for
  • 33:40 - 33:43
    Swaggo, parrorgeek and Samg_is_a_Ninja
    for testing a few things and keeping
  • 33:43 - 33:49
    motivated. And for Jonathan Levin for his
    books basically because he bought a few
  • 33:49 - 33:55
    awesome books about IOS and that got me
    into it two years ago. And yeah. And in
  • 33:55 - 34:00
    the future, I think exploiting kernel
    vulnerability with other cache functions
  • 34:00 - 34:05
    and owning ROP really is a pain and that
    probably won't do it again. Because he's
  • 34:05 - 34:13
    spent most of that. But yeah, the other
    big problem now is that with a 12 so the
  • 34:13 - 34:19
    new iPhones pack. So point authentication
    kills most of these types of exploits
  • 34:19 - 34:23
    because the problem there is that you
    would now need an ASLA bypass and the pack
  • 34:23 - 34:30
    bypass to get into return oriented
    programing. And it's pretty unlikely to
  • 34:30 - 34:37
    basically have both. And because Pegg
    bypasses are really rare and yet I only
  • 34:37 - 34:43
    know about this one is a LA bypass. So you
    would have to get pretty lucky. Also un-
  • 34:43 - 34:48
    tethering gets progressively harder. Apple
    just fixed another good idea ahead in iOS
  • 34:48 - 34:56
    13.1. Basically the idea was to use printf
    with the format string format modify
  • 34:56 - 35:01
    '%n' to get a Turing complete
    machine because printf, this modifier
  • 35:01 - 35:08
    is basic Turing complete and then you
    start to develop a pack bypass basically
  • 35:08 - 35:14
    and get them to ROP. But now we're in IS
    13.1. I think Apple actually removed the
  • 35:14 - 35:20
    '%n' modifier, so you can no
    longer do this. And yeah. So this idea is
  • 35:20 - 35:25
    also gone. And yeah. In the end, I was
    able to complete my pipe dream, so I guess
  • 35:25 - 35:31
    I will need a new one. So watch out, Apple
    and that spice. Are there any questions?
  • 35:31 - 35:41
    Applause
  • 35:41 - 35:45
    Herald: Thank you, littlelailo for is
    fantastic work. I suppose we're going to
  • 35:45 - 35:50
    hear more from you in the future.
    Littlelailo: Maybe.
  • 35:50 - 35:55
    Herald: Are there questions here in
    this audience. No one who wants to hire
  • 35:55 - 36:02
    this guy now right away. No one. No one.
    Can you describe to me what change
  • 36:02 - 36:09
    actually do times in these, you know, all
    the ASICs? Oh, yeah. Oh, yes. versions.
  • 36:09 - 36:12
    Littlelailo: Well what they change to
    make.
  • 36:12 - 36:17
    Herald: Yeah. What. Plus, you know, I told
    you like I started the tethering challenge
  • 36:17 - 36:20
    actually at 5.1.
    Littlelailo: Well, they added a lot new
  • 36:20 - 36:26
    mitigations and also obviously pitched a
    few bugs like for example, those ASLR
  • 36:26 - 36:32
    bypasses that posterity used in Corona
    got patched. And this one also now got
  • 36:32 - 36:38
    patched by accident. But yeah, I mean like
    some bugs are still there. For example,
  • 36:38 - 36:42
    the Bug in racoon and the conflict pass.
    The bug is still an all day. But yeah, I
  • 36:42 - 36:48
    don't really care about it. And the kernel
    bugs got patched by Apple. But for
  • 36:48 - 36:53
    example, this synthetic one, they also
    also patched wrong by accident. And now it
  • 36:53 - 36:56
    always leaked the strike. But I think they
    also fixed that now.
  • 36:56 - 37:00
    Herald: Your team, you you're mentioning
    your team. You're working not on
  • 37:00 - 37:02
    your own, of course.
    Littlelailo: No.
  • 37:02 - 37:06
    Herald: And how would you restructured?
    How are the roles divided? How was...
  • 37:06 - 37:11
    Littlelailo: Well, we are just like four
    people. So and we have this group chat and
  • 37:11 - 37:15
    then we are just hanging out there and
    bouncing ideas back and forth and maybe
  • 37:15 - 37:18
    working on some stuff.
    Herald: A close contact with the Apple
  • 37:18 - 37:22
    developers.
    Littlelailo: No, not at all.
  • 37:22 - 37:26
    Herald: More.
    Littlelailo: I mean, I reported one bug to
  • 37:26 - 37:32
    their bounty once or like actually says to
    them that their bounty and that one got
  • 37:32 - 37:41
    fixed and it was all fine. But yeah, for
    now, I don't report bugs at the moment.
  • 37:41 - 37:44
    Herald: If you have time, you've time left
    now actually, you're looking for a new
  • 37:44 - 37:47
    project doesn't it?
    Littlelailo: Yeah. Yeah. And I might
  • 37:47 - 37:50
    report some of those bugs in the meantime
    but I mean with the presentation they know
  • 37:50 - 37:54
    about them though so they might fix them.
    Herald: They will be listening now and at
  • 37:54 - 37:57
    least probably I hope.
    Littlelailo: Yes.
  • 37:57 - 38:02
    Herald: Is there anyone who has really,
    you where sitting on a question here. None
  • 38:02 - 38:10
    of you? It's already noon. You know, noon
    passed, so could be that none of you. You
  • 38:10 - 38:15
    can ask them something, maybe someone
    asked them something, maybe they can help
  • 38:15 - 38:18
    you out with certain challenges that are
    there.
  • 38:18 - 38:24
    Littlelailo: Well, I don't really have a
    question either. Laughter I have my own
  • 38:24 - 38:30
    research project now. Well, like, I do
    stuff at the moment and look at other
  • 38:30 - 38:35
    things. For example, to the bootrom
    exploit came out now. And so I started
  • 38:35 - 38:41
    developing on the chick team with them.
    And that's what I currently do, basically.
  • 38:41 - 38:45
    Herald: You're great, man. Littlelailo,
    thank you. Giving him a warm applause!
  • 38:45 - 38:48
    Applause
  • 38:48 - 38:52
    36c3 Postroll music
  • 38:52 - 39:15
    Subtitles created by c3subtitles.de
    in the year 2020. Join, and help us!
Title:
36C3 - Tales of old: untethering iOS 11
Description:

more » « less
Video Language:
English
Duration:
39:15

English subtitles

Revisions