Defeating Samsung KNOX With Zero Privilege: Di Shen (@returnsme)

Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Defeating Samsung KNOX

with zero privilege

Di Shen (@returnsme)
KEEN LAB TENCENT (@KEEN_LAB)
OVERVIEW OF KNOX 2.6 3

KASLR (SAMSUNG’S IMPLEMENTATION) 3


REAL-TIME KERNEL PROTECTION (RKP) 4
KERNEL CODE PROTECTION 4
KERNEL PAGE AND PAGE TABLE PROTECTION 5
KERNEL DATA PROTECTION 5
KERNEL OBJECT PROTECTION 6
CREDENTIAL VERIFYING IN SECURE WORLD 7
DATA FLOW INTEGRITY (DFI) 7
SUMMARY OF RKP AND DFI 8
SELINUX ENHANCEMENT 9
REMOVED SELINUX_ENFORCING 9
DISABILITY OF POLICY RELOADING 9
REMOVED SUPPORT OF PERMISSIVE DOMAIN 9

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)

Overview of KNOX 2.6


Bypassing KNOX is necessary if you are trying to apply a rooting exploit on Samsung devices.
In April 2016 I found CVE-2016-6787 affected large numbers of Android devices shipped with
3.10 & 3.18 Linux kernel, and successfully make out the rooting exploit. However, the original
exploit wasn’t working on Samsung Galaxy S7 edge. KNOX introduced many mitigations in
Android kernel to prevent from local privilege escalation, including KASLR, DFI, and
SELinux enhancement.

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)

KASLR (Samsung’s implementation)


Samsung implemented its own KASLR for arm64 Linux kernel earlier than UPSTREAM. By
enabling CONFIG_RELOCATABLE_KERNEL, kernel will be complied as a PIE executable.
Bootloader will pass two parameters to the start entry of Linux kernel, one is the physical
address of kernel, another is the actual load address of kernel. Kernel may save the two address
to __boot_kernel_offset[3], calculate the randomized offset of kernel.

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.

Real-time kernel protection (RKP)


RKP is implemented in both Linux kernel and secure world. The secure world can be TrustZone
or hypervisor, it depends on devices model, for S7 the secure world is TrustZone. According to
samsungknox.com, RKP provides following security features:

1. “completely prevents running unauthorized privileged code”

2. “prevents kernel data from being directly accessed by user processes”

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.

Kernel code protection


This is not an exclusive feature for KNOX 2.6. Most 64 bits Android devices had enabled
“KERNEL_TEXT_RDONLY” while compiling, so that the “.text” section is not writable.
“.data” section is not executable as well. Based on ARM’s feature Privileged eXecute Never
(PXN), user code is never executable in kernel mode.

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.

Kernel data protection


Data protection is based on page protection. Some critical global variables are stored in section
“.rkp.prot.page”, pages in this section cannot be overwritten any more after kernel initialization.

So far following variables are protected by RKP:

struct cred init_cred

struct task_secrity_struct init_sec

struct security_oprations security_ops

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:

cred_jar_ro : credential of processes

tsec_jar: security context

vfsmnt_cache: struct vfsmount – mount namespace

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”.

Data Flow Integrity (DFI)


There is another old trick to manipulate current credential. For now, we know that credentials
are read-only, what if we reuse init process’s credential in current context?

7
Actually it’s not working because of Data Flow Integrity. DFI defines additional members in
struct cred{}

bp_task is a pointer to this cred’s owner, bp_pgd is a


pointer to process’s PGD. During committing/overriding a
new credential in secure world, RKP will record the owner
of this credential in bp_task. RKP also record the owner of
struct task_security_struct{} in bp_cred.

security_integirity_current() is a hard-coded hooking in


every SELinux routines, so almost every Linux syscall will
at least call this checking function once to check data’s
integrity.

Summary of RKP and DFI


With RKP enabled, even we achieved arbitrary kernel memory overwriting, we cannot 1)
manipulate credentials and security context in kernel mode; 2) point current credential to
init_cred; 3) call rkp_override_creds() to ask secure world to help us override credential with
uid 0~1000. But we still can: 1) invoke kernel functions from user mode by hijacking
ptmx_fops->check_flags(int flag), note that the number of parameters is limited, only low 32bit

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.

Disability of policy reloading


And also init process cannot reload SELinux policy after system initialized, which means after
Android initialization, attacker cannot simply change its domain to init and reload a customized
policy to bypass SELinux.

Removed support of permissive domain


Furthermore, permissive domain is not allowed neither. The permissive domain was officially
used by Google before Lollipop for policy developing purpose. On KitKat you can see that init
is a permissive domain, which means even SELinux is enforcing, init process still can do
everything it want without a permission deny from kernel.

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.

Vulnerabilities I used in this chain


The information leaking vulnerability will be disclosed in section “KASLR bypassing”.

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

Rooting S7 requires some additional steps to bypass KNOX mitigation

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.

Kernel pointers leaked in global readable file /proc/tima_secure_rkp_log. At 0x13B80 of this


file, it leaked the actual address of init_user_ns. “init_user_ns” is a global variable in kernel’s
data section, so with the leaked information, we can calculate the loading offset of Linux
kernel, and bypass KASLR.

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.

So the bypassing steps are 1) Call rpk_override_creds() via ptmx_fops->check_flags() to


override own cred to gain full kernel capabilities 2) Overwrite poweroff_cmd with
“/data/data/***/ss7kiler” 3) Call orderly_poweroff() via ptmx_fops->check_flags() 4) Modify
ss7killer’s thread_info->address_limit 5) ss7killer call rpk_override_creds() to change its
context from u:r:kernel:s0 to u:r:init:s0

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

You might also like