Shooting the OS X El Capitan Kernel
Like a Sniper
Liang Chen @chenliang0817
Qidan He @flanker_hqd

About us• Liang Chen
• Senior Security Researcher• Main focus: browser vulnerability research, OS X kernel, Android Root

• Qidan He• Senior Security Researcher• Main focus: Sandbox escape, mobile security, Kernel research• Tencent Security Team Sniper (KeenLab and PC Manager) won Masterof Pwn in this year’s Pwn2Own

Agenda• OS X kernel exploitation mitigation recap• New approach to exploit kernel under sandboxed process• DemoOS X kernel mitigation• kASLR• kslide is assigned upon booting. (Kexts and kernel share the same slide)• DEP• Disallow kernel RWX• SMEP• Disallow kernel code execution from userland address spaceMitigation introduced by El Capitan• SMAP• Disallow memory access from userland address space• Enabled only on supported CPU architecture• From unsupported architecture• Supported architectureMitigation introduced by El Capitan• OOL leak mitigation 1: Structure change in vm_map_copy
Before El Capitan

Kdata pointer is good candidate for AAR with overflowvulnerability

After El Capitan

Kdata pointer is removed

Mitigation introduced by El Capitan• OOL leak mitigation 1: Structure change in vm_map_copy• Still able to achieve limited OOB read, by increasing size field• “OS X kernel is as strong as its weakest part”:
http://powerofcommunity.net/poc2015/liang.pdf• “Free to other zone” approach by @qwertyoruiop:• “Attacking the XNU Kernel in El Capitan”: https://www.blackhat.com/docs/eu15/materials/eu-15-Todesco-Attacking-The-XNU-Kernal-In-El-Capitain.pdf

Mitigation introduced by El Capitan• OOL leak mitigation 2• Introduced in 10.11.1• Changing size field can lead to panic when reading/receiving OOL dataMitigation introduced by El Capitan• OOL leak mitigation 2:What happened
mach_msg_ool_descriptor_t

vm_map_copy

Two redundant size fields

Mitigation introduced by El Capitan• OOL leak mitigation 2• Check mach_msg_ool_descriptor_t.size == mach_msg_ool_descriptor_t.address.size

Panic if size mismatch

What if copy->size ismodified in between?
TOCTTOU? Ah!

Mitigation introduced by El Capitan• OOL leak mitigation• Make general info leak approach harder• Still vulnerable• TOCTTOU issue exists (Although very small time window)• Other approaches• Effective mitigation• Harder kernel exploitation• Even for perfect overflow vulnerability (length + content both controllable)

OS X kernel exploitation requirement• Leak kslide• vm_map_copy followed by vtable object - Mitigated• Leak address pointer of controllable data
• Bypass SMAP/SMEP• Needed by both ROP approach and AAR/AAW primitive approach• mach_port_kobject – Mitigated• Even worse thing is…
• We need perfect overflow bug to achieve those• Many bugs/exploitation approach are not reachable from remote attack
surface (Safari browser)

How about non-perfect write? Even harder…
Remind me the hard time of IE exploitation in 2012...

Memory Spraying• Heap spraying concept on browsers• Helpful to exploitation development (Extremely useful before we got info leak)• Widely used on 32bit systems• Effective when memory is larger than address space• On 64bit systems, less effective
Run the code three times:

Result in:

256 * 4G memory to reliably fill specific data at target address

Memory Spraying in Kernel• OOL vm_map_copy is still good candidate for memory spraying• OOL data keeping in kernel before receiving• But…
• OS X Kernel is 64bit• Address space larger than physical memory• Seems hard?

Memory Spraying in Kernel• Question?
• Is OS X Kernel address space really large (than physical address) ?
• kalloc random?

Memory Spraying in Kernel• Kernel/Kext text base
• Fixed base + kslide• Kslide range : (0x00 – 0xff)<<21, max 0x1fe0 0000
• Address coverage less than 512MB + Kernel + Kext size
• Much smaller than physical memory size

• Kernel/Kext data base
• Fixed base + kslide• Much smaller than physical memory size also

Memory Spraying in Kernel• How about kalloc zone address• zone_map->hdr.links.start• Heavily dependent on kslide
zone_map.hdr.start

kslidezone_map.hdr.start - kslide

0xffffff803b1d4000

0x1c400000

0xffffff801edd4000

0xffffff802071e000

0x1800000

0xffffff801ef1e000

0xffffff80247cc000

0x6a00000

0xffffff801ddcc000

0xffffff803610c000

0x18200000

0xffffff801df0c000• Not too far away from the end of kernel• Allocation starts from low to highMemory Spraying in Kernel• Conclusion• Spray with OOL approach• With more than 512 MB * 2• Reliable (Controllable data at fixed address)

Memory Spraying in Kernel

Memory Spraying in Kernel• Why spraying?
• A good workaround to leak some kalloc-ed address• Locate kernel ROP chain to bypass SMAP/SMEP, thanks to OOL’s sprayingfeature• Other good features to help our “Sniper”• Sniper means remotely (from browser), faraway (address), but reliableCase Study

