KSMA: Breaking Android kernel isolation and Rooting with ARM MMU features
WANG, YONG a.k.a. ThomasKing(@ThomasKing2014)
Pandora Lab of Ali Security

About• WANG, YONG a.k.a. ThomasKing(@ThomasKing2014)• Security Engineer in Pandora Lab of Ali Security, Alibaba Group• Focus on Security Research of Android• Android vulnerability hunting and exploitation since 2015

Agenda• Present Situation of Android Rooting• ReVent Rooting Solution• Kernel Space Mirroring Attack - KSMA
• CPRooter Rooting Solution• Conclusion

Some very interesting codeSome very interesting code

Demo

Present Situation of Android Rooting

Present situation• Memory corruption vulnerabilities in drivers• Lots of vulnerabilities (Android Bulletin)• Need to comprise an associated privileged process first• Fewer vulnerabilities in universal drivers (Binder, etc.)• Memory corruption vulnerabilities in generic syscall• Attractive• Not easy to discover a vulnerability

Present situation• Privileged processes• Fewer vulnerabilities• More strict SELinux policies• ROP/JOP due to “EXEC_MEM” policy• Attack surface reduction• Remove default access to debug features (perf)• Restrict app access to ioctl commands• Seccomp filter in Android 8

New mitigations in Android 8• Privileged Access Never (PAN)• No longer redirect a kernel pointer to user space• Kernel Address Space Layout Randomization(kernel 4.4 and newer)• Need to leak the kernel slide• Post-init read-only memory• Fewer kernel pointers can be overwritten• Hardened usercopy• Fewer vulnerabilities in drivers

ReVent Rooting Solution

CVE-2017-7533• Discovered as a bug by Leilei Lin• Exploitation for Android unknown by that time
• Shipped with kernel 3.18 – 4.4• 64-bit devices• Use-After-Free due to race condition• Overwrite the next slab object with non-zero bytes• ReVent – [Re]name & E[vent]Vulnerability analysis• Monitor one file with actions(IN_ACCESS)• inotify_init• inotify_add_watch• When triggered:• Calculate file name’s length• Allocate a buffer for notification event• Copy file name to event buffer• But the file can be renamed!

Vulnerability analysis
Thread-1

Thread-2

strlen(file_name)kfree_rcu(old_name, u.head);kmalloc(alloc_len, GFP_KERNEL);add_key("spray", payload, 0, 0, 0);
strcpy(event->name, file_name);

Heap overflow

Vulnerability analysis
Kmalloc-256
200

freed

…

freed

freed

…

AAAA…

T1: trigger

200

200

…

249

freed

…

event

T1: kmalloc

freed

200

…

249

freed

…

BBBB…

T2: rename

256

200

…

249

freed

…

Non-zeroT2: spray

256

200

…

freed

…

Overflow

T1: strcpy

44

Two main problems• Victim object• Kernel pointer in the head
• Immunity to ‘\0’ side effect• Heap Fengshui• Name/Event/Payload/Victim object• Victim object should be next to eventPipe subsystem• Time Of Check To Time Of Use• The value and time are controllable when reading/writingPipe subsystem• Time Of Check To Time Of Use• The value and time are controllable when reading/writing• readv/writev a pipe file
• Allocate, import iovecs and check boundaryPipe subsystem• Time Of Check To Time Of Use• The value and time are controllable when reading/writing• readv/writev a pipe file
• Allocate, import iovecs and check boundary• Invoke pipe_read/write callback• No data/space blocking in callback

Pipe subsystem• Time Of Check To Time Of Use• The value and time are controllable when reading/writing• readv/writev a pipe file
• Allocate, import iovecs and check boundary• Invoke pipe_read/write callback• No data/space blocking in callback• No other boundary checkPipe subsystem• Time Of Check To Time Of Use• The value and time are controllable when reading/writing• readv/writev a pipe file
• Allocate, import iovecs and check boundary• Invoke pipe_read/write callback
User_addr• No data/space blocking in callback• No other boundary check• IOVECs - ideal victim object• Gain almost arbitrary R/W

