The Art of Exploiting Unconventional
Use-after-free Bugs in Android Kernel
Di Shen a.k.a. Retme (@returnsme)
Keen Lab of Tencent
whoami
•  Di Shen a.k.a. Retme (@returnsme)
•  Member of Keen Lab
•  Android Kernel vulnerability hunting and exploitation since 2014
•  Aim: to make out universal rooting exploit for Android
•  Trophy:
•  CVE-2016-6787 & CVE-2017-0403 (kernel/events/core.c)
•  CVE-2015-1805 (fs/pipe.c) ’s first working exploit
•  CVE-2015-4421,4422 (Huawei TrustZone)
•  KNOX Bypassing on Samsung Galaxy S7 (BHUSA 17’)
•  Exploiting Wireless Extension for all common Wi-Fi chipsets (BHEU 16’)
•  And more To Be Announced in the future
•  Available on https://github.com/retme7/My-Slides
Agenda
•  Rooting Android: Current situation
•  Overview of exploiting UAF in kernel
•  Conventional approach
•  Afterwards: Gain root
•  The Unconventional UAFs
•  Implementation of perf system
•  Exploiting CVE-2017-0403
•  Exploiting CVE-2016-6787
•  Conclusion
Rooting Android: Current situation
• Universal exploitable vulnerability is rare
•  Available attack surface:
•  Generic Linux syscalls
•  Android universal drivers like Binder, ION, Ashmem
Rooting Android: Current situation
• Enforced SELinux policy
•  Most of device drivers are inaccessible
•  Many syscalls are not reachable from untrusted
Application
•  Sockets ioctl commands are partially restricted
Rooting Android: Current situation
•  Verified Boot through dm-verity kernel feature
•  The gained root privilege is nonpersistent
Rooting Android: Future challenges
•  Privileged Access Never (PAN)
•  KASLR
•  Pointer Authentication
Overview of exploiting UAF in kernel
•  An easily exploitable UAF bug normally has following
features:
•  Has a function pointer in freed object
•  Attacker has plenty of time to refill the freed object.
Conventional approach of UAF exploitation
struct	socket		
(freed)	
ops->ioctl(…)
struct	socket		
(refilled)	
ops->ioctl(…)
JOP	gadgets
•  Free the victim object
•  Refill the object with malformed data by heap
spraying or ret2dir
•  Let the function pointer point to ROP/JOP gadgets in
kernel
•  Ask kernel reference this function pointer to achieve
arbitrary kernel code execution
ioctl(sockfd,…) kernel_sock_ioctl() JOP	gadgets
Afterwards, from code execution to root
Arbitrary	kernel	code	
execuAon
Overwrite	
process's	
addr_limit
Arbitrary	kernel	
memory	
overwriAng
Overwrite	uid,	
security	id,	
selinux_enforcing
However…
•  Not every UAF bug in kernel is so that idealized
•  More unconventional situation to deal with…
•  The victim object may don’t have a function pointer
•  The kernel may crash soon after UAF triggered
•  The attacker may cannot fully controlled the freed object
The unconventional UAFs I found
•  All found in sys_perf_event_open()
•  Perf system is pretty buggy
•  Reachable by application last year
•  But now it’s restricted by a feature called “perf_event_paranoid”
The unconventional UAFs I found
•  CVE-2017-0403
•  Ever affected all devices shipped with 3.10 or earlier Linux kernel
•  More than 14 million users of KingRoot gain root privilege on their
smart phones
•  CVE-2016-6787
•  Ever affected all Qualcomm-based devices. (Only Qucalcomm
enabled hardware perf event…)
•  A part of my exploit chain to bypass Samsung KNOX 2.6
sys_perf_event_open()
•  Will create a perf_event
•  Input: perf_event_attr
•  A description of what kind of performance event
you need
•  Input: group_fd (optional)
•  Specify the group leader of new perf_event
•  Return the fd of perf_event to user space
Key kernel objects in perf system
•  perf_event
•  A performance event which is registered by user
•  perf_event_context
•  The container of all perf events created in one process
•  Each process has two contexts, one for software events, other one for
hardware events
•  Perf group and group leader
•  Multiple events can form a group
•  One event is the leader
perf_sw_context
perf_hw_context
task_struct
event event event
event_list
event	(group_leader)
event	(group_leader)
move_group
•  Happens when user try to create a hardware event in
pure software group
CVE-2016-6787
Remove the group_leader from origin software context and
then install it to hardware context
Remove every event from software context,
and then install it to new hardware context
’move_group‘			leads	to	reducing	
context’s	refcont	by	one
CVE-2016-6787
•  move_group ignored the concurrency issues
•  UAF happens due to race condition
•  Attacker trigger the move_group on same group leader
simultaneously,
•  The ref count of group_leader->ctx may be reduced to zero
•  task_struct->perf_event_ctxp[perf_sw_context] will be freed
accidently
The object is freed
Free perf_event_context (PoC)
Create	a	soJware	
group_leader
Create	a	
hardware	
perf_event
Create	a	
hardware	
perf_event
Main	thread Sub	thread-1 Sub	thread-2
move_group,	
put_ctx()	
move_group,	
put_ctx()	
kfree_rcu(perf_event_context)
ctx->refcount = 1
ctx->refcount = 2
ctx->refcount = 0
Kernel crashed instantly
•  Kernel crashed soon after we
freed the perf_event_context
•  Thread scheduler need to
reference this object
consecutively
•  We don‘t have plenty of time to
refill the object L
Solution: freeze thread after free
•  Keep thread scheduler away from me
•  Switch the status of attacker’s thread from
running to (un)interruptible
•  The thread will be frozen and kernel won’t
crash as soon as perf_event_context freed
How to freeze a thread from user land?
•  Sleep() ? Not working
•  Use futex_wait_queue_me()
switch	to	interrupAble
freezable_schedule()
Create	a	soJware	
group_leader
Create	a	
hardware	
perf_event
Create	a	
hardware	
perf_event
Main	thread Sub	thread-1 Sub	thread-2
move_group,	
put_ctx()	
move_group,	
put_ctx()	
kfree_rcu(perf_event_context)
futext_wait_queue_me()
Phase	
1
Phase	
2
Spraying	the	heap	by	using	‘ret2dir’	trick,	
	fill	a	malformed	perf_event_context{}		
