In this talk I will describe how I used an exploit chain to defeat the Samsung KNOX 2.6 with zero privilege, including KASLR bypassing, DFI bypassing, SELinux fully bypassing and privilege escalation.
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) • Credit to discoveries and exploits • CVE-2015-1805 (fs/pipe.c) • First working exploit • CVE-2015-4421,4422 • Kernel LPE and TrustZone code execution for Huawei Mate 7 • Exploiting Wireless Extension for all common Wi-Fi chipsets (BHEU 16’) • And more To Be Announced in the future
• The Random size is passed to kernel by loader • X1,X2 are set upon kernel start up • X1: phycal offset X2: vitual text offset • Store to __boot_kernel_offset • __boot_kernel_offset[0] : physical address of kernel • __boot_kernel_offset[1] : the actual load address • __boot_kernel_offset[2] : TEXT_OFFSET 0x8000
kernel relocating • Similar to a aarch64 linker in user space • Relocation section ‘.rela’ at offset 0x1446600 contains 233903 entries: Offset Info Type Sym. Value Sym. Name + Addend ffffffc000081698 000000000403 R_AARCH64_RELATIV -3ffff7e968 ffffffc0000816a0 000000000403 R_AARCH64_RELATIV -3ffe5d1e20 ffffffc000081798 000000000403 R_AARCH64_RELATIV -3ffff7e868 ... ffffffc00008e000 000000000403 R_AARCH64_RELATIV -3ffff546b8 # Begin of sys_call_table ... ffffffc000f6f800 000600000101 R_AARCH64_ABS64 ffffffc000080000 _text + 0 # Begin of kallsyms_addresses ... ffffffc0013b1468 000000000403 R_AARCH64_RELATIV -3ffec1afdd
Use-after-free due to race condition in perf subsystem • Moving group in sys_perf_event_open() is not locked by mutex correctly • Spray struct perf_event_context{} • Control code flow by refill ctx->pmu->pmu_disable(X0) • Another long story J
Depends on device model, for S7 edge (SM-G9350), it’s TrustZone • CONFIG_TIMA_RKP , CONFIG_RKP_KDP • Targeted features via samsungknox.com: • “completely prevents running unauthorized privileged code” • “prevents kernel data from being directly accessed by user processes” • “monitors some critical kernel data structures to verify that they are not exploited by attacks”
• “config KERNEL_TEXT_RDONLY” • Data section not executable • Privileged eXecute Never (PXN) • Kill ret2user and other ancient tricks • New in KNOX 2.8? • Control flow protection
• allocations, de-allocations of page table • manipulations of page entries • Neither kernel or user space can change attributes of protected pages unauthorizedly • Related functions • pdg_alloc/free() • set_pte/pmd/pud()
for hypervisor or TrustZone • Protected Object type (name of its kmem_cache): • cred_jar_ro : credential of processes • tsec_jar: security context • vfsmnt_cache: struct vfsmount • Allocation, deallocation and overwriting routines will • call rpk_call() to operate read-only objects • Prevent kernel/user mode manipulating credentials, security context and mount namespace
been applied on S6 • Could be bypassed by calling rkp_override_creds() • able to override current process’s credentials via rkp_call() in secure world • Not working on S7 • S7 add more checking in secure world
Allocate new cred from RO kmem_cache • Ask RKP to update current cred and security context • On S6, attacker can call this function to bypass previous kernel object protection
rkp_call(RPK_CMD(0x41)) -> rkp_assign_creds() • rkp_assign_creds() • Real implementation of override_cred() in secure world • Additional verifying in KNOX 2.6 • e • Part of Data flow integrity • UID checking
up • If not, allow the override • If true, the Android initialize has been finished, start UID checking • Unprivileged process(uid>1000) cannot override the credential with high privilege(uid 0~1000) • But still can change its kernel capabilities (very important!)
in both Linux kernel and Secure world • security_integrity_current() (kernel) • Integrity_checking() • Additional members in struct cred{} • use_cnt: pointer to refcount of cred • bp_task: pointer to this cred’s owner • bp_pgd: pointer to process’s PGD • type: not used in DFI
in both Linux kernel and Secure world • security_integrity_current() (kernel) • Integrity_checking() • Additional members in struct task_security_struct{} • bp_cred: pointer to this context’s owner cred
Verify process’s credential in real-time • To check if • current struct cred{} and struct task_security_struct{} are allocated in RO page • cred->bp_task is current process’s • task_security->bp_cred is current cred • current mount namespace is malformed
kernel memory overwriting, we cannot: • Manipulate credentials and security context in kernel mode • Point current credential to init_cred • Call rkp_override_creds() to ask secure world to help us override credential with uid 0~1000 • But we still can: • Call kernel function from user mode • Hijacking ptmx_fops->check_flags(int flag) • The number of parameters is limited • Only low 32bit of X0 is controllable • Override credential with full kernel capabilities (cred->cap_**) • Overwrite unprotected data in kernel
create a privileged process for me • Creating a root process • I can’t call call_usermodehelper(path,argv,envp,wait) via ptmx_fops->check_flags(flag) • Call orderly_poweroff() instead
cred to gain full kernel capabilities • But don’t change uid • Overwrite poweroff_cmd with “/data/data/***/ss7kiler” • Call orderly_poweroff() via ptmx_fops->check_flags() • Modify ss7killer’s thread_info->address limit • ss7killer: call rpk_override_creds() to change its sid from u:r:kernel:s0 to u:r:init:s0
disable SELinux by overwrite selinux_enforcing • Statically enforcing all the time • init process cannot reload SELinux policy after system initialized • Permissive domain is not allowed
For policy developing purpose • All domains are non-permissive since Lollipop • Domains still can be switched to permissive mode by policy reloading (/sys/fs/selinux/load)
not initialized yet • Depends on global variable ss_initialized (writable) selinux hooking routines avc_has_perm security_compute_av if !ss_initialized ALLOW check • All labels will reset to none except kernel domain • Now able to load customized policy and reinitialize SELinux
the database with libsepol API • Load policy DB to the user memory • Add rules into database • Allow untrusted_app, init, toolbox domain to do everything • Ask kernel to reload the database • Set ss_initialized to 1