Kernel_addr

Iov_len=8…

iov_len=8

Iov_len=8…

iov_len=8

Limitations• Target kernel address may contain ’0’ bytes• 0xFFFFFFC000D0E1CC• kernel data contains ideal callback pointers• Spawn lots of threads• The reading/writing threads block in callback function

Ideal heap layout
Kmalloc-256
1

freed

freed

freed

…

freed

freed

…

2

iovs

iovs

iovs

…

iovs

iovs

…

3

freed-iovs

iovs

freed-iovs…

freed-iovs

iovs

…

4

nameiovs

freed-iovs

…

event

iovs

…

5

payload

iovs

name…

event

iovs

…

6

payload

iovs

name…

iovs

…

Shape the heap
• Many ‘hole’s in the heap
freed

Obj-A

Obj-B

freed

freed

Obj-C

…

Obj-B

Event-2

Event-3

Obj-C

…

freed

freed

freed

freed

…

• Fill with events• Full list
Event-1

Obj-A• New empty list
freed

freed

Merge or not• Freed buffer holes• Trigger notifications with different actions• Not merge• Freed-iovec buffers are not enough• Trigger notifications with a same action• MergeBypassing PXN
Kernel space
ptmx_fops

check_flags

0x40002000
0x40002028

0x40002000

0x40002048

kernel_sock_ioctl_end
User spaceAndroid 7 devices• Exploitation steps• Step 0: Prepare resources and fill the buffer holes• Step 1: Spawn reading threads and shape the heap with iovec objects• Step 2: Spawn race threads• Step 3: Win the race
• fcntl(ptmx_fd, F_SETFL, 0x40002000) == 0x40002000• Step 4: Overwrite uid, disable SELinux and spawn a ROOT shellAndroid 8 devices• Kernel Address Space Layout Randomization• kernel 4.4 (Pixel 2)• Privileged Access Never• ARMv8.0 - Emulated• ARMv8.1 - Hardware featureBypassing KASLR• Use objects instead of payload data
• Kernel func/data pointer at the offset 16• No overflow• No such object 😢

Object-x

Object-x

name…

Object-x…

Bypassing KASLR• After a few days…
• ‘inode’ field is at the offset 0x10 of event• ‘inode’s are allocated in another heapBypassing KASLR• After a few days…
• ‘inode’ field is at the offset 0x10 of event• ‘inode’s are allocated in another heap
• ‘i_op’ callback – kernel data pointer• Kernel slide:• Stage1: leak the address of a inode• Stage2: read ‘i_op’ of this inode

PAN mitigation
Kernel space
ptmx_fops

check_flags

kernel_sock_ioctl

0x40002000
0x40002028

0x40002000

0x40002048

kernel_sock_ioctl_end
User space

PXN and PAN• Construct another ROP/JOP chain• X0 is fully controllable• Writing additional payload for chain increases the crash rate

• CVE-2017-13164 (Discovered by me in 2016, fixed in Dec 2017)• Born with Binder• Leak a kernel address filled with any payload reliably(< 4K)• Goal• Only a vulnerability• No ROP/JOP chain• Bypassing PXN and PAN

Kernel Space Mirroring Attack

Linux Page Table layout
Offset within
Process PGD

Offset within
PMD Page Frame

Offset within
PTE Page Frame

pte_offset()
pmd_offset()

pgd_offset()

mm_struct->pgd

pgd_t

pmd_t

Offset within
Data Frame

pte_t

PageFrame

ARMv8-64 address translation• For Android• 4KB granule• 39-bit (512GB)• Three levels• TTBRx• TTBR0 - user address• Up to 0x0000_007F_FFFF_FFFF• TTBR1 - kernel address• Start from 0xFFFF_FF80_0000_0000

Descriptor formats• ARMv8-64 level 0, level 1, and level 2 descriptor formats

