Jailbreaking Apple Watch

Jailbreaking Apple Watch

DEFCON 25, Las Vegas, NV

52de428e5d6112f5d989187bb89d82c4?s=128

Max Bazaliy

July 27, 2017
Tweet

Transcript

  1. 1.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Max Bazaliy Jailbreaking Apple Watch
  2. 2.

    July 27-30, 2017 whoami 1 2 3 4 5 6

    7 8 9 10 11 12 o  Security researcher at Lookout o  Lead researcher on Pegasus exploit chain o  Focused on advanced exploitation techniques o  Fried Apple team co-founder o  iOS/tvOS/WatchOS jailbreak author
  3. 3.

    July 27-30, 2017 o  Released in 2015 o  Apple S1/S2

    processor o  ARMv7k 32 bit architecture o  Taptic engine o  512 MB RAM o  WatchOS 1 2 3 4 5 6 7 8 9 10 11 12 What is Apple Watch ?
  4. 4.

    July 27-30, 2017 o  Access to file system o  Run

    tools like radare or frida on a watch o  iPhone attack vector 1 2 3 4 5 6 7 8 9 10 11 12 Why to jailbreak a watch ?
  5. 5.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Apple Watch security o  Secure boot chain o  Mandatory Code Signing o  Sandbox o  Exploit Mitigations o  Secure Enclave Processor (2-nd gen only) o  Data Protection
  6. 6.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Possible attack vectors o  Malformed USB descriptor over debug port Debug port
  7. 7.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Possible attack vectors o  Malformed email, message, photo, etc Still limited by sandbox o  Application extension based More freedom on bug choice
  8. 8.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Jailbreak step by step o  Leak kernel base o  Dump whole kernel o  Find gadgets and setup primitives o  Disable security restrictions o  Run ssh client on a watch
  9. 9.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Bugs of interest o  WatchOS 2.x - CVE-2016-4656 - osunserialize bug - CVE-2016-4669 - mach_port register bug o  WatchOS 3.1.3 - CVE-2016-7644 - set_dp_control_port bug - CVE-2017-2370 - voucher extract recipe bug
  10. 10.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 Leaking kernel base o  CVE-2016-4655 and CVE-2016-4680 o  Object constructor missing bounds checking o  OSNumber object with high number of bits o  Object length used to copy value from stack o  Kernel stack memory leaked o  Can be triggered from an app’s sandbox
  11. 11.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 OSObject * OSUnserializeBinary(const char *buffer, size_t bufferSize, OSString **errorString) { uint32_t key, len, wordLen; len = (key & kOSSerializeDataMask); ... case kOSSerializeNumber: bufferPos += sizeof(long long); if (bufferPos > bufferSize) break; value = next[1]; value <<= 32; value |= next[0]; o = OSNumber::withNumber(value, len); next += 2; break; No number length check
  12. 12.

    July 27-30, 2017 1 2 3 4 5 6 7

    8 9 10 11 12 bool OSNumber::init(unsigned long long inValue, unsigned int newNumberOfBits) { if (!super::init()) return false; size = newNumberOfBits; value = (inValue & sizeMask); return true; } unsigned int OSNumber::numberOfBytes() const { return (size + 7) / 8; } No number length check Return value is under control
  13. 13.

    July 27-30, 2017 kern_return_t is_io_registry_entry_get_property_bytes( io_object_t registry_entry, io_name_t property_name, io_struct_inband_t

    buf, … ) { ... UInt64 offsetBytes; // stack based buffer ... } else if( (off = OSDynamicCast( OSNumber, obj ))) { offsetBytes = off->unsigned64BitValue(); len = off->numberOfBytes(); bytes = &offsetBytes; ... if (bytes) { if( *dataCnt < len) ret = kIOReturnIPCError; else { *dataCnt = len; bcopy( bytes, buf, len ); // copy from stack based buffer Will be returned to userland We control this value Points to stack based buffer 13 14 15 16 17 18 19 20 21 22 23 24
  14. 14.

    July 27-30, 2017 CVE-2016-4656 exploitation o  Kernel mode UAF in

    OSUnserializeBinary o  OSString object deallocated o  retain() called on deallocated object o  Fake object with fake vtable –> code exec 13 14 15 16 17 18 19 20 21 22 23 24
  15. 15.

    July 27-30, 2017 OSObject * OSUnserializeBinary(const char *buffer, size_t bufferSize,

    …) { newCollect = isRef = false; ... case kOSSerializeDictionary: o = newDict = OSDictionary::withCapacity(len); newCollect = (len != 0); break; ... if (!isRef) { setAtIndex(objs, objsIdx, o); if (!ok) break; objsIdx++; } Save object to objs array 13 14 15 16 17 18 19 20 21 22 23 24
  16. 16.

    July 27-30, 2017 if (dict) { if (sym) … else

    { sym = OSDynamicCast(OSSymbol, o); if (!sym && (str = OSDynamicCast(OSString, o))) { sym = (OSSymbol *) OSSymbol::withString(str); o->release(); o = 0; } ok = (sym != 0); } } case kOSSerializeObject: if (len >= objsIdx) break; o = objsArray[len]; o->retain(); isRef = true; break; Object saved to objs array destroyed Deallocated object retained 13 14 15 16 17 18 19 20 21 22 23 24
  17. 17.

    July 27-30, 2017 o  Problem: No WatchOS kernel dumps o 

    No keys for WatchOS kernels o  Idea: read kernel as OSString chunks o  vtable offset required to fake OSString o  vtable stored in __DATA.__const in kernel Dumping kernel 13 14 15 16 17 18 19 20 21 22 23 24
  18. 19.

    July 27-30, 2017 Getting vtable - __DATA.__const leak o  __DATA.__const

    address is in Mach-O header o  Kernel base + 0x224 == __DATA.__const o  Deref and branch to address via fake vtable 13 14 15 16 17 18 19 20 21 22 23 24
  19. 20.

    July 27-30, 2017 Getting vtable - known offset o  Get

    vtable offset from similar XNU build o  Known delta from __DATA.__const start o  Tune address with +/- delta 13 14 15 16 17 18 19 20 21 22 23 24
  20. 21.

    July 27-30, 2017 Getting vtable - known offset o  Get

    vtable offset from similar XNU build o  Known delta from __DATA.__const start o  Tune address with +/- delta 13 14 15 16 17 18 19 20 21 22 23 24
  21. 22.

    July 27-30, 2017 Getting vtable – OSString layout … OSObject::retain()

    … vtable ptr + 0x8 retain count flags length string ptr vtable ptr + 0x8 retain count flags length string ptr OSObject::retain() 0x0 0x4 0x8 0xC 0x10 0x0 0x8 0x10 0x18 0x0 0x4 0x8 0xC 0x10 0x0 0x8 0x10 0x18 0x20 OSString 32 bit OSString 64 bit 13 14 15 16 17 18 19 20 21 22 23 24 size == 0x14 size == 0x20 … … 0x20 0x14 … … … … 0x28 0x14
  22. 24.

    July 27-30, 2017 OSString layout OSString vtable pointer OSObject::retain() offset

    0x20 13 14 15 16 17 18 19 20 21 22 23 24 OSString object OSString object vtable Kernel code section … …
  23. 25.

    July 27-30, 2017 o  vtable ptr is first 4/8 bytes

    of a on object o  What if object is not reallocated ? o  Memory marked as free o  New node pointing to next node in freelist Getting vtable – next free node trick 25 26 27 28 29 30 31 32 33 34 35 36
  24. 26.

    July 27-30, 2017 Heap zone freelist Next node pointer Freelist

    head 25 26 27 28 29 30 31 32 33 34 35 36
  25. 27.

    July 27-30, 2017 o  OSString memory marked as free o 

    Now it’s a node pointing to next node o  Next node ptr will be interpreted as vtable o  Call to retain() will branch out of node bounds o  What if OSString size == retain() offset ? o  We can branch out to the start of next node Getting vtable – next free node trick 25 26 27 28 29 30 31 32 33 34 35 36
  26. 28.

    July 27-30, 2017 Next node ptr as a vtable ptr

    Interpreted as OSString Interpreted as OSString vtable pointer Interpreted as retain() 25 26 27 28 29 30 31 32 33 34 35 36
  27. 29.

    July 27-30, 2017 o  Heap spray OSString objects o  Free

    few OSString’s o  Next free chunk pointer dereferenced as vtable o  Free chunk is surrounded by OSStrings o  retain() -> OOB branch to next OSString Getting vtable – next free node trick 25 26 27 28 29 30 31 32 33 34 35 36
  28. 30.

    July 27-30, 2017 Heap spray and OOB branch to vtable

    25 26 27 28 29 30 31 32 33 34 35 36 Used memory chunks
  29. 31.

    July 27-30, 2017 Heap spray and OOB branch to vtable

    25 26 27 28 29 30 31 32 33 34 35 36 Allocated OSString objects Used memory chunks
  30. 32.

    July 27-30, 2017 Heap spray and OOB branch to vtable

    Allocated OSString objects Used memory chunks 25 26 27 28 29 30 31 32 33 34 35 36 Deallocated OSString objects
  31. 33.

    July 27-30, 2017 Heap spray and OOB branch to vtable

    Allocated OSString objects Used memory chunks 25 26 27 28 29 30 31 32 33 34 35 36 Deallocated OSString objects Out of bounds branch to next OSString vtable
  32. 34.

    July 27-30, 2017 o  Heap spray OSString objects o  Make

    few OSDictionary with OSString o  Trigger OSDictionary deallocation o  retain() -> deref next free chunk pointer o  Free chunk is surrounded by OSStrings o  retain() -> OOB branch to next OSString node Getting vtable – next free node trick 25 26 27 28 29 30 31 32 33 34 35 36
  33. 35.

    July 27-30, 2017 Getting vtable – dump over panic o 

    OSString vtable reference in OSUnserializeBinary! o  OSUnserializeBinary reference in OSUnserializeXML 25 26 27 28 29 30 31 32 33 34 35 36
  34. 37.

    July 27-30, 2017 Getting vtable – dump over panic o 

    Crash in OSUnserializeBinaryXML o  Copy panic log from a watch o  Get LR register value from panic o  We got OSUnserializeBinaryXML address 37 38 39 40 41 42 43 44 45 46 47 48
  35. 38.

    July 27-30, 2017 Dumping kernel by panic logs o  retain()

    offset in vtable is 0x10 o  Use address to leak as vtable_addr - 0x10 o  vtable will be interpreted and branch to address o  Kernel will crash, but save panic log o  Address content appear in panic registers state 37 38 39 40 41 42 43 44 45 46 47 48
  36. 39.

    July 27-30, 2017 Dumping kernel by 4 bytes o  Use

    address to leak as fake vtable address o  Watch will crash, wait until it restore o  ssh to a iPhone and run synchronization service o  Copy panic from Watch to iPhone and to Mac o  Parse panic, read 4 bytes and disassemble ! o  Update address with 4 bytes delta and upload app o  Repeat 37 38 39 40 41 42 43 44 45 46 47 48
  37. 40.
  38. 41.

    July 27-30, 2017 OSString vtable in kernel OSString vtable offset

    OSUnserializeBinary address 37 38 39 40 41 42 43 44 45 46 47 48
  39. 42.

    July 27-30, 2017 Getting vtable – final steps o  Crash

    in OSUnserializeXML o  Dump 4 bytes, disassemble, read opcode o  Leak opcode until ‘BL OSUnserializeBinary’ o  Leak OSUnserializeBinary opcodes o  Finally leak OSString vtable offset 37 38 39 40 41 42 43 44 45 46 47 48
  40. 44.

    July 27-30, 2017 Getting vtable – results o  5 minutes

    for recover watch after crash o  5 minutes to fetch panic from watch o  2 minutes to copy to Mac and parse o  No way to automate a process o  It takes me just 2 weeks to dump a vtable 37 38 39 40 41 42 43 44 45 46 47 48
  41. 45.

    July 27-30, 2017 Next step – full kernel dump o 

    Now use fake OSString obj to read kernel o  Read data via IORegistryEntryGetProperty o  Leak kernel header, calculate kernel size o  Dump full kernel to userland by chunks 37 38 39 40 41 42 43 44 45 46 47 48
  42. 46.

    July 27-30, 2017 Next step – kernel symbolication o  Find

    and list all kexts o  Find sysent and resolve syscalls o  Find and resolve mach traps o  Resolve IOKit objects vtable 37 38 39 40 41 42 43 44 45 46 47 48
  43. 47.

    July 27-30, 2017 Next step – setting up primitives o 

    Scan kernel dump for gadgets o  Set up exec primitive o  Set up kernel read & write primitives STR R1, [R2] BX LR LDR R1, [R2] BX LR 37 38 39 40 41 42 43 44 45 46 47 48
  44. 48.

    July 27-30, 2017 Next step – kernel structs layout o 

    Look for proc_* functions o  Restore proc structure layout o  Dump memory, check for known values 37 38 39 40 41 42 43 44 45 46 47 48
  45. 49.

    July 27-30, 2017 Next step – patchfinder o  memmem string

    \ byte pattern o  + xref + instruction analysis o  Resolve syscalls table, mach traps table o  Simple instruction emulation 49 50 51 52 53 54 55 56 57 58 59 60
  46. 50.

    July 27-30, 2017 Next step – kernel structs layout o 

    memmem string \ byte pattern o  + xref + instruction analysis o  Resolve syscalls table, mach traps table o  Simple instruction emulation 49 50 51 52 53 54 55 56 57 58 59 60
  47. 51.

    July 27-30, 2017 Getting root and sandbox bypass o  Patch

    setreuid (no KPP) o  patch ucred in proc structure in kernel o  patch sandbox label value in ucred 49 50 51 52 53 54 55 56 57 58 59 60
  48. 52.

    July 27-30, 2017 Getting kernel task o  Patch task_for_pid() o 

    Or save kernel sself in task bootstrap port o  Read it back via task_get_special_port() o  Restore original bootstrap port value 49 50 51 52 53 54 55 56 57 58 59 60
  49. 53.

    July 27-30, 2017 Disable codesign checks o  Patch _debug to

    1 o  patch _nl_symbol_ptr (got) entries o  Patch amfi variables - cs_enforcement_disable - allow_invalid_signatures 49 50 51 52 53 54 55 56 57 58 59 60
  50. 54.

    July 27-30, 2017 Remount rootfs o  Patch __mac_mount o  Change

    flags in rootfs vnode and mount RW o  Patch lwvm is_write_protected check o  Patch PE_i_can_has_debugger in lwvm 49 50 51 52 53 54 55 56 57 58 59 60
  51. 55.

    July 27-30, 2017 Spawning ssh client o  Compile dropbear for

    ARMv7k o  Compile basic tools package for ARMv7k o  Problem: More sandbox restrictions o  Remove WatchOS specific sandbox ops 49 50 51 52 53 54 55 56 57 58 59 60
  52. 56.

    July 27-30, 2017 ssh connection problem… "awdl0/ipv6" = "fe80::c837:8аff:fe60:90c2"; "lo0/ipv4”

    = "127.0.0.1"; "lo0/ipv6" = "fe80::1"; "utun0/ipv6" = "fe80::face:5e30:271e:3cd3"; o  WatchOS interfaces 49 50 51 52 53 54 55 56 57 58 59 60
  53. 58.

    July 27-30, 2017 Watch <-> iPhone port forwarding NSDictionary *comm

    = @{! @"Command" :@"StartForwardingServicePort", @"ForwardedServiceName" :@"com.apple.syslog_relay",! @"GizmoRemotePortNumber" :[NSNumber numberWithUnsignedShort: pt],! @"IsServiceLowPriority" :@0,};! ! AMDServiceConnectionSendMessage(serviceConnection,! (__bridge CFPropertyListRef)(comm), kCFPropertyListXMLFormat_v1_0);! ! AMDServiceConnectionReceiveMessage(serviceConnection, &response, (CFPropertyListFormat*)&format);! ! NSNumber *iphone_port = response[@"CompanionProxyServicePort"];! Thanks to Luca Todesco 49 50 51 52 53 54 55 56 57 58 59 60
  54. 60.
  55. 61.

    July 27-30, 2017 Apple Watch usage o  Watch has access

    to SMS, Calls, Health o  Photos and emails synced to Watch o  Fetch GPS location from the phone o  Microphone usage o  Apple Pay 61 62 63 64 65 66 67 68 69 70 71 72
  56. 63.

    July 27-30, 2017 Interesting findings o  Full access to jailbroken

    watch file system o  Including sqlite3 databases - Messages - Call history - Contacts - Emails 61 62 63 64 65 66 67 68 69 70 71 72
  57. 64.

    July 27-30, 2017 What's next ? o  Interpose or trampoline

    system functions o  Catch data on sync with a iPhone o  Create tweaks for a watch o  Run frida and radare 61 62 63 64 65 66 67 68 69 70 71 72
  58. 65.

    July 27-30, 2017 Takeaways o  WatchOS security is equal to

    iOS o  But new techniques required o  Easier data forensics on a Watch 61 62 63 64 65 66 67 68 69 70 71 72
  59. 66.

    July 27-30, 2017 References o  Lookout - Technical Analysis of

    the Pegasus Exploits on iOS o  Luca Todesco - com.apple.companion_proxy client o  Siguza - tfp0 powered by Pegasus o  Stefan Esser - iOS 10 - Kernel Heap Revisited 61 62 63 64 65 66 67 68 69 70 71 72