>>Hi Everyone, Uhm my name is Max Bazaliy and today we'll talk about jailbreaking Apple Watch. [inaudible yelling from audience] Okay, let me fix it. Oh. Yeah. That's a bit better. So. Once again, yeah I'm Max Bazaliy and uh I'll talk about jailbreaking Apple Watch. I work as a security researcher at Lookout when I focused on Malware detection. Last year I was the leader researcher on eh Pegasuses exploit chain. And currently focused advanced exploitation techniques for software hardware Uhm I I co-founded the fried Apple Team and I'm author various of jailbreaks for IOS, tvOS and now for ewatches. So what is apple watch? Uhm It's a smart watch device that was released in 2015 and it uses Apple S1 or S2 processor. uh it's basically 32 bit architecture processor uh called the RM7K uh which is derived from RM7A uhm It uses taptic engine for user identifications it has eh 512mb of RAM and it's running the watches which is derived from IOS. So I hear a question a few times, why we even need to jailbreak a watch? Well, I have a few watches at home, and it should be pretty interesting from a security researcher's point of view. Why not give it a try? Why not try to jailbreak it? Cause it's hmm if I can jailbreak it I have access to filesystem so I can like check the watches internals. And it should be just fun to run ehm debuggers like radare or Frida on this teeny Apple Watch screen. Uhm or use it as iPhone attack vector to send some form of data to the iPhone. So before jumping to uh jailbreak internals let's make a quick overview of the Apple Watch security which starts from secure boot chain where each element in the boot chain is checked ot be properly signed by Apple and it basically stops boot if it's not. The next thing is Mandatory Code Signing, so all code that is running on the watches should be signing by trusted party. The Sandbox which is limited application access to other applications' data and to limit the application access to critical system APIs. Exploit mitigations like address space layout randomization on user mode and the kernel mode keep and stack cookies and of course the data execution prevention. Starting from second generation Apple Watch there is a hardware breakage Secure Enclave Processor and it's used for safe group operations and the data protection, so all user data is encrypted when device is locked. So, after thinking about the possible attack vectors on the watch and the first idea was hmm why not try to send some malformed payload or even like false USB descriptors because there is a teeny port, the debug port on a watch if we [inaudible] some of the things, we see like this small port. the problem here hu the cables from this debug port to USB are not public, so I don't want to mess with all the nonpublic hardware so I decided to switch for other way. the other way will be sent malformed emails or messages or photos from a phone to a watch and try to basically uhm attack the parser. Well, this will be still limited by a sandbox, the parsers, so I need additional out of sandbox bug to continue the jailbreak. Well, starting from watch to Apple added the support for uhm user defined application extensions which is basically the native code, which is running on watch OS, so I decided to stay on this one its looks pretty good and I have freedom on the bug choice. So to choose one of the uh uhm unfixed bugs and and use it for jailbreak. So this how the jailbreak may look like. So first of all we need to locate the kernel memory all the security measurements are in uh kernel so we need to patch the kernel to disable them. For this one we need to break the KSLR and find the kernel base then read the whole kernel to user mode, start analyzing it look for a gadgets setup primitives like read write, then finally patch the kernel to disable security restrictions and to run SSH client on the watch. Well looks do-able. So as I say uh I have a few version of uh watches at home like watch0S2 and watch0S3, and I started looking for a good bug. Uhm for watch0S2 there is a pretty good bug the CVE-4656 which is used to free in the kernel and this bug is famous because it's a part of the Pegasus Exploit Chain. So this may be pretty good candidate as alternative ways to be 4669 which is a mach port register uh it can be exported to 32-bit as a free neuron zone but I decided to stay with Osunserialize because it's more stable. As for watch0S2 there are two bugs, CVE-7644 set dp control, which was eh discovered and exploited by iron beer in uh mock portal jailbreak, and CVE-2370 which is heap overflow in voucher extractor side. It was used later by [inaudible] in yellow 10.2. So I decide to stay with CVE-2370 in watch0S3. Okay, eh, we now know the bugs how to get the kernel level code execution, let's start from leaking the kernel base. There are a few good CVEs eh CVE-4655 and CVE-4680. They both share the same problem. So because during the parsing in the kernel, one of the objects which is like OSNumber object eh it's basically object constructor missing the bound check in, which leads it attacker can create OS number with a high number of bits. And later on this high number of bits will be used as object length and the object length will be used to copy eh how many bytes we need to copy from a kernel stack to a kernel heap and return to the user of length. So this means the kernel stack memory will be leaked and we can a kernel base. And this bug can be triggered from the sandbox. I will check more detailed. So the bug is in a centralized binary which is a method to handle eh binary related data. It converts the binary from format to basic kernel data objects like arrays, strings, boolean, numbers, and so on. And the problem is when ehm the parser goes to OSNumber. The OSNumber represents the eh number object in the kernel. And it basically blindly draws the input argument here the value len and just calls it designated constructor, which is OSNumber in it. And here's a problem, because ehm one of the arguments which is newNumberOfBits is set to one of the class variable size and this size will be used later on on other [inaudible] number of byte. This leaves it a return value number of bytes is fully controlled by attacker and why is that? Well later on in ioregistry property bytes this eh number of bytes method will be used to calculate the OSNumber lens. And which is bad because OSNumber value is stored on the kernel stack eh here it is offset bytes ehm buffer and basically the object lens will be used to copy that many from the kernel stack that it needed from, from this buffer and returned to user mode. As it controls the how many bytes will be copied we can control, well like, 255 bytes and basically leak some of the kernel memory. If the kernel memory will ehm will have any of the kernel pointers it will be enough to determinate the kernel base. So we know the kernel base, now it's time to get some code execution in the kernel end. So, CVE-4656 is eh pretty good, it's used to free in in the same OSUnserialized binary, but in the different branch. Uh the problem here that during the unserialization the OSString object will be delegated but the pointer to the object still start in one of the arrays and later the routine method will be called on this delegated eh data which leads to user to free. So if you can relegate this object with attacker control object like fake OS string which point to attacker control vtable we can get to kernel mode code execution. This is how it's looked like. So, the same OSUnseriializedbinary, which parse the binary encoded [inaudible] OS uhm it's the problem in when the uh object will be eh deserialized and saved to object array so now we see the set of index macro that basically store the pointer of an object but not doing any memory measurement on it like not retaining the object. This is bad because later on when OSstring will be casted to OSsymbol the Osstring object will be delegated but the pointer to it will still exist in one of the arrays. And in a case of KOSSerializedObject which is a reference to an object uh it will be retained which caused the user to free. So, we get the kernel well mode execution and it uh the problem that we need to know what executing the kernel. So basically there are no Watch OS dump available in public in that time as well there is no keys for watchOS kernels. So and to basically to continue jailbreak I need to know some part of a kernel. And I have an idea, why not create a fake OSString, point it to the beginning of the kernel, and basically read the kernel as OSString chunk. Looks doable, eh the problem here that even the object is fake we still need the real vtable offset. And with vtable offset stored in data const which mean basically I need some part of the kernel to dump the kernel. It's like a chicken and egg problem. And I start looking for a way, is there any possibility to dump this vtable just in a runtime? Well, it is. Ehm, as we know the vtable is stored in the data const in the kernel and the data const is referenced in the kernel Mach-0 header. Which means it some how we can leak the kernel here, how we can leak the data const address in a runtime. Uhm and the offset to the data const hmm reference in eh in a header is always constant there are like 0X224 I determined from similar external build. So if in run time we can branch to start of the kernel plus this offset we can leak the data const. And how we can do it just use it as a fake vtable pointer. So the device will crash, the kernel will crash, but we will get uh the data const address. Now if we know the data const I was trying to calculate the offset for vtable by checking uhm, the similar external build from the start of the data const to the vtable. Uh, unfortunately it's not just work out of the box because there are, the kernels are different so I was trying to tune it with plus minus four bytes delta. But in reality it never works. So later on I found it the difference are really significant in the watchOS and the IOS, it's like more than hour kilobytes of difference in a data segment. So, this method is not working. Uhm Okay, just looking there should be some other way to do it. Uhh and I look at OSString layout in the memory. So for 32-bit the size of the object is like 20 bytes, for 64-bits like 32 bytes and the very first pointer of an object is a pointer to object vtable. And which is more interesting the layout of the vtable, so uh our bug triggers a user to free by calling a retain. Retain is like fifth element in array. So I start looking well it should be some way to reuse information. Uhm, here is what what I mean, eh we have the OSString object layout in the memory and the very first pointer of OSString object is point to object vtable. eh and the vtable is pointing somewhere in the kernel called section and our trigger which is OSObject retain is like fifth element in the array. Uh, okay, so how we can use it? What if the object that will be triggered in the bug will be delegated but not relocated? So this memory chunk will be marked as free and which is more interesting ehm, this free memory chunk will be pointing to next free memory chunk in the freelist. This is interesting, ehm this is how it's looked like so we have a node in eh and if released we have like freelist head and know each node is pointing to the next node in the freelist. And again is like the first pointer in ehm this free memory chunk is a pointer to the next free memory. So what if uhm we not relegate OSString object, this memory will be marked as free and uh basically now it's pointing to the next free node in the freelist and this pointer will be interpreted as OSString vtable pointer. So if we call to retain it, it will basically branch out of bounds on the next node. This is what I mean, so again we have the nodes, each node is pointing to other node and uh if it called the user to free without relegating an object eh one of the nodes will be interpreted as OSString. And the beginning of the node is the pointer to the next node and this pointer will be interpreted as a pointer to OSString vtable. Then we call a retain and uh run out of bounds basic- branch out of bounds to the next node. But what if we can control the next node? So if you spray OSString objects ehm make a holes on a heap then basic trigger to use of to free the pointer of to the next node will be interpreted as a vtable which is now branching to the next node but if this next node is OSString it will be branched just in the beginning of the OSString object, which is in the beginning of the vtable. This is what I mean, this is the initial state of the heap. With some memories allocated and a basically heaps sprayed a lot of OSString objects. so we fill memory and trigger some of those OSString delegations so basically now we have holes on the heap and triggered the user to free. One of these holes will be interpreted as OSString objects and pointing ot the next hole on the heap and when we call the retain it will be out of bounds for the next node, which in our case, is OSString. So we will dump vtable in the panic log. Uhm, well despite of this working, uh I get it working 64-bits, I can dump vtables on 64-bit kernels but for certain to be pretty painful to make it work because of the size miss match on the zone size and object size. So I said aw, it should be some other way to do it. Well, I started looking for a similar kernels and I found that the reference to OSString is the OSString vtable is in the IOSunserialized binary. This is interesting because our bug is in IOSunserialized binary and IOSunserialized binary is referenced in IOSunserialized XML. Say okay uhm, if somehow we can leak uh one of the OPcodes from IOSunserialized binary we should like calculated this vtable offset. [mumbles] [drinks water] So, yeah. Should be doable. Uhm, so I crash uhm in IOSunserialized binary during the object desterilization kernel will crash, device will crash, I get the panic log I just copied from a watch uh read the LO registry and now I know where the IOSunserialized binary is uh cleared out over the start of the kernel. Which is interesting because now we can start to dampen the OPcodes by the panic logs. so, we can use the address we want to dump as an address of our fake vtable minus 0X10. This vtable is this uh pointer will be interpreted as a vtable uh and branch to this address, obviously the device will crash but eh the- we will get the kernel panic log and the register state. And the register state will contain basically the PC register, which is pointing to the content of the address. So, I start dampening the kernel by 4-bytes by crashing the device, uhm, using the address as the fake vtable, uh the watch crashed wait until it's restore and now I need to copy panic somehow. that's a problem because it not just work out of the box, so I need to jailbreak my phone access the phone, uh trigger special service which basically copy panic from my watch to phone, then copy to a mac, parse the panic, get the uhm address content, put it in disassembler, get the OPcodes, and update this address with like plus four bytes delta, upload it to watch and repeat. So, it's like you can say it's pretty painful way to dump a kernel. I can say it's pretty fun, yes so. I have a lot of panics and a lot of crashes. Uh once again how this looks like. So, we we need to start damping OPcodes in IOSunserialized XML until we've found the branch to IOSunserialized binary. It's as soon as we leaked this address uh we can start dumping OPcodes in IOSunserialized binary until we've found the reference to OSString vtable. It could take like another ten panics. Again, the full attack plan crash an IOSunserialized XML, dump the 4 bytes, but it in disassembler, read the OPcodes until we find the OP code which is branched into IOSunserialized binary. Leak the int- leak the address and start leaking the IOSunserialized OPcodes until we've found the reference to OSString vtable. uhm, yeah so it takes me like a long time to, to dump a kernel like this way, but it's it's doable. This is how long it take uhm usually takes like five minutes to recover a watch after a panic and it takes another five minutes to fetch a panic from a watch because as I say I need to jailbreak a phone and SSH the phone and like keep trying and triggering the special service that is fetching all the panics. Then we copy to my mac, parse it, and I already don't find any way to optimise the process because I need to recompile the binary all the time and reupload it over to bluetooth to my apple watch. So, it takes me just two weeks to dump the vtable. uh, yeah but now I have the vtable, now I can construct my exploit. I use the fake OSString with the real vtable, pour it to the beginning of the kernel and start reading kernel as OSString chunks. We can even read them in eh user mode using IORegistryEntryGetProperty. So, I leak the kernel header, calculate the kernel size form header, and dump the whole kernel into user mode. Okay, we have a kernel but there is no symbols. So uh the first thing that I've done is start looking for kernel extensions, basically drivers in the kernel which are pulling to this kernel. There are a few ways of doing it. We can look for the XML which is usually at the end of the kernel which describes all the kernel extension Prolink into this kernel. Or in a bad case which can just look for a mark or header magic and if we find the magic we know this is the beginning of the sum of the kernel drivers. So I find the kernel drivers and the next step is to find the system call table. The trick here that system call table not truly changes over the watch OS versions. Which means that Apple at the new syscall at the end of the table but did not truly change the syscalls in the middle of the table. So if we found the beginning of the table we can automatically resolve more than 400 functions and get some bas- and basically get some symbols. The same for mach traps. And finally we can resolve IOkit objects vtables and get like even more symbols. So I have the same [inaudible] kernel and uh starting to analyze it and set up the primitives. We already have the exec primitive we used to free. so all I need to find some interesting gadgets for read and write. yeah, so they are on a slide. Uh we set up the primitives, we get the same disabled kernel and one thing I need is to uh get internal kernel structures layout. In this case there's a proc structure which represents the process structure in the kernel space. Uh and the kernel that's different between the IOS and the watch uh IOS and like I-I cannot even use the new source code. Again, because because of the differences. So I start reconstructing this proc uhm structure just in runtime by um looking for proc underscore functions they are reference to some of the fields in the proc structures so I can reconstruct the layout I need. In some real bad cases I just dump the piece of memory and uh look for constant values. for example the CPU type or the CPU subtype all of this is constant on the watch IOS and I know okay if it's the CPU type it should be like field 0X10 or so. so I see me reconstruct a few of the structure that I need and uh well now it's time to find the patches what to disable in the kernel. I use pretty classic approach called dispatch finder. So when we are looking for a string or byte references find uh uh yeah f-find the pattern find the reference to it and if some additional instruction analysis we can find the beginning of the function or the variable we need or so. Again we're solving the syscalls table will be pretty useful here as we can automatical resolve more than 400 functions. In some bad cases uh our [inaudible] two instruction and emulation was needed to determined it was the sum of the variables allocated. so it was a pretty big win for me, I get the same disabled kernel I get uh internal structures layout I know what to patch. now it's time to patch. Uhm. So, I begin my jailbreak where by getting escalating to root privileges and get out of the sandbox. So, the most easy to do it is to patch the central dysfunction. so there is no patch protection on 32-bit kernels eh including the apple watch so nobody really prevents us to patch basically the kernel and patch the kernel page tables. So I patch out the checks in central ID and just call central ID 00 to to get the root. Or and there is another way if you don't want to patch uh the kernel code. Eh, it tried to heap find the point or two in the old uh proc pointer in this old proc find pointer in the proc structure uhm of our process and this proc structure find the pointer to ucred structure. And in this ucred basically updates your ID area your ID fields and basically becomes root. The same thing is how to get out of the sandbox the same ucred structure just move out the sandbox label no label, no sandbox restrictions So we are out of the sandbox the next thing is uh how to obtain the kernel task the kernel task is eh pretty useful way to write anywhere in the kernel memory, read anywhere, and even allocate the kernel memory. so most obvious way to do it is uh to patch task for pid, is a special api that return uhm the process task to user mode based on the process ID Of course there is a hard coded check if the process ID is zero, which is kernel, return now. So we can patch out this check and just read the kernel task. other way to do it is uhm again enter the heap look for eh alt proc pointer and this alt proc pointer finds the proc structure for the kernel uhm find eh pointer to a task structure in a kernel memory and s-save the send right, which is itself. Just write to our task uhm bootstrap port. Then in user mode we can read it by task get special port it will convert this point or two useful task structure well yeah we get the kernel task port. With the kernel task it's pretty easy now to write anywhere in the kernel memory including the kernel text so I start disable the code sign checks There is a global variable which is called debug which is referenced by functions which like said the debugger capabilities for kernel or one of the drivers so we can set it globally to one or we can patch eh [inaudible] debugger only for eh kernel extensions we need by updating NL symbol pointers. hmm the next thing is update the amfi variables like cs enforcement disable and all invalid signature. This will allow us to run any unsigned code on the apple watch. And the last thing we need is to remount the root filesystem the problem here is that the root filesystem is mounted as read only which means if we want to override the some of the other binaries or just add the new binaries we needed to remount it first to be right, there are a few ways of doing it uh there is a mock mount function in the kernel and this function is checking if the filesystem is a root file system prevent it from remounting so we can patch out this check, call it remount or again it's ready heap find the pointer to rootfs vnode and this uh vnode find the pointer to the mac uh the mount flex and basically remove the check and then remove the flag which represents this filesystem as root filesystem which means this filesystem is not the filesystem we can easily remount uh with a mac mount additionally we need to patch uh lightweight volume manager it's a special kernel uhm extension which is prevent us to write any data to protect it partitions. in our case the root defensive partition so we need to find map for IO uh function and patch our its write protected check in addition said PE i an has debugger capabilities in this light right below manager. So yeah basically we patch the kernel we disable most of the restrictions so now I start to basic compile my payload i recompile the dropbear which is lightweight ssh client for this ARMv7k architecture and recompile the basic tools package like PS, mode, LS and so on put everything in my watch S extension and basically as soon as I get out of sandbox copy to a root filesystem there is a problem here when I spawned the dropbear it got killed it got killed by sandbox which means the watchOS is more restrictions than the IOS so some of the thing that just work on IOS get killed by sandbox in the watch OS so I just found poitners to specific sandbox operations in the kernel and just know the mount okay now it should work uh but I have a surprise here is a list of watchOS interfaces the AWDL0 looks promising which is apple wifi direct but LC now it works only eh bluetooth which may be a problem because I plan to basic connect from my mac to a watch over the SSH and I need to somehow to figure out how to uh run this SSH over the bluetooth. And there is a way uh thanks to for Luca to point me to eh this possibility We can eh use mobile framework uh mobile device framework on my on the mac and send the special message to phone And the fun part that phones should not be jailbroken it's it can be any release phone. so we send a message which say hey please start forwarding service port with port 22 and uh we get a response with eh companion proxy server ports. so basically what what we've done we bind the port on the watch to some random port on the phone And later on we can use this port on the phone to bind it to some port on the mac oops what happened? uhm I guess it it HDMI yep okay uhm so as I said we bind some port on the watch to some random port on the phone and bind this port on the phone to some port on ehm mac and basically over this USB and bluetooth uh we can run this SSH connection and basically it works uh now I can show how uhhh okay yes so yeah the very first thing I need to do is like bind this port and call it like bluetooth proxy which bind port 22 on the watch to eh phone and uh bind the same port from the phone to my mac now it can use just a local host with this port to connect over the bluetooth to the watch yeah we got the shells up it so let's prove that we are running on the apple watch by tuning the uname which is apple watch on uh watch 1 2 okay let's try one of the command line too like psm dump delete all the processes so we see that the watch OS is pretty similar for the IOS for the point of security and the point how the eh operating system work or example we can yeah we can least our dropbear client which is yeah doing SSH connection so as I said I recompile the basic tools like tar, like http to easily archive or unarchive stuff to copy files from the watch but as for me it's pretty painful to copy file from the watch to the phone then copy it from eh phone to a mac. uhm so I found more easier way ehm just http directly from mac to watch and just upload the files so in this case eh I just take procs explorer binary it's a tool written by Jonathan Lewin and yeah copy it to the watch we get a pretty good speed over the bluetooth uh still open yeah so it's copied now lets prove that it's basic on the watch by running our procs explorer. as I say it is a toolit's like similar to PS or TOP but shows the memory pressure and some other useful things so the process called the watchpwnz is our jailbreak. so yeah basically the copying to watch for Now we need to find a way how to easy copy files from the watch to a mac for this purpose I use like SSHT watch tar, basic archives some of the files I need uh and use the pipe to easily just dump everything to my mac laptop like check how it work so yeah it start archiving the product frameworks on the apple watch and I will switch to my mac basically to prove that we got some files copied especially like 3 megabytes of applewatch data was just copied to my mac which is like one single comment. okay this is basically the ssh on the watch uh so I've start looking. okay. We've got the jailbreak on the watch and the watch have a access to SMS calls, even the Photos and emails can be synched to the watch it can fetch GPS location from a phone it in some cases if you use a watch to answer a call it has a microphone usage and even its enrolled in apple pay so it may be pretty interesting to to look what is on a file system so as I said we have a jailbreak so it has full access to file systems and we can look for sql3 databases not limited to the , messages, call history, contacts, emails, and so on so what's going to be next? I've basically recompiled the hooking and engine that can do interposing on trampolining to hook some of the system functions. Which means that I can catch data when the data will be syncing between iphone and the watch. So maybe in the future we will see tweaks for the watch or as I said we can run the frida or the radare on this pretty small watch screen. Okay uhm so as we see the watchOS is pretty secure operating system it's securities equal to IOS but still address some differences and uh some of the techniques that we used on IOS should be adapted for the watchOS And as for me eh the data forensic may be a little bit easier on the watch as on the phone because the phone has all the kernel patch protectors and like hardware restriction which watch doesn't have. Okay uhm basically we still have time for questions, yeah it was an apple watch jailbreak [applause]