The external method 0x206 of IGAccelGLContext is gst_configure. This method takes an arbitrary sized input structure
(passed in rsi) but doesn't check the size of that structure (passed in rcx.)
__text:000000000002A366 __ZN16IGAccelGLContext13gst_configureEP19GstConfigurationRecS1_jPj proc near
__text:000000000002A366 ; DATA XREF: __const:000000000005BF88o
__text:000000000002A366 push rbp
__text:000000000002A367 mov rbp, rsp
__text:000000000002A36A push r15
__text:000000000002A36C push r14
__text:000000000002A36E push r12
__text:000000000002A370 push rbx
__text:000000000002A371 mov rax, rdx
__text:000000000002A374 mov r15, rsi ; <-- r15 points to controlled mach message data
__text:000000000002A377 mov r14, rdi
__text:000000000002A37A mov edx, [r15+800h] ; <-- size never checked -> oob read
__text:000000000002A381 cmp edx, 200h
__text:000000000002A387 jbe short loc_2A3AD
__text:000000000002A389 lea rdi, aIgaccelglcon_0 ; "IGAccelGLContext::%s Error: Number of e"...
__text:000000000002A390 lea rsi, aGst_configure ; "gst_configure"
__text:000000000002A397 mov ecx, 200h
__text:000000000002A39C xor eax, eax
__text:000000000002A39E call _IOLog
here we can see that the method is reading a dword at offset 0x800 of the input struct and comparing that value to 0x200.
This method is reached via MIG and if we call userspace IOConnectCallMethod with a small input struct then the mach
message is actually packed such that only the input struct size we send actually gets sent; therefore this is an OOB read.
The first interesting conseqeuence of this is that if the value read is > 0x200 then it gets logged to /var/log/system.log
which we can read from userspace allowing us to disclose some kernel memory.
However, we can do more:
r15 is passed to IntelAccelerator::gstqConfigure:
mov rsi, r15
call __ZN16IntelAccelerator13gstqConfigureEP19GstConfigurationRec
where we reach the following code:
__text:000000000001DC29 mov edx, [rsi+800h]
__text:000000000001DC2F shl rdx, 2 ; size_t
__text:000000000001DC33 lea rdi, _gstCustomCounterConfigPair ; void *
__text:000000000001DC3A call _memcpy
here the value at +0x800 is read again and used as the size for a memcpy assuming that it has already been verified, but
since it's outside the bounds of the allocation this is actually a toctou bug since with some heap manipulation we can
change that value to be > 0x200 allowing us to overflow the _gstCustomCounterConfigPair buffer.
Since the struct input comes from a mach message this heap grooming shouldn't be that difficult.
clang -o ig_gl_gst_oob_read ig_gl_gst_oob_read.c -framework IOKit
repro: while true; ./ig_gl_gst_oob_read; done
Tested on OS X ElCapitan 10.11.1 (15b42) on MacBookAir5,2