CVE-2016-1815 – ‘Blit’zard - our P2Obug• This bug lies in IOAcceleratorFamily• A vector write goes out-of-bound under certain carefully preparedsituations (8 IOkit calls) in a newly allocated kalloc.48 block• Finally goes into IGVector::add lead to OOB write

0x28

0x1size

IGV ector

F ake IGV ector

capa

capa

48′ blockstorage deadbeefsize

storage size

F ake IGV ector

capa

controlled 48′ blockstorage••••

rect_pair_t is pair of two rectangles, totally 8 floats, in range [-0xffff, 0xffff](hex)Overwrite starts at storage + 24, ends at storage
In IEEE.754 representation the float is in range [0x3f800000, 0x477fff00], [0xbf800000, 0xc77fff00]
We will not discuss about the detailed reason of this vulnerability here

Found a write-something vulnerability?
• Write anything anywhere – piece of cake• Write *more* *restricted* something anywhere?
• What if you can only write eight floats continuously in range [-0xffff,0xffff]?
• Translate to range• 0x3f800000 3f800000 - 0x477fff00 477fff00• 0xbf800000 bf800000 - 0xc77fff00 c77fff00

Challenges• How to turn it into RIP control?
• Write where? Write what? Stability? Must Sandbox reachable!

• How to defeat kASLR?
• Pwn the Apple with a single bug?

Hard, but not impossible!

Challenge #1• Overwriting vm_map_copy length?
• Apple fixed that in 10.11.1• Still have ways to bypass...

• Not applicable to our vulnerability• Why?

• Adjacent write• Write value qword not good
• 0x3f....3f....
• 0xbf....bf....

• Overwriting some address?

HIGH

LOW

bf

ﬀ80

ﬀ00

ﬀ00

80

bf81

80

ab00

cd00

ef0xﬀﬀﬀ80 81abcdef

IOUserClient
Object

HIGH

ﬀ
ﬀ
ﬀ80
bf81

80

ab00

cd00

ef

bf
80
00
00

LOW

0xﬀﬀﬀ80 81abcdef

IOUserClient
ObjectRAX RSI controllable• Why not overwrite vptr at head of userclients?
• High bytes are 0xffffff7f, address content not controllable• Except RootDomainUserClient• But size too small … problems?
• N*PAGE_SIZE allocations are more reliable and predictable• Speed issues

• Spray Speed decreases as userclient count increases• Why?

• Child IOUserClient need to link to their parent IOService

IORegistryEntry::attachToParent