in	every	1024	bytes
Use	futex_wake()	wake	up	
main	thread	
Phase	
4
schedule()
finish_task_switch()
perf_event_context_sched_in()
ctx->pmu->pmu_disable()
Phase	
3
A brief summary of CVE-2016-6787
•  Easy to win the race, and trigger the bug
•  Hard to refill the freed object (no time)
•  Easy to control the code flow (corrupted object has
function pointer)
•  Proposed an approach of thread freezing to gain more
time to refill object
Review: relationship of perf event, group and group leader
•  Group leader has a sibling_list
•  sibling_list is a list of perf events which belongs this
group
perf_sw_context
perf_hw_context
task_struct
event event event
event_list
event	(group_leader)
event	(group_leader)
CVE-2017-0403 (PoC)
•  Create a perf event as ‘A’
•  Create another perf event as ‘B’, specify ‘A’ as its group
leader
•  Free ‘A’,the group leader
•  Free ‘B’, a sibling of group ß---- UAF happens here
Root cause
•  Now group leader ‘A’ is freed
•  Kernel doesn’t empty its sibling list
•  Leads to leaving a dangling pointer in
sibling’s event->group_entry
Root cause
•  Later on the sibling ‘B’ is freed
•  list_del_event()
•  list_del_init(&event->group_entry);
•  overwrite a pointer to the freed group
leader.
•  Slub poinson infomation
•  0xfffffc00fc2b1a0 is overwritten
to (group_leader+ 0x20)
The unconventional scenario
•  The only thing I can do is overwritting the freed object as
following
*(size_t*)(freed_object + 0x20) = (freed_object + 0x20)
Pipe subsystem in Linux
•  readv() & writev(): read/write multiple buffers through pipe
•  Use an array of struct iovec{iov_base,iov_len} to describe
user buffers
•  When no contents available from the write end, readv() may
block in kernel
•  Then an array of struct iovec{} may stay in kernel’s heap
Compromise pipe system
•  Call readv()
•  rw_copy_check_uvector() confirm every iov_base must points to
userland space.
•  An array of struct iovec{} now is in heap. Nothing comes from
the write end of pipe, so readv() block.
•  If you can somehow overwrite the iovec{}, modify the iov_base
to a kernel address. Emmm…
iov_base iov_len iov_base iov_len iov_base iov_len
…..
kernel_addr iov_len kernel_addr iov_len kernel_addr iov_len
Compromise pipe system
•  Now write something to
another end of pipe
•  pipe_iov_copy_to_user()
won’t check the iov_base
again.
•  Buffers you wrote to pipe
will be copied to the
kernel address
········
Trigger UAF, write two 8-bytes value“A+0x20”to address = A+0x20
↓ the 1st freed object, address is A
Solution: convert UAF to arbitrary R/W
↓the 2nd freed object, address is B = A + 0x400
Use iovec to spray the heap
Freed Data Freed Data Freed Freed Data Freed Data ·········
base len base len base base len base len ···················
base len A	+	0x20 A+0x20 base
··········
base len base len
Write a buffer to pipe ,the buffer will be copied to (A + 0x20)
·········
base len KADDR 8 KADDR ·········· KADDR 8 KADDR 8 ·········
Write a buffer to pipe again,it will be copied to KADDR
KADDR can be any address value, we achieved arbitrary kernel memory overwriting
1
2
3
4
5
A brief summary of CVE-2017-0403
•  Attacker lost the file descriptor of freed object
•  Cannot achieve code execution via refilling object’s
function pointer
•  Only be able to write the address value of freed object
twice to freed object
•  Proposed a new approach: compromising pipe system
Conclusion
•  Most UAF bugs looks not exploitable, but there may be
another way
•  No idea? Put it down for a while, but do not let it go…
•  Be familiar with kernel’s source code, kernel’s own
feature may help your exploitation (e.g. pipe for
CVE-2017-0403)
Di shen pacsec_final

