Ian Beer did an incredible work with his iOS 10.1.1 exploit. The mach_portal proof of concept gives you a root shell on iOS 10.1.1. You can read more about it here:

https://bugs.chromium.org/p/project-zero/issues/detail?id=965

While playing with it, I discovered that the amfid patch was only supporting thin arm64 binaries. I did not find a fix online so here is my solution.

amfid patch

In this PoC amfid is patched to allow any signatures and entitlements. The amfid patch searches for the LC_CODE_SIGNATURE blob, calculate the expected SHA1 checksum and write it.

However as mentioned at the top of the file cdhash.c:

this code has very minimal mach-o parsing - it works for thin arm64 binaries though

And effectively if you run a fat binary (arm64 and armv7), you will get a kernel panic after:

got exception message from amfid!

got thread state

got filename for amfid request: /private/var/containers/Bundle/Application/7066B0A5-BFDC-4C60-B08B-3C64FF98FFDB/mach_portal.app/iosbinpack64/usr/bin/printHello

[-] too many load commands

To support fat binaries, we just need to improve the function that searches for the LC_CODE_SIGNATURE blob. Here is the original find_cs_blob() function:

void* find_cs_blob(uint8_t* buf, size_t size) { struct mach_header_64* hdr = (struct mach_header_64*)buf; uint32_t ncmds = hdr->ncmds; assert(ncmds < 1000, "too many load commands"); uint8_t* commands = (uint8_t*)(hdr+1); for (uint32_t command_i = 0; command_i < ncmds; command_i++) { //assert(commands + sizeof(struct load_command) < end, "invalid load command"); struct load_command* lc = (struct load_command*)commands; //assert(commands + lc->cmdsize <= end, "invalid load command"); if (lc->cmd == LC_CODE_SIGNATURE) { struct linkedit_data_command* cs_cmd = (struct linkedit_data_command*)lc; printf("found LC_CODE_SIGNATURE blob at offset +0x%x

", cs_cmd->dataoff); return ((uint8_t*)buf) + cs_cmd->dataoff; } commands += lc->cmdsize; } return NULL; }

You can see the assert ‘too many load commands’ that is printed just before the kernel panic. As you can see, this function expects a mach_header_64 and don't support a fat header.

find_cs_blob() with fat support

To solve this issue, we need to:

detect a fat binary

find the correct fat_arch for the current cpu type and cpu subtype

find the file offset of the fat_arch

use the file offset to get the mach header and correct LC_CODE_SIGNATURE blob

Below is the updated find_cs_blob() function:

void* find_cs_blob(uint8_t* buf, size_t size) { uint32_t fileOffset = 0; uint32_t magic = *(uint32_t*)buf; if(ntohl(magic) == FAT_MAGIC) { printf("found a fat header

"); // Get the cputype and cpusubtype of the mach_portal binary struct mach_header_64 *mainMachHeader = (struct mach_header_64 *)_dyld_get_image_header(0); cpu_type_t mainCpuType = mainMachHeader->cputype & ~CPU_ARCH_MASK; cpu_type_t mainCpuSubType = mainMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK; struct fat_header *fatHeader = (struct fat_header *)buf; struct fat_arch *fatArch = (struct fat_arch *)(buf + sizeof(struct fat_header)); for(int i = 0 ; i < ntohl(fatHeader->nfat_arch) ; i++, fatArch++) { cpu_type_t cpuType = ntohl(fatArch->cputype) & ~CPU_ARCH_MASK; cpu_subtype_t cpuSubType = ntohl(fatArch->cpusubtype) & ~CPU_SUBTYPE_MASK; if(cpuType == mainCpuType && cpuSubType == mainCpuSubType) { fileOffset = ntohl(fatArch->offset); printf("arm64 arch offset is %u

", fileOffset); fatHeader++; break; } } if (fileOffset == 0) { printf("arch not found in fat header

"); } } struct mach_header_64* hdr = (struct mach_header_64*)(buf + fileOffset); uint32_t ncmds = hdr->ncmds; assert(ncmds < 1000, "too many load commands"); uint8_t* commands = (uint8_t*)(hdr+1); for (uint32_t command_i = 0; command_i < ncmds; command_i++) { //assert(commands + sizeof(struct load_command) < end, "invalid load command"); struct load_command* lc = (struct load_command*)commands; //assert(commands + lc->cmdsize <= end, "invalid load command"); if (lc->cmd == LC_CODE_SIGNATURE) { struct linkedit_data_command* cs_cmd = (struct linkedit_data_command*)lc; printf("found LC_CODE_SIGNATURE blob at offset +0x%x

", cs_cmd->dataoff); return (((uint8_t*)buf + fileOffset)) + cs_cmd->dataoff; } commands += lc->cmdsize; } return NULL; }

Note that you will need to include:

#include <mach-o/dyld.h> #include <mach-o/fat.h>

Running a fat binary

Running a fat binary doesn't trigger a kernel panic anymore:

Downloads

You can download the complete modified cdhash.c file here.