Descriptor formats• ARMv8-64 level 3 descriptor format

General view of address translation

No level 0 table for Android

Attribute fields for RWX• UXN or XN (Exception Level 0 & 1)
• Not executable in same translation regime• PXN• Not executable at EL1• AP[2:1]• Data Access Permissions

Data access permission• ‘00’• Kernel data region• ‘10’• Kernel text region• ‘01’ and ‘11’
• Seem useless because of PAN• ‘01’• A way to read/write the kernel virtual address• Easy way to bypass PXN and PAN!

• Never appearedCraft ‘01’ combination• Modify AP[2:1] attributes of a kernel address• Look up each level of page table• Find the address of the associated page table entry• Set ‘01’ combination• Walk the page table• Ability of arbitrary kernel memory reading/overwriting required• Do you really need to walk the page table?

Principle of KSMA
Start of 1GB region

Level 1 table

FFFFFFFFC0000000

0…
00000...
D_TableD_TableD_Table…
0

FFFFFFC240000000
FFFFFFC200000000
FFFFFFC1C0000000
FFFFFFC180000000
FFFFFFC140000000
FFFFFFC080000000
FFFFFFC040000000
FFFFFFC000000000
FFFFFF8000000000

Text and Data

Freed Pages

Freed Pages

Physical memory(3GB)

Principle of KSMA
Start of 1GB region

Level 1 table

FFFFFFFFC0000000

0…
0
D_Block
000...
D_TableD_TableD_Table…
0

FFFFFFC240000000
FFFFFFC200000000
FFFFFFC1C0000000
FFFFFFC180000000
FFFFFFC140000000
FFFFFFC080000000
FFFFFFC040000000
FFFFFFC000000000
FFFFFF8000000000

FFFFFFC230002000R/W from EL0 & EL1, break kernel isolation

AP[2:1] = ‘01’

PA: 30002000

Text and Data

Freed Pages

R/W from EL1

FFFFFFC030002000

Freed Pages

Physical memory(3GB)

KSMA without KASLR• Where to add a special block• swapper_pg_dir is the pgd for the kernel• Kernel mirroring base
• Entry address• swapper_pg_dir + (Kernel_Mirroring_Base / 1G) * 8• Kaddr to Mirroring Kaddr• Mirroring_kaddr = Kernel_Mirroring_Base + (kaddr - PAGE_OFFSET)KSMA with KASLR• Where to add a special block• swapper_pg_dir is the pgd for the kernel• Kernel mirroring base
• Entry address• (swapper_pg_dir + kernel_slide) + (Kernel_Mirroring_Base / 1G) * 8• Kaddr to Mirroring Kaddr• Mirroring_kaddr = Kernel_Mirroring_Base + (kaddr - PAGE_OFFSET)ReVent with KSMA
• Exploitation for Android 8(with KASLR)• Stage 1-2: Leak kernel heap and data pointers, calculate the kernel slide• Stage 3:• Step1: Prepare a special block descriptor• Step2: Calculate the entry address (No ‘0’ bytes)• Step3: Spawn race threads and win the race
• Step4: Disable SELinux• Write ‘0’ to the mirroring addresses of ‘selinux_enable’ and ‘selinux_enforcing’• Step5: Patch a syscall• Write shellcode to the mirroring address• Step6: Invoke the syscall and spawn a ROOT shell• Bypassing PXN and PAN• Bypassing ‘post-init read-only memory’ constraint

KSMA for ARMv7a• Section descriptor• Block descriptor of ARMv8a• AP[2:1] = ‘01’

Rooting Android 8 Demo

CPRooter Rooting Solution

CVE-2017-0583• Qualcomm CP access driver• Enable to access CPU registers (e.g. TTBRx)• Attract attention in Sep 2016
• Exploitation for Android 7 in Nov 2016
• Ranked as Moderate and fixed in April 2017
• Mode: 0644

• Only root user can write…
• CVE-2016-5195