IORegistryEntry::attachToChild (child already contains refs toparent, No need to call attachToParent again`links` is OSArray
arrayMember performs linear
searchOh man … Total time complexity here: O(N^2)

setObject in makeLinks

Freeing, allocating and copying…

Total Spray Time500

Average Spray Time450
40400

3535030300

252502020015150101005500
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30012345678910 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30(X axis multiply by 0x500*5, y axis in second)

It’s in 2016 and we still have a O(N^2) timecomplexity function in the core of a modern
operating system…

Hey man check your accelerator• Nearly all IOAcceleratorFamily2 userclients have a `service` pointerassociated• Point to IntelAccelerator• Virtual function calls• Heap location starts with 0xffffff80 yeah

• Overwrite it and point it to controllable memory!

• We cannot directly call the fake `service`’s virtual function• Header of vm_map_copy cannot be controlled• An indirect virtual function call is needed• Selector 0x0 (context_finish) is our superstar• Virtual function invoked on service->mEventMachinePreparing memory• Spray 0x50,000 ool_msgs, pushing heap covering 0xffffff80 bf800000(B) with controlled content (ool)• kASLR will push heap location up or pull heap down at each boot• This is a stable fixpoint address reachable in spraying• Higher addresses not applicable• free middle parts of ool, fill with IGAccelVideoContext covering
0xffffff80 62388000 (A)• Perform write at A- 4 + 0x528 descending• Call each IGAccelVideoContext’s externalMethod and detectcorruption

(the offset was 0x1230 in 10.11.3, changed afterwards)

For the record• Now we have known address A covered with IGAccelVideoContext.
• Known address B covered with vm_map_copy content controlled.
• With these in minds lets move further to infoleak

Selector 0x100 of IGAccelVideoContext

AppleIntelBDWGraphics::get_hw_steppings come to rescue!

Leaking strategy• By spraying we can ensure 0xf… 62388000(A) (lies anIGAccelVideoContext• And 0xf... Bf800000(B) lies an vm_map_copy with size 0x2000• Overwrite the service pointer to B, point to controlled vm_map_copy
filled with 0x4141414141414141 (at 0x1288 set to A - 0xD0)• Test for 0x41414141 by calling get_hw_steppings on sprayed
userclients• If match, we get the index of userclient being corrupted• a2[4] returns a byte at A!

service field+0x528IntelAccelerator…

IGAccelVideoCont IGAccelVideoCont
ext
ext
0xﬀ… 62388000…

vm_map_copy

vm_map_copy

0xﬀ… bf800000

KALLOC.8192 ZONE

service field+0x528IntelAccelerator…

…vm_map_copy…

IGAccelVideoCont IGAccelVideoCont
ext
ext

…

0xﬀ… 62388000

vm_map_copy

vm_map_copy

0xﬀ… bf800000+0x528IntelAccelerator…

vm_map_copy…

IGAccelVideoCont IGAccelVideoCont
ext
ext

0xﬀ… 62388000

KALLOC.8192 ZONE…

vm_map_copy

vm_map_copy

0xﬀ… bf800000

+0x528
IntelAccelerator…

vm_map_copy…

IGAccelVideoCont IGAccelVideoCont
ext
ext

0xﬀ… 62388000…

vm_map_copy

vm_map_copy

0xﬀ… bf800000+0x1288+0x1140bf800000

bf801000
vm_map_copy header

niddle(filled 0x41414141)

KALLOC.8192 ZONE

filled with 0x41414141

Leaking strategy?
• Wait… what if the predict address fall at the 1st page instead of 0th?
• Middle of userclients - 50% chance• Middle of vm_map_copy - 50% chance• Write twice to ensure 100% success rate

• OOB write at A and A+0x1000• A - 0xD0 both at 0x1288 and 0x288 for vm_map_copy+0x1000 lies 0+0x528
IntelAccelerator…

vm_map_copy…

IGAccelVideoCont IGAccelVideoCont
ext
ext

0xﬀ… 62388000…

vm_map_copy

vm_map_copy

0xﬀ… bf800000+0x1288+0x1140bf800000

bf801000
vm_map_copy header

niddle(filled 0x41414141)

KALLOC.8192 ZONE

filled with 0x41414141

0xﬀ… 62388000+0x1528
IntelAccelerator…

vm_map_copy…

0xﬀ… 6238a000

0xﬀ… bf800000+0x528

IGAccelVideoCont IGAccelVideoCont
ext
ext

…

vm_map_copy

vm_map_copy

0xﬀ… 62389000+0x140 +0x288

0xﬀ… bf7ﬀ000+0x1140 +0x1288

0xﬀ… bf800000
vm_map_copy header

niddle(filled 0x41414141)

KALLOC.8192 ZONE

0xﬀ… bf801000
filled with 0x41414141

vm_map_copy

0xﬀ… 62388000+0x1528
IntelAccelerator…

vm_map_copy…

0xﬀ… 6238a0000xﬀ… bf800000+0x528

IGAccelVideoCont IGAccelVideoCont
extext…

vm_map_copy

vm_map_copy

0xﬀ… 62389000+0x140 +0x288

0xﬀ… bf7ﬀ000+0x1140 +0x1288

0xﬀ… bf800000
vm_map_copy header

niddle(filled 0x41414141)

KALLOC.8192 ZONE

0xﬀ… bf801000
filled with 0x41414141

vm_map_copyFill ool_msg with service offset point to0xf… …N

Trigger IOConnect
Callredo with vptr value

let N=N+1, freeand refill ool_msgs

8 bytes all leaked?

KEXT vptr leakedkernel offset
leakedLeaked byte zero?

Replace with …N+1000

Leaking strategy• We can use an additional read to determine if the address is at A orA+0x1000• If we try A but its actually at A+0x1000, we will read byte at +0x1000 ofIGAccelVideoContext, which is 0, then we can try again with A+0x1000 to readthe correct value• Free and fill the vm_map_copy living at B to increment the location toread by 1 byte
• Free and fill vm_map_copy , modified with leaked vptr to leak kernel
section offset, thus kslide• Better way exists - exercise for readers J

Final workflow• Spray 0x50000 ool_msgs with data size 0x2000 (2GB), taint with0x41414141, write A at 0x1288 and 0x288 offset• Free middle parts of ool_msgs, fill in IGAccelVideoContext• Trigger oob write at A - 0x4 + 0x528 and A -4 + 0x528 +0x1000• Iterate all opened IGAccelVideoContext userclients, call get_hw_steppingsand look for 4141, adjust 0x1288 and 0x288 if needed• Change to A+0x1000 if 0 got• Advance read location 1byte by 1, read out KEXT vtable address and thenkern address offset• Refill ool_msgs bundled with ROP chain, call context_finish• Pwn

Conclusion• We discussed previous exploitation techniques and their exploitations• We present a new generalized exploitation technique working evenon restricted OOB write abstracted from our `blitzard` exploitation

Credits• Marco Grassi• Qoobee• Wushi• Windknown• qwertyoruiop• UfotalentDemo && Questions?
• POC will be available at https://github.com/flankerhqd/blitzard/ in afew weeks• We will talk about the `blitzard` itself internals at Las Vegas Blackhat
USA 2016, see you there J