Defeating Samsung KNOX With Zero Privilege: Di Shen (@returnsme)
Defeating Samsung KNOX With Zero Privilege: Di Shen (@returnsme)
Defeating Samsung KNOX With Zero Privilege: Di Shen (@returnsme)
Di Shen (@returnsme)
KEEN LAB TENCENT (@KEEN_LAB)
OVERVIEW OF KNOX 2.6 3
BYPASSING TECHNIQUES 10
REQUIREMENTS 10
VULNERABILITIES I USED IN THIS CHAIN 10
EXPLOIT CHAIN 11
KASLR BYPASSING 11
DFI BYPASSING 12
SELINUX BYPASSING 13
GAIN ROOT 14
2
Defeating Samsung KNOX with zero
privilege
Di Shen (@returnsme)
Keen Lab Tencent (@keen_lab)
In this section we will have a look at the kernel defence implemented by Samsung KNOX 2.6,
all analyses are based on Galaxy S7 edge, the Qualcomm-based devices, Hong Kong
version.(SM-G9350)
3
Then __relocate_kernel() handles kernel relocating,it’s very similar to a aarch64 linker in user
space. There is a ‘.rela’ section, contains entries of relative addresses.
3. “monitors some critical kernel data structures to verify that they are not exploited by
attacks”
rpk_call() is the syscall entry of RKP. Many critical kernel functions call this function to enter
the secure world, including SLAB allocation and deallocation routines, page table
operations,and copy/override/commit credential routines.
4
Kernel page and page table protection
RKP provides read-only kernel pages for sensitive kernel data and objects, only secure world
can allocate,de-allocate and manipulate these kernel pages. So these pages’ table entries should
be protected from page attribute manipulation as well. When kernel need to access protected
PGD/PTE/PMD/PUD, related routines will call rpk_call() to enter the secure world.
5
Kernel object protection
The kernel objects in kernel heap also can be protected by RKP. So far following objects (and
their kmem_cache) are protected:
These objects are all read-only in kernel/user mode. Allocation, de-allocation and overwriting
must be done in secure world. For example, in original Linux kernel, kernel can call
override_creds() to update a process’s credential. But in Samsung’s repository, this function is
replaced by rkp_override_creds() , it will allocate credential and security context in read-only
kmem_cache then call rkp_call(cmid=0x46) to ask secure world to update process’s credential.
6
Credential verifying in secure world
On Galaxy S6, attacker can simply call rkp_override_creds() to bypass the kernel object
protection and escalate privilege, but this trick isn’t working for S7 any more. RKP add another
checking to verify if the submitted new credential is a legal one.
Uid_checking()
Before adbd and zygote start up, uid_checking will always return ALLOW; after that
unprivileged process(uid>1000) cannot override the credential with high privilege (uid 0~1000)
any more. That is why the old tricks on S6 was not working any more. However, in fact, on S7
you call still use this trick to modify the kernel capabilities of your current credential, even
changing uid is not permitted.
Integrity_checking()
This checking will check if current credential belongs to current process, and check if current
security context belongs to current credential. This is very similar to function
security_integrity_current() in Linux kernel. We’ll analyze this function in next section “Data
Flow Integrity”.
7
Actually it’s not working because of Data Flow Integrity. DFI defines additional members in
struct cred{}
8
of X0 is controllable; 2) Override credential with full kernel capabilities (cred->cap_**); 3)
overwrite other unprotected data in kernel.
SELinux enhancement
Removed selinux_enforcing
On other Android devices, SELinux can be simply disabled by overwriting “selinux_enforcing”
to 0 in Linux kernel. Samsung removed this global variable in kernel by disabling
CONFIG_SECURITY_SELINUX_DEVELOP long time ago.
After that Google remove the permissive domain on Lollipop’s SELinux policy, but permissive
domain is still allowed by kernel’s SELinux access vector checking routing. If you can reload a
customized SELinux policy with permissive domain declared, it’s still a good way to bypass
SELinux. Permissive domain’s access vector database will be marked as
AVD_FLAGS_PERMISSIVE, as you can see in avc_denied(), with this flags all denied
operation can be allowed.
9
Samsung modified the function avc_denied(), this function always returns –EACCESS without
any exception on S7.
Bypassing techniques
Requirements
To build an exploit chain to root Galaxy S7, at least you need two vulnerabilities, one for leak
kernel information, another for arbitrary kernel memory overwriting. Combined with following
bypass techniques, a fully working exploit chain will be explained in this section.
Another one is CVE-2016-6787 found by myself in April 2016, an use-after-free due to race
condition in perf subsystem. Note that the patch for “kernel.perf_event_paranoid” was not
applied on Android at that time, so that this bug could be triggered by any local application.
And also you can use any other exploitable kernel memory corruption bugs instead of this one.
The root cause of this vulnerability is that moving group in sys_perf_event_open() is not locked
by mutex correctly. By spraying kernel memory you call refill struct perf_event_context{} and
control code flow by triggering ctx->pmu->pmu_disable(X0).To make this exploit 100%
10
reliable is another long story. A full description of exploiting CVE-2016-6787 may disclose in
the future.
Exploit chain
Rooting a standard Android device normally requires 4 steps.
Overwrite uid,
Arbitrary kernel memory Overwrite Overwrite
security id,and
overwriting ptmx_fops address_limit
selinux_enforcing
Arbitrary Bypass
Overwrite Overwrite Gain root
Bypass KASLR kernel memory Bypass DFI SELinux for
ptmx_fops address_limit privilege
overwriting Samsung
KASLR bypassing
On S7 there are some debugging files in /proc fs, the following are TIMA logs.
11
DFI bypassing
The main idea is asking kernel to create a privileged process for me, so that I’ll not break any
checking rules defined by RKP and DFI. However I cannot call call_userermodehelper() via
ptmx_fops->check_flags(int), as I’ve explained above, this function have 4 parameters while I
can only pass one from user mode. So I choose to call orderly_poweroff() instead.
orderly_poweroff() will create a worker thread to create a new user mode process
“/sbin/poweroff”. The path of this executable file is poweroff_cmd which can be manipulated.
12
Till now we ask kernel thread to create a root process “ss7killer” with u:r:init:s0 security
domain. However, this process is still limited by SELinux, to gain full access we need to bypass
SELinux.
SELinux bypassing
We need to cheat kernel that SELinux is not initialized yet, this status depends on global
variable ss_initialized, which is not protected by RKP. If ss_initialized is set to 0, all security
labels will be reset to none except kernel domain, all operations can be allowed by SELinux
hooking routines, loading customized policy and reinitializing SELinux can be possible.
13
After setting ss_initialized to 0, we need to load SELinux policy in user space, modify it with
libsepol API. The policy database locates at /sys/fs/selinux/policy. Then insert allow rules into
the database, allow domains including “untrusted_app”, init, toolbox to do everything. Finally,
we should recover ss_initialized ASAP, otherwise other process with none security label may
create none label files and corrupt the file system.
Gain root
Finally we got a full root access on Samsung Galaxy S7 with KNOX 2.6.
14