CVE-2016-5195• Famous name - Dirty Cow• Disclosed in Oct 2016

• For Android• Modify /system files temporarily• Hijack ‘init’ process, fork a root process• For Android 6• A root shell.

• For Android 7• Cannot reload SELinux policy• Cannot execute other binaries• Cannot allocate memory for shellcode

Exploitable or not• Bypass “EXEC_MEM” policy• Write shellcode into /system files• Map into R-X memory• Modify TTBR1 register• Redirect the physical address of PGD for kernel• Access the kernel text/data• Need to construct all level page tables

Exploitable or not• Really need all level page tables?

Virtual address• For ARMv8-64, No• Block descriptorKernel• Only level 1 table
VDSO RW-• Level 1 table• 512 entries(4K) – one page
• Need a known physical page
• VDSO Page

VDSO R-XUser

Physical Pages

Initial idea
• Exploitation steps• Stage 1 (CVE-2016-5195)• Step 1: Prepare kernel block descriptors and shellcode for stage 1 & 2• Step 2: Write shellcode for stage 2 into ’ping6’ binary• Step 3: Write descriptors and shellcode for stage 1 into VDSO page
• Step 4: Spawn a root process by hijacked init process• Step 5: Map and execute the shellcode for stage 2• Stage 2 (CVE-2017-0583)• Step 1: Read the value of TTBR1• Step 2: Write the physical address of VDSO into TTBR1• Step 3: Disable SELinux with KSMA
• Step 4: Write the backup value into TTBR1 and spawn a ROOT shellImprove the success rate
• Kernel crash rate is very high• Not continuous physical pages allocated by ’vmalloc’• Block descriptors are not enough for those addresses• TTBR1 cannot be modified• Turn to TTBR0• Main idea
• Map the physical page of PGD for kernel into user process• Add a crafted block descriptor• No crash after testing 😊
• 100% success rate

Improve the success rate
Virtual address
Physical Pages
Kernel space

VDSO RW-VDSO R-X

TTBR0User space

TTBR1Improve the success rateVirtual address
Physical Pages
Kernel space

VDSO RW-VDSO R-X

TTBR0

Kernel PGD
User space

TTBR1

Exploitation steps• Stage 1 is the same
• Stage 2• Step 1: Read the value of TTBR0 & TTBR1• Step 2: Write two block descriptors into VDSO
• for shellcode(for stage 2) and kernel PGD• Step 3: Write the physical address of VDSO into TTBR0• Step 4: Add a crafted block descriptor to kernel PGD• Step 5: Write the backup value into TTBR0• Step 6: Disable SELinux and patch a syscall with KSMA
• Step 7: Invoke the syscall and spawn a ROOT shell

CPRooter demoBlack Hat Sound Bytes• A new reliable root exploitation technique KSMA is introduced, which
can break Android kernel isolation and bypass both PXN and PAN
mitigations of Android 8.
• Two rooting solutions are detailed. The ideas of exploitations are freshand awesome.
• Nowadays, rooting large numbers of newest Android devices with asingle vulnerability is becoming more and more difficult andchallenging, but it is still possible.

References• Protecting Android with more Linux kernel defenses• Seccomp filter in Android O• Hardening the Kernel in Android Oreo
• CVE-2017-7533• http://seclists.org/oss-sec/2017/q3/240• https://www.kernel.org/doc/gorman/html/understand/understand006.html• ARM® Architecture Reference Manual(ARMv8, for ARMv8-A architecture profile)• ret2dir: Rethinking Kernel Isola6on (USENIX 14’)
• ARM® Architecture Reference Manual(ARMv7-A and ARMv7-R edition)• https://source.codeaurora.org/quic/la/kernel/msm-3.18/commit/?id=452d2ad331d20b19e8a0768c4b6e7fe1b65abe8f

Thank you
WANG, YONG a.k.a. ThomasKing(@ThomasKing2014)
ThomasKingNew@gmail.com