Di shen pacsec_final

  • 1.
    The Art ofExploiting Unconventional Use-after-free Bugs in Android Kernel Di Shen a.k.a. Retme (@returnsme) Keen Lab of Tencent
  • 2.
    whoami •  Di Shena.k.a. Retme (@returnsme) •  Member of Keen Lab •  Android Kernel vulnerability hunting and exploitation since 2014 •  Aim: to make out universal rooting exploit for Android •  Trophy: •  CVE-2016-6787 & CVE-2017-0403 (kernel/events/core.c) •  CVE-2015-1805 (fs/pipe.c) ’s first working exploit •  CVE-2015-4421,4422 (Huawei TrustZone) •  KNOX Bypassing on Samsung Galaxy S7 (BHUSA 17’) •  Exploiting Wireless Extension for all common Wi-Fi chipsets (BHEU 16’) •  And more To Be Announced in the future •  Available on https://github.com/retme7/My-Slides
  • 3.
    Agenda •  Rooting Android:Current situation •  Overview of exploiting UAF in kernel •  Conventional approach •  Afterwards: Gain root •  The Unconventional UAFs •  Implementation of perf system •  Exploiting CVE-2017-0403 •  Exploiting CVE-2016-6787 •  Conclusion
  • 4.
    Rooting Android: Currentsituation • Universal exploitable vulnerability is rare •  Available attack surface: •  Generic Linux syscalls •  Android universal drivers like Binder, ION, Ashmem
  • 5.
    Rooting Android: Currentsituation • Enforced SELinux policy •  Most of device drivers are inaccessible •  Many syscalls are not reachable from untrusted Application •  Sockets ioctl commands are partially restricted
  • 6.
    Rooting Android: Currentsituation •  Verified Boot through dm-verity kernel feature •  The gained root privilege is nonpersistent
  • 7.
    Rooting Android: Futurechallenges •  Privileged Access Never (PAN) •  KASLR •  Pointer Authentication
  • 8.
    Overview of exploitingUAF in kernel •  An easily exploitable UAF bug normally has following features: •  Has a function pointer in freed object •  Attacker has plenty of time to refill the freed object.
  • 9.
    Conventional approach ofUAF exploitation struct socket (freed) ops->ioctl(…) struct socket (refilled) ops->ioctl(…) JOP gadgets •  Free the victim object •  Refill the object with malformed data by heap spraying or ret2dir •  Let the function pointer point to ROP/JOP gadgets in kernel •  Ask kernel reference this function pointer to achieve arbitrary kernel code execution ioctl(sockfd,…) kernel_sock_ioctl() JOP gadgets
  • 10.
    Afterwards, from codeexecution to root Arbitrary kernel code execuAon Overwrite process's addr_limit Arbitrary kernel memory overwriAng Overwrite uid, security id, selinux_enforcing
  • 11.
    However… •  Not everyUAF bug in kernel is so that idealized •  More unconventional situation to deal with… •  The victim object may don’t have a function pointer •  The kernel may crash soon after UAF triggered •  The attacker may cannot fully controlled the freed object
  • 12.
    The unconventional UAFsI found •  All found in sys_perf_event_open() •  Perf system is pretty buggy •  Reachable by application last year •  But now it’s restricted by a feature called “perf_event_paranoid”
  • 13.
    The unconventional UAFsI found •  CVE-2017-0403 •  Ever affected all devices shipped with 3.10 or earlier Linux kernel •  More than 14 million users of KingRoot gain root privilege on their smart phones •  CVE-2016-6787 •  Ever affected all Qualcomm-based devices. (Only Qucalcomm enabled hardware perf event…) •  A part of my exploit chain to bypass Samsung KNOX 2.6
  • 14.
    sys_perf_event_open() •  Will createa perf_event •  Input: perf_event_attr •  A description of what kind of performance event you need •  Input: group_fd (optional) •  Specify the group leader of new perf_event •  Return the fd of perf_event to user space
  • 15.
    Key kernel objectsin perf system •  perf_event •  A performance event which is registered by user •  perf_event_context •  The container of all perf events created in one process •  Each process has two contexts, one for software events, other one for hardware events •  Perf group and group leader •  Multiple events can form a group •  One event is the leader perf_sw_context perf_hw_context task_struct event event event event_list event (group_leader) event (group_leader)
  • 16.
    move_group •  Happens whenuser try to create a hardware event in pure software group
  • 17.
    CVE-2016-6787 Remove the group_leaderfrom origin software context and then install it to hardware context Remove every event from software context, and then install it to new hardware context ’move_group‘ leads to reducing context’s refcont by one
  • 18.
    CVE-2016-6787 •  move_group ignoredthe concurrency issues •  UAF happens due to race condition •  Attacker trigger the move_group on same group leader simultaneously, •  The ref count of group_leader->ctx may be reduced to zero •  task_struct->perf_event_ctxp[perf_sw_context] will be freed accidently The object is freed
  • 19.
    Free perf_event_context (PoC) Create a soJware group_leader Create a hardware perf_event Create a hardware perf_event Main threadSub thread-1 Sub thread-2 move_group, put_ctx() move_group, put_ctx() kfree_rcu(perf_event_context) ctx->refcount = 1 ctx->refcount = 2 ctx->refcount = 0
  • 20.
    Kernel crashed instantly • Kernel crashed soon after we freed the perf_event_context •  Thread scheduler need to reference this object consecutively •  We don‘t have plenty of time to refill the object L
  • 21.
    Solution: freeze threadafter free •  Keep thread scheduler away from me •  Switch the status of attacker’s thread from running to (un)interruptible •  The thread will be frozen and kernel won’t crash as soon as perf_event_context freed
  • 22.
    How to freezea thread from user land? •  Sleep() ? Not working •  Use futex_wait_queue_me() switch to interrupAble freezable_schedule()
  • 23.
  • 24.
    A brief summaryof CVE-2016-6787 •  Easy to win the race, and trigger the bug •  Hard to refill the freed object (no time) •  Easy to control the code flow (corrupted object has function pointer) •  Proposed an approach of thread freezing to gain more time to refill object
  • 25.
    Review: relationship ofperf event, group and group leader •  Group leader has a sibling_list •  sibling_list is a list of perf events which belongs this group perf_sw_context perf_hw_context task_struct event event event event_list event (group_leader) event (group_leader)
  • 26.
    CVE-2017-0403 (PoC) •  Createa perf event as ‘A’ •  Create another perf event as ‘B’, specify ‘A’ as its group leader •  Free ‘A’,the group leader •  Free ‘B’, a sibling of group ß---- UAF happens here
  • 27.
    Root cause •  Nowgroup leader ‘A’ is freed •  Kernel doesn’t empty its sibling list •  Leads to leaving a dangling pointer in sibling’s event->group_entry
  • 28.
    Root cause •  Lateron the sibling ‘B’ is freed •  list_del_event() •  list_del_init(&event->group_entry); •  overwrite a pointer to the freed group leader.
  • 29.
    •  Slub poinsoninfomation •  0xfffffc00fc2b1a0 is overwritten to (group_leader+ 0x20)
  • 30.
    The unconventional scenario • The only thing I can do is overwritting the freed object as following *(size_t*)(freed_object + 0x20) = (freed_object + 0x20)
  • 31.
    Pipe subsystem inLinux •  readv() & writev(): read/write multiple buffers through pipe •  Use an array of struct iovec{iov_base,iov_len} to describe user buffers •  When no contents available from the write end, readv() may block in kernel •  Then an array of struct iovec{} may stay in kernel’s heap
  • 32.
    Compromise pipe system • Call readv() •  rw_copy_check_uvector() confirm every iov_base must points to userland space. •  An array of struct iovec{} now is in heap. Nothing comes from the write end of pipe, so readv() block. •  If you can somehow overwrite the iovec{}, modify the iov_base to a kernel address. Emmm… iov_base iov_len iov_base iov_len iov_base iov_len ….. kernel_addr iov_len kernel_addr iov_len kernel_addr iov_len
  • 33.
    Compromise pipe system • Now write something to another end of pipe •  pipe_iov_copy_to_user() won’t check the iov_base again. •  Buffers you wrote to pipe will be copied to the kernel address
  • 34.
    ········ Trigger UAF, writetwo 8-bytes value“A+0x20”to address = A+0x20 ↓ the 1st freed object, address is A Solution: convert UAF to arbitrary R/W ↓the 2nd freed object, address is B = A + 0x400 Use iovec to spray the heap Freed Data Freed Data Freed Freed Data Freed Data ········· base len base len base base len base len ··················· base len A + 0x20 A+0x20 base ·········· base len base len Write a buffer to pipe ,the buffer will be copied to (A + 0x20) ········· base len KADDR 8 KADDR ·········· KADDR 8 KADDR 8 ········· Write a buffer to pipe again,it will be copied to KADDR KADDR can be any address value, we achieved arbitrary kernel memory overwriting 1 2 3 4 5
  • 35.
    A brief summaryof CVE-2017-0403 •  Attacker lost the file descriptor of freed object •  Cannot achieve code execution via refilling object’s function pointer •  Only be able to write the address value of freed object twice to freed object •  Proposed a new approach: compromising pipe system
  • 36.
    Conclusion •  Most UAFbugs looks not exploitable, but there may be another way •  No idea? Put it down for a while, but do not let it go… •  Be familiar with kernel’s source code, kernel’s own feature may help your exploitation (e.g. pipe for CVE-2017-0403)