36c3 preroll music Herald: So, Samuel is working at Google Project Zero on especially vulnerabilities in Web browsers and mobile devices. He was part of the team that discovered some of the vulnerabilities that he will be presenting in this talk today in detail about the no user interaction vulnerability that will be able to remotely exploit and compromise iPhones through iMessage. Please give Samuel a warm round of applause. Applause Samuel: OK. Thanks, everyone. Welcome to my talk. One note before I start, unfortunately, I only have one hour. So I had to omit quite a lot of details. But there will be a blog post coming out hopefully very soon that has a lot more details. But for this talk, I wanted to get everything in there and leave out some details. OK. So this is about iMessage in theory some of it applies, or quite a lot actually applies to other messengers, but we'll focus on iMessage. So what is iMessage? Yeah, it's a messaging service by Apple. We've heard about it in the previous talk a bit. As far as I know, it is enabled by default. As soon as someone signs into an iPhone with their account, which I guess most people do, because otherwise you can't download apps. Interestingly, anyone can send messages to anyone else. So it's like SMS or phone calling. And then if you do this, then it pops up some notifications, which you can see that here on the right screenshot, which means that there must be some kind of processing happening. And so, yeah, this is like default enabled, zero click attack surface without the user doing anything, there's stuff happening. And then on the very right screenshot, you can see that you can receive messages from unknown senders. It just like says there. This sender is not in your contact list, but all the processing still happens. In terms of architecture, this is roughly how iMessage is structured, not very, yeah, anything too interesting, I guess. You have Apple cloud servers and then sender and receiver are connected to these servers. That's pretty much it. Content is end to end encrypted, which is very good. We heard this before, also. Interestingly, this also means that Apple can hardly detect or block these exploits though, because, well, they are encrypted, right? So that's an interesting thing to note. So what does an iMessage exploit look like? So in terms of prerequisites, really the attacker only needs to know the phone number or the email address, which is the Apple account. The iPhone has to be in default configuration so you can disable iMessage. But that's not done by default. And the iPhone has to be connected to the Internet. And in terms of prerequisites, that's pretty much all you need for this exploit to work. So that's quite a lot of iPhones. The outcome is the attacker has full control over the iPhone. After a few minutes, I think it takes like five to six, seven minutes maybe. And it is also possible without any visual indicator. So there's no... you can make it so there are no notifications during this entire exploit. OK. But before we get to exploiting, of course, we need a vulnerability and for that we need to do some reverse engineering. So I want to highlight a bit how we started this or how we approached this. And I guess the first question, you might be interested in, is what daemon or what service is handling iMessages. And one easy way to figure this out is you can just make a guess. You look at your process list on your Mac, the Mac can also receive iMessages. You, like, stop one of these processes and then you see if iMessages are still delivered. And if not, then probably you found a process that's somewhat related to iMessages. If you do this, you'll find "imagent", already sounds kind of related. If you look at it, it also has an iMessage library that it's loading. Ok, so this seems very relevant. And then you can load this library in IDA. You see a screenshot top right. And you find a lot of handlers. So for example, this "MessageServiceSession handler: incomingMessage:", and then you can set a breakpoint there. And then at that point you can see these messages as they come in. You can dump them, display them, look at them, change them. And so this is a good way to get started. Of course, from there, you want to figure out how these messages look like. So, yeah, you can dump them in there when they come in in the handler, on the right side you see how these iMessages look like more or less on the wire. They are encoded as a PList, which is an Apple proprietary format. Yeah, think of it like JSON or XML. And I guess some fields are self-explanatory. So, "p", that's the participants in this case this is me sending a message to another account I own. You have "T" which is the text content of the message. So "Hello 36C3!". You have a version, for some reason you also have an XML or HTML- ish field, which is probably some legacy stuff. It's being parsed, this XML. But yeah, the whole thing looks kind of complex already. I mean maybe you would expect a simple string message to just be a string. In reality, it's sending this dictionary over the wire. So let's do some more attack service enumeration. If you then do more reverse engineering, read the code of the handler, you find two interesting keys that can be present, which is ATI and BP, and they can contain NSKeyedUnarchiver data, which is another Apple proprietary serialization format. It's quite complex, it has had quite a few bugs in the past. On the left side you see an example for such an archive. It's yeah, it's being encoded in a plist and then it's pretty much one big array that has, like, every object has an index in this array. And here you can see, for example, number 7 is some object, is the class NSSharedKeyDictionary. And I think key one is an instance of that class and so on. So it's quite powerful. But really what this means is that this serializer is now zero click attack surface because it's being passed on this path without any user interaction. So I said it's quite complex. It even supports things like cyclic references. So you can send an object graph where A points to B and B points back to A for whatever reason you might want that. Natalie wrote a great blog post where she describes this in more detail. What I have here is just an example for the API, how you use it. This is Objective C at the bottom. If you're not familiar with Objective C, you can think of these brackets as just being method calls. So this is doing, in the last line, it's calling the unarchivedObjectOfClasses method for this NSKeyedUnarchiver. You can see you can pass a whitelist of classes. So in this case, it will only decode dictionary, strings, data, etc. So looks quite okay. Interestingly, if you dig deeper into this, this is not quite true because it also allows all the subclasses to be decoded. So if you have an NS- something-something dictionary that inherits from NSDictionary, then that can also be decoded here, which is quite unintuitive I think. And this really blows up the attack surface because now you have not only these 7 or so classes, but you have like 50. Okay. So this is what we focused on when me and Natalie were looking for vulnerabilities. It seemed like the most complex thing we found. We reported quite a few vulnerabilities here, you can see it maybe a bit on the left. The one I decided to write an exploit for is this 1917, reported on July 29th and then exploits sent on August 9th. Yeah, mostly I decided to use this one because it seemed the most convenient. I do think many of the other ones could be exploited in a similar way, but not quite as nice, so would maybe take some more heap manipulation, etc. So then Apple first pushed the mitigation quite quickly, which basically blocks this code from being reached over iMessage. In particular, what they did is, they exactly no longer allow subclasses to be decoded in iMessage. So that's quite a good mitigation, it blocks off maybe 90 percent of the attack surface here. Yeah. So then they fully fixed it in iOS 13.2. But again, after August 26th this was only just local attack surface. OK, so what is the bug? It's some initialization problem during decoding, the vulnerable class is SharedKeyDictionary, which again, it's a subclass of NSDictionary, so it's allowed to be decoded. So let's take a look at that. So, yeah. SharedKeyDictionary. Here's some pseudocode in Python. It's a dictionary. So its purpose is to, well, look up keys to values or map keys to values. The lookup method is really simple. It just looks up an index in a key set. So every key dictionary has a shared key set and then that index is used to index into some area. OK, so that's quite simple so most of the magic happens in the SharedKeySet. And so what that does is something like compute a hash of the key. Use that hash to index into something called a rankTable, which is an array of indices. And then if that index is valid, so it's being bounced, checked against the number of keys. Then it has found the the correct index and if not, it can recurse to another SharedKeySet. So every SharedKeySet, can have a sub-SharedKeySet, and then it repeats the same procedure. So it already looks kind of complex. Why does it have... why does it need this recursion? I'm not quite sure, but it's there. And so now we look at how this goes wrong. So this is the initWithCoder, which is the SharedKeySet constructor used during decoding with the keyedUnarchiver. And it looks pretty solid at first, it's really just taking the values out of the archive and then storing them as the fields of this SharedKeySet. I have a, I'm gonna go through the code here in, like, single step to highlight where it goes wrong or what goes wrong here, what's wrong with this code. So we start with SharedKeySet1 which implies there's gonna be another one. And at the start it's all zero initialized. It's basically being allocated through ?calloc?. So everything is zero. Then we execute the first line. Okay. So numKey, you see some interesting values coming. So far this is all fine. Note, that you can set numKey, at this point numkey can be anything because it's only being validated three lines further down, right? Where it's making sure that numKey matches the the real length of this array. So this is fine, but here it's now recursing and it's decoding another SharedKeySet. So we start again. We have another SharedKeySet, all filled with zeros and we start from the top. Again, numKey is one, so this is this is a legitimate SharedKeySet, decoding a rankTable. And here we are making a circle. So for SharedKeySet2 we pretend that its sub-KeySet is SharedKeySet1. And this actually works. So the NSKeyedUnarchiver has special handling to handle this correctly. So it does not create a third object and it makes the cycle. And we're good to go. Okay. Next to decode the keys area. So this is fine. SharedKeySet2 seems legitimate so far. And now it's doing some sanity checking. Where it's trying, where it's making sure that this SharedKeySet can look up every key. And so it does this for the only key it has, key one. Now, at this point, it's again, remember, it's hashing the key going into rank table, takes out 42, which is bigger than numKey. So in this case, this look up here has failed. And now it's recursing to SharedKeySet1. Right? This was the logic. And at this point it's taking out this hex41414141 as index, compares it against hexffffffff and that's fine, and now it's accessing, null pointer, which is.. the keys area is still null pointer plus, well, 41 41 41 41 times 8. So at this point it's crashing. It's accessing invalid memory, precisely because in this situation the SharedKeySet1 hasn't been validated yet. OK, so that's the bug we're going to exploit. I have these checkpoints just to think where we are, so we now have a vulnerability in this NSUnarchiver API. We can trigger it through iMessage. So what Exploit Primitive do we have? Let's take a look again at the lookup function, which we saw before. So here where it's bold, this is where we crash. keys is null pointer, index is fully controlled. So we can access null pointer plus offset. And then what happens is the result of this memory access is going to be used as some Object-C Object. So this is all Objective-C in reality, it's doing some comparison, which means it does something like it called some method called isNSString, for example. And then also eventually it calls dealloc, which is the destructor. So yeah, we have... the thing it reads from whatever, it will treat it as an objectif C-Object calls a message on it. And that's our Exploitation Primitive. Okay, so here we are. How do we exploit this? So the rough idea for exploiting such vulnerabilities looks like this. You want to have some fake Objective-C object somewhere in memory that you're referencing. So again, we have an we can access an arbitrary absolute address. We want some fake Objective-C object there. Every Objective-C object has a pointer to its class. And then the class has something called a method table, which basically has function pointers to these methods. Right. And so if we fake this entire data structure thing, the fake object and the fake class, then as soon as the process calls some method on our fake thing, we get code execution. So we get control over the instruction pointer and then it's game over. So that's going to be our goal for this exploit. So here we have two different types of addresses: On the left side we have heap addresses or data, really. And on the right side, in this NSString-thing we need library addresses or code addresses, simply because on iOS you can't have writeable code regions. So we have to necessarily reuse existing code, do so to something like ROP also. So we need to know where libraries are mapped for this. And this is exactly the problem we are gonna face now because there is something called ASLR, Address Space Layout Randomization. And what it does is it will randomize this entire address space. So on the left side, you can see how a process looks like, how the virtual memory of a process looks like before ASLR. And there everything is always mapped at the same address. So if you start the same address twice on different phones, maybe even without ASLR the same library is at the same address, the heap is always at the same address stack. Everything is the same. And so this would be really simple to exploit now because, well, everything is the same. With ASLR everything is shifted and now all the addresses are randomized and we don't really know where anything is. And so that makes it harder to exploit this. So we need an ASLR bypass is what this means. We're gonna divide it into two parts. So the heap addresses we get them from in a different way than the library addresses. So let's see how we get heap addresses. It's really simple honestly, what you can do is heap spraying, which is an old technique. I think 15 years old maybe. And it does still work today. The idea is that you simply allocate lots of memory. So if you look at this code there put on the right, which you can use to test that, what it does is that it allocates 256 megabytes of memory on the heap with malloc. And then afterwards there's one address or there's many addresses. But in this case, I'm using this hex110000000 where you will find your data at. Okay. So just spraying 256 megabytes lets you put controlled data at a controlled address, which is enough for this first part of the exploit. The remaining question is how can you heap spray over a iMessage. That's a bit more complicated. But it is possible because NSKeyedUnarchiver is great and it lets you do all sorts of weird stuff which you can abuse for heap spraying. So, yeah. Blog posts will have more details. Okay. So we have these, the heap addresses. We have them. We need the library addresses. Let's go back to the virtual memory space. On iOS and also on macOS the libraries - so maybe in this case all three libraries, but in reality, it's like hundreds of system libraries - they are all prelinked into one gigantic binary blob, which is called a dyld_shared_cache. The idea is that this speeds up like loading times because all the interdependencies between libraries are resolved pretty much at compile time. But yeah, so we have this gigantic binary blob and it has everything we need. So it has all the code, it has all the ROP gadgets and it has all the Objective-C classes. So we have to know where this dyld_shared_cache is mapped. If you dig into that a bit or if you look at the documentation or the the binaries, you can find out that it is going to be mapped always between these two addresses. So between 0x180000000 and 0x280000000, which leaves only a 4 gigabyte region, so it's only being mapped in these 4 gigabytes. And then the randomization granularity is also 0x4000 because iOS uses large pages so it can only randomize with page granularity, and that page granularity is 0x4000. But really what's most interesting is that on the same device, the dyld_shared_cache is only randomized once per boot. So if if you have two different processes on the same device, the shared cache is at the same virtual address. And if you have one process, then it crashes and you have another one. And so on, like the shared cache is always going to be at the same address. And that makes it really interesting. And also, it's one gigabyte in size. It's gigantic. So it's not too hard to find in this four gigabyte region. Right. So this is what our our task has boiled down to at this point. We have this address range, we have the shared cache. And all we need to know now is what is this offset? So let's make a thought experiment. Let's say we had an oracle which would tell us... which we could give an address. And it would tell us if this address is mapped in the remote process. OK, if we have this, it suddenly becomes really easy to solve this problem, because then all you have to do is you go in 1 gigabyte steps the the size of the shared cache between these two addresses and then at some point you find a valid address. So maybe here after 3 steps, you find a valid address, and then from there you just do a binary search. Right. Because you know that somewhere between the green and the second red arrow, the shared cache starts. So you can do a binary search and you find the the start address in logarithmic time in a few seconds, minutes, whatever. So obviously the question is what? How? Where would we get this oracle from? This seems kind of weird. So let's look at receipts, message receipts. So iMessage like many other messengers - I think pretty much all of them that I know - send receipts for different things. iMessage in particular has delivery receipts and read receipts. Delivery received means the device received the message, read receipt means the user actually looked - opened the app, looked at the message. You can turn off read receipts, but as far as I know, you cannot turn off delivery receipts. And so here on the left you see a screenshot. Three different messages were sent and they have three different states. The first message was marked as read, which means it got a delivery receipt and a read receipt. The second message is marked as delivered. So it only got a delivery receipt and the third message doesn't have anything. So it hasn't received any receipt. OK. So why is it useful? Here on the left is some pseudocode of imagent's handling of how it handles messages and when it sends these receipts. And so you can see that it first parses the plist that's coming in and it's then doing this nsUnarchive at some later time. And this is this is exactly why all but would trigger during nsUnarchive. And only then does it send a delivery receipt. Right. So what that means is if during our during our nsUnarchive, if we can trigger the bug and cause a crash, then we have somewhat of a one bit sidechannel. Right. Because if we cause a crash, then we won't see a delivery receipt. And if we don't cause a crash, then we see a delivery receipt. So it's a one bit of information. And this is going to be our oracle. All right. So ideally, you have a vulnerability that gives you this perfect oracle of is an address mapped or not? So crash, if it is not mapped, don't crash if it mapped. In reality, you probably will not get this perfect oracle from your bug. On the left side, you see the real Oracle function for this vulnerability, which is, well it has to be mapped. OK. But then it's also using the value that it's reading. And so it will only not crash if the value is either 0 or if it has the most significant bit set, that is some like pointer taking stuff or if it's a real legitimate pointer to an Objective-C object. So this Oracle function is a bit more complex, but the similar idea still works. So you can still do something like a binary search, and then infer the shared cache start address in logarithmic time. Right. And so it only takes maybe five minutes or so to do this. But for this for this part, again, I have to refer to the blog post which will cover how this works. OK. So this is the summary of the remote ASLR bypass. Two phases, there's linear scan where it's just scanning, sending these payloads and checking if it gets the receipt back, and the first time it gets a receipt back, it knows. OK. This address is valid. I now found an address that is within the shared cache. And at that point it starts this searching phase, which in logarithmic time figures out the exact, precise starting address. So there's a few common questions about this that I want to briefly go into. The first maybe obvious question is, can you really just crash this agent like 20 plus times? And the answer is yes. There's no indicator or anything that the user would would see that this demon crashes. The only thing you can do is you can go into like settings, privacy, something something, crash log something, and then you can see these crash logs. Second question is you can I think by default, the iPhone is configured to send crash logs to the vendor, to Apple. So isn't that a problem? So I think I looked at this briefly. What I stumbled across was that it seems that iOS collects at most 25 crash logs per service. This is not designed to be like a security feature. Right. So this makes sense. But what that means is that an attacker can use some kind of, well, resource exhaustion bug to crash this daemon maybe 25 times first, and then only start to exploit and then no trace of the exploit will be sent over. Third question is whether this can be fixed by simply sending the delivery receipt very early on. I think this is... this was my first suggestion to Apple to just send this delivery receipt right at the start. Eventually I figured out it doesn't really work because you can still make some kind of timing side channel, because when when a demon crashes multiple times, it's subject to some penalty and it will only restart like a few seconds or even minutes later. So from the timing of getting a delivery receipt, you can then still basically get this oracle. Right. So it doesn't really work by just sending it earlier. I'll go into some other ideas that might work later. Okay. So at this point I'm starting the demo. The demo is two parts. Let's see where it is. Right. So I have this iPhone here and you can with QuickTime... the screen is mirrored to the projector. So this iPhone is it's a 10S, so it's from last year. It's on 12.4, which is the last vulnerable version. So that's like half a year old at this point. And what else? So there is no existing chats open. Okay. And let's see. So I hope the Wi-Fi works. What you can see here is the way the exploit works that it's hooking with Frida into... Do we get delivery receipt? Uh, do we? Yeah. Okay, cool. It works. So, yeah, it's popping up these messages. The way the exploit works that it's hooking the messages app on macOS with Frida and then it's sending these specific marker messages like INJECT_ATI, and then the Frida hook replaces this message with like the current payload. Right. And now it's testing these addresses. It's not too slow I guess. Yeah. And it's popping up some nice messages. Okay. It already found. Okay. So this is already the end of the first stage. So that was quite fast. It found a valid address in this like first probing step and now it has 21,000 candidates for the shared cache base. I know it's doing this kind of binary search thing to half that in every step. Okay. Now it only has 10,000 left and so it's quite fast and quite efficient. Okay. While this runs, um, let's continue. So this is where we are. We can now create fake objects. We have all the addresses we need. It's like this 1170 is where we can place our stuff and then we will gain control over the program counter. And from there it's standard stuff, right? It's what you would do in all of these exploits you pivot maybe to the stack, you do return oriented programing and then you can run your code and you've succeeded. Now, at this point, there is another thing coming in. Pointer authentication is a new security feature that Apple designed and implemented first in the 10S, so this device from 2018. And the idea is that you can now - for this you need CPU support - the idea that you can now store a cryptographic signature in the top bits of a pointer. OK, so here on the very left side, you have a raw pointer. So the top bits are zero because the way the address space works. Now there's a set of instructions that sign a pointer and they will maybe take a context on it, but they use some key that's not in memory - that's in a register, compute a signature of this pointer and store the signature in the top bits. And that's what you see on the right side. The green things. That's the signature. And now before using this pointer, the code will now authenticate by running another instruction. And this instruction, if the verification fails, it will basically clobber this pointer, make it invalid. And then the following instructions will just crash. Right. So here this is the function called the BL, branch and link instruction. This is doing a function call to a function pointer. But first it's authenticating this pointer. And if this authentication step fails, then the process will crash right there. What this means for an attacker is that more or less, ROP is dead, because ROP involves faking a bunch of function point... or like, well, code pointers really, that point in the middle of existing code. So this is no longer possible because an attacker cannot generate these signatures. So this is where our exploit breaks, right, the red thing. Well, we have a fake objective C class with our own function pointer. This does no longer work because we cannot compute these signatures. So what do we do? One thing that's still possible and it's even documented in the documentation is that this class pointer in the object - what's also called the ISA pointer - it's not protected by PAC in any way. Which means we can fake instances of legitimate existing classes. Right. So in this case here we can have a fake object that points to a real class that has real, legitimately signed method pointers. So this tool works. And with this, we can now get existing methods called, out of place and kind of manipulate the control flow. And these existing methods are basically now gadgets. So if you want to think about it that way. So what can we do with this? One very interesting method we can get called is dealloc, the destructor. So I think in quite a few, maybe most of the Objective-C exploitation scenarios, you can probably get a dealloc method called. Now what you do is you just enumerate all the destructors in the shared cache. There's tons of them, I think 50,000, and you can get any of those called. And then one of them or a few of them are really interesting because they call this invoke method, which is part of the NSInvocation object, or class. And an NCInvocation is basically a bound function. So it has a target object, the method to be called and all the arguments. And as soon as you call invoke on this NCInvocation, it does this method call with fully control arguments. Right. So what that means is with this destructor, we can now make a fake object with a fake NSInvocation that has any method call we would like to perform, and then it's going to do that because it's running this invoke here. Again, you see this shield here, which I put in place for things that Apple has hardened since we sent them the exploit. So what they did so far is they hardened NSInvocation and it's now no longer easily possible to abuse it in this way. But yeah. So for us, we can now run arbitrary Objective-C methods with controlled arguments. What about sandboxing? If you do some more reverse engineering and figure out what services play into iMessage, this is what you end up with. On the right side. So you have a number of services. Most of them are sandboxed. If it has the red border, it means there's a sandbox. Interestingly, Springboard also does some NSUnarchiver stuff. So it's decoding the BP key. So it could also trigger our vulnerability and Springboard is not sandboxed. So it's the main UI process. It's basically what's handling showing the the welcome screen. And so on. And so what that means is, well, we can just target Springboard and then we get code execution outside of the sandbox so we don't actually need to worry too much about the sandbox. As of iOS 13, this is fixed and this key is now decoded in the sandbox. Cool, so we can execute Objective-C methods outside of the sandbox. We can with that access user data, activate camera, microphone, etc. This is all possible through Objective-C quite easily. But of course we don't care about that. What we want is a calculator and this is also quite easy, with one Objective-C call - UIApplication launchApplication blah blah blah. And so let's see if this works. Go back to the demo. So where are we at? So the, uh, the ASLR bypass ran through. You can nicely see that it roughly halved the candidates in every round, or with every message it had to send. It ended up with just one message. Yeah, well with just one candidate at the end. And that is the shared cache base in this case 0x18a608000. Now it's preparing the heap spray. This is all kind of hacked together. I think if you wanted to do this properly, for one, you can send the whole heap spray in one message. I'm just lazy. It's also probably way too big. Another thing is, I think you would probably not target springboard in reality just because spring board is very sensitive. So if you crash, did you get this re-spring and the UI restarts. So I think in reality you would probably target IM agent and then chain the sandbox escape. Because while this bug would also get you out of the sandbox. So looks should be doable. Okay. So I think the last message arrived. It's freezing here for a couple of seconds. I don't actually know why I never bothered, but it does work. Applause Thank you. Yeah. So that was a demo. It's it's kind of naturally reliable, this exploit, because there is not much of heap manipulation involved except this one heaps spray, which is controllable. Okay. Um, so what's left? I think one more thing you can do is you can attack the kernel if you want that. You have to deal with two problems here. One is code signing. You cannot execute unsigned code on iOS. And then the standard workaround for that is you abuse JIT pages in safari. But we are not in safari or we are not in web content, so we don't have JIT pages. What I did here is I basically pivoted into JavaScript core, which is the the JS library. You can use it from from any app also. And then I'm just bridging syscalls into JavaScript and then implementing the kernel exploit in JavaScript. This does not require any more vulnerabilities. So you do not need a JavaScript core bug to do this. And the idea is very similar to pwn.js. Maybe some of you know about that. It's a library. I think initially developed for Edge because they did something similar was like JIT page hardnings. So what I decided to do is take SockPuppet from Ned or CVE-2019-8605, which works on this version, it works on 12.4. This is the trigger for it. And I only ported the trigger. I didn't bother re -implementing the entire exploit. So yeah, this is the trigger. It will cause a kernel panic. It's quite short. Which is nice. So if you want to run this from JavaScript, really, there's only three things you care about, right? So the first one is you need the syscalls. So highlighted here, there is like four or so different syscalls here. Not a lot. And you just have to be able to call them from JavaScript. The other thing is you need constants, right? So I have AF_INET6, SOCK_STREAM. These are all integer constants. So this is really easy, right? You just need to look up what these values end up being. And then the last thing is you need some data structures. So in this case, I need this so_np_extension thing. It needs some integer value to pass pointers to and so on. Yeah. And then this is kind of the the magic that happens. You take sock_puppet.c extract the syscalls etc. There is one Objective C message you can call which is very convenient, which gives you a dlsym. What this lets you do is, it lets you get native C function pointers that are signed, right. Because so far we can only call Objective C methods, but we need to be able to call syscalls or at least the C wrapper functions. So with this dlsym method thing we can get signed pointers to C functions. Then we need to be able to pivot into JavaScript code, which is also really easy with one method call, the JSContext evaluateScript. We need to mess around with memory a bit like corrupt some objects from outside, corrupt some area buffers in javascript, get read, write. Kind of standard browser exploitation tricks I guess. But yeah. So if you do this what you end up with is sock_puppet.js. It looks very similar. You can see a bit of my javascript API that lets you allocate memory buffers. I read and write memory, have some integer constants and yeah, apart from that, it doesn't really look much different from the initial trigger. And so this can now be served over, well, staged onto the iMessage exploit building on top of this object a C method called primitive. And I guess at least in theory I didn't fully implement it. This should be able to just run a kernel exploit and fully compromise the device without any interaction in probably less than 10 minutes. Okay, so this was the first part. How does how does this exploit work. What I have now is a number of suggestions how to make this harder and how to improve things. So one of the first things that is really critical for this exploit is the ASLR bypass, which relies on a couple of things. And I think a lot of this ALSR bypass also works on other platforms. So Android has a very similar problem with like mappings being at the same address across processes. And other messengers have these like receipts and so on. So I think a lot of this applies not just to Apple but to Android and to other messengers. But okay. What is the first point? So weak ALSR, this is basically the heap spraying, which is just too easy. This shouldn't be so easy. In terms of theoretical ASLR, you can see it maybe sketched here on the right. In theory, ASLR could be much stronger, much more randomized. In reality, it's just like the small red bar. So it really it should just have much more entropy to make heap springing not viable anymore. The next problem with ASLR is per-boot stuff. At the bottom you can see it, right? So you have three different processes, the shared cache is always at the same address, similar problems on other platforms, I mentioned that. This is probably hard to fix because by this point a lot of, quite a lot relies on this. And it would be a big performance hit to change this. But maybe some clever engineers can figure out how to do it better. The third part here is the delivery receipts, which, interestingly, they can give this side channel, this one bit information side channel and this can be enough to break ASLR. And as I've mentioned before, I think a lot of other messengers have this same problem. What might work is to either, well, remove these receipts. Sure. Or maybe send them from a different process so you can't do this timing thing or even from the server. I think if you send them, if the server already sends the delivery receipt, it's a bit of cheating. But at least this attack doesn't work. Sandboxing, another thing, it's probably obvious, right? So the everything that's on zero click attack surface should be sandboxed as much as possible. Of course, to, you know, to require the attacker to do another full exploit after getting code execution. But Sandboxing can also complicate information leaks. So not only had this other iMessage bug CVE-2019-8646, there's a blog post about this one. It basically lets you. She was able to send to cause a Springboard to send HTTP requests to some server and those would contain pictures, data, whatever. If Springboard would've been sandbox to not allow network activities, this would have been much harder. So sandboxing is not necessarily just about this second breakout. What I do want to say about sandboxing, ithat it shouldn't be relied on. So I think that this remote attack surface is pretty hard. And it's not unlikely that it's actually harder than the sandboxing attack surface. And also on top of that, this bug, the NSKeyedUnarchiver bug, it would also get you out of the sandbox because the same API is used locally for IPC. So there's that. Yeah. This would be nice if the zero click attack surface code would be open source. Would have been nice for us. It would have been easier to audit. Maybe someday. Another feature that I would like to see or another theme is reduced zero attack surface. Make it one click at least one click attack surface. Right. So before and here you could see that an unknown sender can send any messages. It would be nice if there would be some pop up that's like, well, do you actually want to accept messages? Threema lets you block unknown senders. I think that's a cool feature. So yeah, there's more work to be done here. Also, this restarting service problem, I think it could get bigger even. So, here we have pretty much unlimited tries for the ASLR bypass. It's probably going to become even more relevant with memory tagging, which we can also be defeated if you have many tries. So yeah, I guess if there's some process or some critical demon crashes ten times, maybe not restart it. I don't know. It's gonna need some more thinking, right? You don't want to denial-of-service the user by just not restarting this demon that crashed for some unrelated reason. But yeah, this would be a very good idea to have some kind of limit here. Okay. Conclusion. So yeah, zero click exploit, they are thing. They do exist. It is possible to exploit single memory corruption bugs on this surface with, you know, without separate info leaks. Despite all the mitigations we have. However, I do think by turning the right knobs, this could be made much harder. So I gave some suggestions here. And yeah, we need more atack surface reduction, especially on the zero click surface. But I think there is progress being made. And with that thanks for your attention. And I think we have time for questions. Thank you. applause Herald: We do have time for questions. And if you're in the room, you should line up at the microphones and then we might also have questions from the Internet. One quick reminder is that all fun things that what they work with explicit consent that includes photos. So the photo policy of the CCC is that if you take a photo, you need to have explicit consent by the people in the frame. So remember, don't do any long shots into the crowd because you want to have the consent of everybody there. Good. We have the first question from the Internet. Question: The Internet wants to know. Did Apple give you some kind of a reward? And was it in your iPhone? Answer: No, we did not get any kind of reward. But we also didn't ask for it. No, I didn't get a new iPhone, but I'm still using mine. Which is it? Yeah. I mean, this is a Xs, right? Current hardware models can be defeated with this, if that is the question. Herald: Good. We have a question for microphone number 3. Q: Hello. Uh, just a question. I did not truly understand how the fix with the server or having another process, uh, sending that there every message will fix the problem because if it does work, if you are in the right addresses, the thing just will work. Make the server or the process, send the delivery message and if it crashes, it doesn't do anything so... A: So the idea would be in this case, I'm like sending this one method that would crash and then either I get a delivery received or I don't. If the server already sends the delivery receipt before it actually gives the message to the client or to the receiver, then I would always see a delivery receipt and I wouldn't be able to figure out if my message caused the crash or not. So that's the idea behind maybe sending it on the server side, if that makes sense. Follow-up question: Yeah. But in this case, if legit people send a message and it doesn't reach the people because... A: Yeah. Yeah. It's a hack. Right. So it's not perfect. I mean the server could only send to find this delivery receipt once it like send it out over TCP and maybe got a TCP ACK or whatever happens in the kernel. But it's a hack in any case. Yeah. Like it's a tradeoff. Herald: We have a question for microphone number two. Q: Hello. Okay. Thanks for the talk. Two questions. First: Is OS X also a potential candidate for this bug. And second: Can you distinguish multiple devices with your address based randomization detection? A: Mm hmm. So yes: OS X or MacOS is affected just the same. I think this specific exploit wouldn't directly work because address space looks a bit different, but I think you could make it work and it's affected. In terms of multiple devices, so I haven't played around with that. I could imagine that it is possible to somehow figure out that there are multiple devices or that you know which device just crashed. But I haven't investigated. That's the answer. Follow-up: Thanks. Herald: We still have time for more questions. There was a question from microphone number 1. Q: Hi. Thanks for the talk. Quick question. You said that exploitation could be made without having any notification. How would that be made? A: Yeah, I briefly looked into how it could work. Well. So for one, you can take out parts of the message so that it fails parsing later on in the processing and then it will just be like thrown away because it says, well, this is garbage. The other thing is, of course, once you get with the like very last message where you get code execution, you cannot prevent it from showing a message like a notification, because that happens afterwards. Follow-up Q: But until you get the code execution, you can't remove it. So you see the first message? A: So but you can do the other. The other thing, like make it make the message look bad - bad enough that like later parsing stages will throw them away. Follow-up: Thanks. Herald: Good. We have a couple of more questions. Remember, if you don't feel comfortable lining up behind the microphones, you can ask through the signal angel through the Internet. Microphone number 4, please. Q: Yes. Hi. Hi Samuel. Um, I was curious you have some suggestions about reducing the attack surface. Are there any suggestions that you'd make to save, like Apple or Google? You know, in terms of what they can see. You mentioned logging a little bit earlier. A: Yeah. So I sent pretty much this list with the exploit I sent to Apple. And I think the blog post will have a bit more. But yeah, I told them the same thing. Yeah, if that's your question, did I get it right? Follow-up Q: Yes. I mean, maybe I misunderstood a little bit, but I suppose that some of these reductions in the attack surface seem to be in terms of like what's happening on the device. Yeah. Whereas I'm wondering in terms of monitoring. So being able to catch something like this in progress. A: Right. Right. So this is gonna be really hard because of end-to-end encryption. So the server just sees like encrypted garbage and has no way of knowing is this an image? Is that the text? This is an exploit? So on the server, I don't think you can do much there. I think it's gonna have to be on the device. Herald: We have a question from the Internet. Q: How do you approach a attack surface mapping? A: Um, well, reverse engineering, playing around, looking at this message format. In this case, what was somewhat obvious what an attack surface was. Right. So figure out which key is off this message are being processed in some way. Make a note. Decide which one looks most complex. Go for that first. That's what we did. Herald: We have a question from microphone number 2, please. Q: Hi. How long did you and your colleague research to get the export running? A: So the the vulnerability finding thing was not only I think we spend maybe three months finding the exploit. So I had a rough idea how I wanted to how how I would approach this exploit. So I think at the end it took me maybe a week to finish it. But I had thought about doing that for like, while a looking while looking for vulnerabilities and those two to three months. Herad: We have another question from microphone number three. Q: Um, is there the, uh, threat that the attacked iPhone would itself turn into a tack up by the exploit? A: Sure. Yeah, you can do that. I mean, you have full control, right? So you have access to the contacts list and you can send out iMessages. The question is if it's necessary. Right. I mean, you can also send messages from you don't really need them, the iPhone to send the messages. But I think in theory: Yes, that's possible. Herald: Do we have more questions from the Internet? Q: Does the phone stay compromised after restart? A: So there is no persistence exploit here. No. You will need another exploit a littlelailo did a talk. I think just an hour ago about persistence. So you would need to change this with what, for example, to exploit that he showed. Herald: And if you have questions in the room, please line up behind the microphones. Do we have more questions from the Internet. Q: Yes. So you've achieved the most novel buck ever found to be fine in iOS. What's the next big thing you'll be looking at? A: Good question. I don't really know myself, but I'm going to stay probably around for zero click attack surface reduction for a bit more. Herald: Looks like we don't have any brave people asking questions in the room. Does the Internet have more courage? Q: How long does discovery and exploitation and development take and how much does the team work to improve the process and development time? A: Okay, so how much how long does this exploitation process work? That's the first question. Yes. Yeah. I mean, this is generally a hard thing to answer. Right. There's like years of hacking around and learning how to do this stuff, etc. that you have to take into account. But as I said, I had a rough idea how this exploit would look like. So then really implementing it was like one or two weeks. The initial part of reverse engineering iMessage reverse engineering this NSUnarchiver thing. I kind of I think this took forever. This took many months and it was also very necessary for exploit writing. Right. So a lot of the expert primitives I use, they also abuse the NSKeyUnarchiver thing. Herald: We have time for perhaps two quick questions. Mike number 4, please. Q: Super. Uh, I'm not super familiar with iOS virtual memory address space but you should two heap regions when you showed the picture of it. And I'm wondering why are there two heap regions? A: OK, because there is only a minor detail, but I think there is one region initially like below the shared cache and one state is full. It just makes another one above it. So it's really just like if the one gets used or gets gets used up, it makes another one. And that's going to be like above the shared cache. I think that's the picture you're referring to. Follow-up: Yeah, thank you. Heralds: And unfortunately, we are out of time. So the person that might have some number one, please come up to the stage afterwards and perhaps you can grab a talk. So please give a warm. I can't say this exactly. applause Thanks. applause Postroll music Subtitles created by c3subtitles.de in the year 2020. Join, and help us!